OpenCL AES Parallelization
OpenCL AES Parallelization
我正在尝试编写一些为SSL服务器进行AES解密的代码。为了加快速度,我试图将多个数据包组合在一起,以便在GPU上一次解密。
如果我只是循环遍历每个数据包,并将每个内核提交给gpu,然后读取使用内核事件的等待。然后我收集所有读取的事件,并同时等待它们,但它似乎只是一次运行一个块,然后再执行下一个块。这不是我所期望的。我希望,如果我将所有内核排队,那么我希望驱动程序会尝试并行地做尽可能多的工作。
我错过了什么吗?我是否必须指定全局工作大小为所有数据包块的大小,并指定内核局部大小为每个数据包块的大小?
这是我的OpenCL内核代码。
__kernel void decryptCBC( __global const uchar *rkey, const uint rounds,
__global const uchar* prev, __global const uchar *data,
__global uchar *result, const uint blocks ) {
const size_t id = get_global_id( 0 );
if( id > blocks ) return;
const size_t startPos = BlockSize * id;
// Create Block
uchar block[BlockSize];
for( uint i = 0; i < BlockSize; i++) block[i] = data[startPos+i];
// Calculate Result
AddRoundKey( rkey, block, rounds );
for( uint j = 1; j < rounds; ++j ){
const uint round = rounds - j;
InverseShiftRows( block );
InverseSubBytes( block );
AddRoundKey( rkey, block, round );
InverseMixColumns( block );
}
InverseSubBytes( block );
InverseShiftRows( block );
AddRoundKey( rkey, block, 0 );
// Store Result
for( uint i = 0; i < BlockSize; i++ ) {
result[startPos+i] = block[i] ^ prev[startPos+i];
}
}
使用这个内核,我可以在单个数据包中使用125块数据胜过8核CPU。为了加快多个数据包的速度,我尝试将所有数据元素组合在一起。这涉及到将输入数据组合到单个向量中,并且由于每个内核需要知道在键内访问的位置,从而导致两个额外的数组,其中包含轮数和轮的偏移量。这比为每个包单独执行一个内核还要慢。
将您的内核视为执行CBC工作的函数。正如您所发现的,它的链式性质意味着CBC任务本身基本上是序列化的。此外,GPU更倾向于在相同的工作负载下运行16个线程。这基本上是一个多处理器核心中单个任务的大小,你往往有几十个多处理器核心;但总的来说,管理系统只能满足其中的一些任务,而记忆系统几乎无法跟上它们的步伐。此外,循环是内核最糟糕的用途之一,因为gpu没有设计成执行太多的控制流。
所以,看看AES,它在16字节块上操作,但只是按字节操作。这将是您的第一个维度-每个块应该由16个线程处理(可能是opencl术语中的本地工作大小)。确保将块传输到本地内存,在本地内存中,所有线程都可以以非常低的延迟同步运行随机访问。展开AES块操作中的所有内容,使用get_local_id(0)来了解每个线程操作的字节。与barrier(CLK_LOCAL_MEM_FENCE)同步,以防工作组在处理器上运行时可能无法同步。键可能会进入常量内存,因为它可以被缓存。如果只是为了避免从全局内存中重新加载前一个块密文,块链可能是具有循环的适当级别。此外,使用async_work_group_copy()异步存储完整的密文可能会有所帮助。你可以通过使用向量让线程做更多的工作,但这可能没有帮助,因为有像shiftRows这样的步骤。
基本上,如果16个线程中的任何一个线程(可能因架构而异)得到任何不同的控制流,你的GPU就会停滞。如果没有足够的这样的组来填充管道和多处理器,那么您的GPU将处于闲置状态。除非你非常仔细地优化了内存访问,否则它不会接近CPU的速度,即使在那之后,你也需要一次处理几十个数据包,以避免给GPU提供太小的工作组。问题是,尽管GPU可以运行数千个线程,但它的控制结构在任何时候都只能处理几个工作组。
另一件要注意的事情;当您在工作组中使用屏障时,工作组中的每个线程都必须执行相同的屏障调用。这意味着即使您有额外的线程空闲运行(例如,那些在联合工作组中解密较短数据包的线程),即使它们不进行内存访问,它们也必须继续通过循环。
从你的描述中不完全清楚,但我认为有一些概念上的混淆。
不要遍历每个包并启动一个新内核。你不需要告诉OpenCL去启动一堆内核。相反,上传尽可能多的数据包到GPU,然后只运行内核一次。当你指定工作组大小时,这就是GPU试图同时运行的内核数量。
您需要对您的内核进行编程,以便在您上传的数据中的不同位置查找它们的数据包。例如,如果要将两个数组添加到第三个数组中,内核应该是这样的:
__kernel void vectorAdd(__global const int* a,
__global const int* b,
__global int* c) {
int idx = get_global_id(0);
c[idx] = a[idx] + b[idx];
}
重要的是每个内核通过使用其全局id知道数组的索引。您将需要做类似的事情。
- AES加密到解密未正确输出
- LINK 尝试使用 OpenSSL evp aes 256 c++ 时出错
- OpenSSL API,使用GCM(伽罗瓦计数器模式)进行AES加密
- 压缩天然气.AES.错误解密(可能加密)的文件
- 用于 AES-gcm 加密的 IV 中是否有不起作用的值?
- Boost.Spirit Alternative Parser parallelization
- OpenSSL AES加密似乎无法正常工作
- AES ECB已知文本攻击
- AES-128 CFB-8解密的前16个字节已损坏
- C++中AES NI加密的正确方法
- aes cbc反向加密
- 在Crypto++中向AES解密传递密钥
- 如何使用 AES 和 sha256 哈希作为密钥加密++进行加密
- 无法为 AES 加密创建 contex.视窗XP
- 短消息 AES 性能C++
- QT:AES-256-CBC 根据 PHP 代码在C++中加密/解密
- 在加密++中使用RSA加密对称AES密钥
- 错误:AES 加密密钥:从"char*"到"无符号字符"的转换无效
- AES加密/解密接收所有明文
- OpenCL AES Parallelization