【问题标题】:CUDA kernel with atomics producing wrong answer带有原子的 CUDA 内核产生错误的答案
【发布时间】:2012-07-30 03:30:48
【问题描述】:

我正在尝试了解 CUDA 编程模型及其功能。作为练习,我正在尝试将以下带有函数调用的循环结构转换为高效的 CUDA 内核

//function call
bool gmul(int rowsize,int *Ai,int *Bj,int colsize)
{
    for(int i = 0;i < rowsize;i++)
    {
        for(int j = 0;j < colsize;j++)
        {
            if(Ai[i] == Bj[j])
            {
                return true;
            }
        }
    }
    return false;
}

//Some for loop in main function is as follows

for(i = 0;i < q ;i++)
    {
        cbeg = Bjc[i];
        cend = Bjc[i+1];        
        for(j = 0;j < m;j++)
        {
            beg = Aptr[j];
            end = Aptr[j+1];            
            if(gmul(end - beg,Acol + beg,Bir + cbeg,cend - cbeg))
            {   
                temp++;             
            }                       
        }
        Cjc1[i+1] = temp ;              
    } 

我的内核函数调用如下。

    __device__ bool mult(int colsize,int rowsize,int *Aj,int *Bi,int *val)
    {       
        for(int j = 0; j < rowsize;j++)
        {           
           for(int k = 0;k < colsize;k++)
            {   
              if(Aj[j] == Bi[k])
               {    
                return true;
                }                               
            }           
        }
            return false;       
    }


__global__ void kernel(int *Aptr,int *Aj,int *Bptr,int *Bi,int rows,int cols,int *count,int *Cjc)
    {
        int tid = threadIdx.x + blockIdx.x * blockDim.x;
        int i;
        if(tid < cols)
        {
            int beg = Bptr[tid];
            int end = Bptr[tid+1];
            for(i = 0;i < rows;i++)
            {
                int cbeg = Aptr[i];
                int cend = Aptr[i+1];
                if(mult(end - beg,cend - cbeg,Aj+cbeg,Bi+beg,count))
                {
                    //atomicAdd(count,1);
                                    //Changes made are in next line
                              atomicAdd(Cjc+tid+1,1);           
                }
            }
            //atomicAdd(Cjc+tid+1,*count);              
        }               
    }

我想要的是,每当__device__ multtrue 值返回时,我的全局内核函数应该增加该特定线程的计数器,并且一旦for循环(在内核函数中)结束,它应该将值存储到Cjc 数组和计数交给其他线程进行增量操作。但是,我没有得到预期值。我在这个Cjc 数组中得到的只是所有线程完成执行后的最终计数。

我正在使用带有 CC 2.0 的 GTX 480

任何关于我为什么会得到错误答案或对此 CUDA 内核优化的建议/提示将不胜感激。 提前致谢。 ********已解决*** ********

现在,我面临一个问题,即每当我达到 4000 及以上的大小时,我都会将数组中所有元素的值设为0。这是我启动内核的方式。

    int numBlocks,numThreads;

        if(q % 32 == 0)
        {
            numBlocks = q/32;
            numThreads = 32;
        }
        else
        {
            numBlocks = (q+31)/32;
            numThreads = 32;
        }
findkernel<<<numBlocks,numThreads>>>(devAptr,devAcol,devBjc,devBir,m,q,d_Cjc);          

我想知道我是否跨越了块或网格尺寸的任何限制,但对于 CC 2.0,我认为我可以启动足够的块和线程而不会跨越任何限制。我想知道为什么所有答案都以0 的形式出现。

【问题讨论】:

  • 您的第一步应该是在谷歌上搜索“并行前缀总和”或“扫描”。这将为实现这种累积求和带来一些合理的算法基础。已经有许多不错的 CUDA 实现可供您参考,以了解如何正确实现它。
  • @talonmies ..谢谢...我会尝试将各个计数存储在一个数组中,然后我会尝试实现并行前缀求和或扫描...我想知道推力是否有任何实现一样的……
  • thrust::exclusive_scanthrust::inclusive_scan 可能是您正在寻找的。​​span>
  • @talonmies...我进行了更改并按照您的建议使用了thrust::inclusive_scan。一切正常,直到我达到 4000 及以上的大小。我得到的数组的所有值都为0。我已经用我的内核启动参数更新了这个问题。

标签: cuda gpu gpgpu


【解决方案1】:

您编写了在不同步的情况下读取和写入count 的并行线程。线程以不可预知的顺序并发运行,因此线程原子地修改count 并以不可预知的顺序读取count。表达式*count 将根据确切的执行顺序产生不同的结果。

一旦 for 循环(在内核函数中)结束,它应该将值存储到 Cjc 数组中,并将计数交给其他线程进行递增操作。

没有同步,所以没有线程等待另一个线程完成循环。为什么不让所有线程为count 共享相同的存储空间,为什么不给每个线程一个不同的存储空间呢?然后线程不会影响彼此的结果。您可以在此之后运行扫描内核以合并结果。

【讨论】:

  • @Heatsink....谢谢...我会尝试将每个计数的结果放入一个数组中,然后我会尝试应用并行前缀总和或对其进行扫描。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-11
相关资源
最近更新 更多