OpenCL SHA1吞吐量优化

时间:2014-04-10 00:45:22

标签: c++ hash opencl sha

希望有更多OpenCL使用经验的人可以帮助我!我正在做一个项目(帮助我学习更多的加密并尝试GPGPU编程),我试图实现自己的SHA1算法。最终我的问题是关于最大化我的吞吐率 - 目前我看到的是56.1 MH /秒,这与我所看到的OpenSource程序非常相似,例如John the Ripper和OCLHashcat,它们是分别给出1000和1500 MH /秒(哎呀,我的第三个就好了!)。

所以,我正在做的事情;我在OpenCL内核和C ++主机应用程序中编写了一个SHA1实现,用于将数据加载到GPU(使用CL 1.2 C ++包装器)。我在CPU上以线程方式生成候选数据块并使用CL C ++调用enqueueWriteBuffer(使用uchars表示要散列的字节)将此数据加载到全局GPU内存中:

    errorCode = dispatchQueue->enqueueWriteBuffer(
        inputBuffer,
        CL_FALSE,//CL_TRUE,
        0,
        sizeof(cl_uchar) * inputBufferSize,
        passwordBuffer,
        NULL,
        &dispatchDelegate);

我使用enqueueNDRangeKernel按以下方式对数据进行排队(其中global worksize是用户定义的变量,目前我已将此设置为我的GPU,每次运行的最大扁平化全局工作量为1677.7万):

errorCode = dispatchQueue->enqueueNDRangeKernel(
    *kernel,
    NullRange,
    NDRange(globalWorkgroupSize, 1), 
    NullRange, 
    NULL,
    NULL);

意思是,对于每个调度,我使用get_global_offset(0)在一维数组中加载1677.7万个项目并从我的内核索引到这个项目。我的内核签名:

__kernel void sha1Crack(__global uchar* out, __global uchar* in, 
                        __constant int* passLen, __constant int* targetHash, 
                        __global bool* collisionFound)
{
    //Kernel Instance Global GPU Mem IO Mapping:
    __private int id = get_global_id(0);
    __private int inputIndexStart = id * passwordLen;

    //Select Password input key space:
    #pragma unroll
    for (i = 0; i < passwordLen; i++)
    {
        inputMem[i] = in[inputIndexStart + i];
    }

    //SHA1 Code omitted for brevity...
}

所以,鉴于此,我在加载数据的方式上做了一些根本性的错误吗?即1次调用enqueueNDrange,在1D输入向量上执行1670万次内核执行? (我应该使用2D空间并细分为localworkgroup范围吗? - 我试过玩这个,但它看起来并不快)。

或者,也许我的算法本身可能是缓慢的来源(我已经花了很长时间来优化它并使用预处理器指令手动展开所有循环阶段等)?

我已经读过硬件上的内存合并,这是我的问题吗? :S

任何建议都表示赞赏!如果我错过了任何重要的内容,请告诉我,我会更新。

提前致谢! ;)


更新:16,777,216是设备最大报告的工作组大小; 256 ** 3。全局布尔值数组是一个布尔值。在内核入队开始时将其设置为false,然后如果仅发现碰撞,则分支语句将此设置为true - 是否会强制收敛? passwordLen是当前输入值的长度,而目标哈希是要检查的int [4]编码哈希值。

1 个答案:

答案 0 :(得分:0)

您的'最大扁平全球工作量'应乘以passwordLen。它是您可以运行的内核数,而不是输入数组的最大长度。您很可能会向GPU发送比此更多的数据。

其他潜在问题:“在CPU上以线程方式生成候选数据块”,尝试在内核迭代之前执行此操作,以查看延迟是在生成数据块还是在处理内核;你的sha1算法是另一个明显的潜在问题。我不确定你通过“展开”循环对它进行了多少优化,通常更大的优化问题是'if'语句(如果工作组中的单个内核实例测试为true,那么所有的锁步工作组实例必须并行地跟着那个分支。)

DarkZeros是正确的,您应该手动使用本地工作组大小,使其成为全局大小的最高公倍数和可以在卡上一次运行的内核数量。最简单的方法是将全局工作组大小向上舍入到卡容量的下一个倍数,并在内核中使用外部if {}语句,仅运行内核,使global_id小于您想要的实际内核数量运行

戴夫。