OpenCL 结果随不相关的任意代码更改而变化

问题描述

这是一个很奇怪的问题。我正在开发基于 GPU 的加密矿工,但我在使用 SHA 哈希函数时遇到了问题。

1 - 初始函数调用 SHA256 例程,然后打印结果。我将这些结果与基于 cpu 的 SHA256 进行比较,以确保得到相同的结果。

2 - 在函数的后面,还会发生其他操作,例如加法、异或和额外的 SHA 轮。

作为矿工内核的一部分,我编写了一个辅助函数,使用 AND 掩码和位移位将 8 个 uint 的数组分解为 32 个无符号字符的数组。

我正在调用全局/本地工作单元为 1 的内核。

所以,这就是事情变得非常奇怪的地方。我比较的部分是第一个 SHA。我得到一个 80 字节的缓冲区,对其进行 SHA 处理,然后打印结果。它在某些条件下匹配。但是,如果我对 SHA 执行后正在执行的代码进行更改,则它不匹配。这是我能够缩小的范围:

1 - 如果我在分解辅助函数中放置 printf 调试,结果匹配。只是删除那个 printf 会导致它不匹配。

2 - 我使用 4 个操作将 uint 分解为 char。我尝试了很多不同的方法来实现相同的结果。但是,如果我删除例程中的 4 个“for”循环中的任何一个,它就会匹配。只需删除在初始代码后执行代码中的 for 循环,即可更改初始 SHA 的结果。

3 - 如果我将 while 循环更改为从不执行,则它匹配。同样,这是在初始 SHA 比较之后。

4 - 如果我删除对辅助函数的所有调用,那么它匹配。简单地在初始 SHA 之后调用函数会导致不匹配。

我已经尝试在任何地方添加内存保护,但是由于它有 1 个全局工作单元和 1 个本地工作单元,我看不出这如何适用。

我很想调试这个,但显然 openCL 无法在 VS 2019 中调试(真的吗?)

任何想法、猜测、见解将不胜感激。

谢谢!

    inline void loadUintHash ( __global unsigned char* dest,__global uint* src) {

      //**********if I remove this it doesn't work
printf ("src1 %08x%08x%08x%08x%08x%08x%08x%08x",src[0],src[1],src[2],src[3],src[4],src[5],src[6],src[7]
);
    
//**********if I take away any one of these 4 for loops,then it works
    for ( int i = 0; i < 8; i++) 
        dest[i*4+3] = (src[i] & 0xFF000000) >> 24;

    for ( int i = 0; i < 8; i++) 
        dest[i*4+2] = (src[i] & 0x00FF0000) >> 16;
        
    for ( int i = 0; i < 8; i++) 
        dest[i*4+1] = (src[i] & 0x0000FF00) >> 8;
               
    for ( int i = 0; i < 8; i++) 
        dest[i*4] = (src[i] & 0x000000FF);
        

      //**********if I remove this it doesn't work
printf ("src2 %08x%08x%08x%08x%08x%08x%08x%08x",src[7]
);


}

#define HASHOP_ADD 0
#define HASHOP_XOR 1
#define HASHOP_SHA_SINGLE 2
#define HASHOP_SHA_LOOP 3
#define HASHOP_MEMGEN 4
#define HASHOP_MEMADD 5
#define HASHOP_MEMXOR 6
#define HASHOP_MEM_SELECT 7
#define HASHOP_END 8

__kernel void dyn_hash (__global uint* byteCode,__global uint* memGenBuffer,int memGenSize,__global uint* hashResult,__global char* foundFlag,__global unsigned char* header,__global unsigned char* shaScratch) {
    
    int computeUnitID = get_global_id(0);

    __global uint* myMemGen = &memGenBuffer[computeUnitID * memGenSize * 8];        //each memGen unit is 256 bits,or 8 bytes
    __global uint* myHashResult = &hashResult[computeUnitID * 8];
    __global char* myFoundFlag = foundFlag + computeUnitID;
    __global unsigned char* myHeader = header + (computeUnitID * 80);
    __global unsigned char* myScratch = shaScratch + (computeUnitID * 32);


    sha256 ( computeUnitID,80,myHeader,myHashResult );

    //**********this is the result I am comparing
    if (computeUnitID == 0) {
       printf ("gpu first sha uint  %08x%08x%08x%08x%08x%08x%08x%08x",myHashResult[0],myHashResult[1],myHashResult[2],myHashResult[3],myHashResult[4],myHashResult[5],myHashResult[6],myHashResult[7]
                );
    }


    uint linePtr = 0;
    uint done = 0;
    uint currentMemSize = 0;
    uint instruction = 0;

//**********if I change this to done == 1,then it works
    while (done == 0) {

        if (byteCode[linePtr] == HASHOP_ADD) {
            linePtr++;
            uint arg1[8];
            for ( int i = 0; i < 8; i++)
                arg1[i] = byteCode[linePtr+i];
            linePtr += 8;
        }


        else if (byteCode[linePtr] == HASHOP_XOR) {
            linePtr++;
            uint arg1[8];
            for ( int i = 0; i < 8; i++)
                arg1[i] = byteCode[linePtr+i];
            linePtr += 8;
        }


        else if (byteCode[linePtr] == HASHOP_SHA_SINGLE) {
            linePtr++;
        }


        else if (byteCode[linePtr] == HASHOP_SHA_LOOP) {
            printf ("HASHOP_SHA_LOOP");
            linePtr++;
            uint loopCount = byteCode[linePtr];

            
            for ( int i = 0; i < loopCount; i++) {

                loadUintHash(myScratch,myHashResult);

                sha256 ( computeUnitID,32,myScratch,myHashResult );

                


                
                            if (computeUnitID == 1)     {
                loadUintHash(myScratch,myHashResult);

... more irrelevant code...


This is how the kernel is being called:

    size_t globalWorkSize = 1;// computeUnits;
    size_t localWorkSize = 1;
    returnVal = clEnqueueNDRangeKernel(command_queue,kernel,1,NULL,&globalWorkSize,&localWorkSize,NULL);

解决方法

问题最终是多方面的。 1 - CPU SHA 中有一个错误,在某些情况下会导致错误的结果。 2 - 有一个非常奇怪的语法错误,似乎以一种奇怪的方式破坏了编译器:

void otherproc(){ ...做东西... }

如果(某事){/ ...其他代码 }

左花括号后面的斜杠以一种奇怪的方式弄乱了“otherproc”,编译器没有抛出错误。一行一行地看代码后,我发现了斜线,把它去掉,一切都开始工作了。

如果有人感兴趣,可以在这里找到 GPU 矿工的工作实现:

https://github.com/dynamofoundation/dyn_miner