并行缩减无法正常工作

Posted

技术标签:

【中文标题】并行缩减无法正常工作【英文标题】:Parallel Reduction does not work correctly 【发布时间】:2021-12-22 22:40:23 【问题描述】:

我在 OpenCL 上编写了以下并行内核缩减。我只想对 BlockSum 数组中的所有值求和。在使用work_group_reduce_add(BlockSum[GetIndex]); 时,它可以正常工作,但使用我从https://www.fz-juelich.de/SharedDocs/Downloads/IAS/JSC/EN/slides/opencl/opencl-05-reduction.pdf?__blob=publicationFile (幻灯片11)读取的优化代码无法正常工作。这里的错误似乎是什么? global_work_size 设置为 16,16 以及 local_work_size(意味着每个工作组总共有 256 个线程)。在 work_group_reduce_add 的情况下,我得到 255,这是正确的,但使用优化的代码,我得到 0

__kernel void Reduction()

        unsigned char GetThreadX = get_local_id(0); //it takes values from 0..15
        unsigned char GetThreadY = get_local_id(1); //it takes values from 0..15
        unsigned char GetGroup   = get_local_size(0); //16
        unsigned short  BlockSum[256];      
        int SumOfAll= 0;            
        
        unsigned short GetIndex = GetThreadX + (GetGroup * GetThreadY); // takes values 0..255, group=16        
        
        BlockSum[GetIndex] = 1;             
        barrier(CLK_LOCAL_MEM_FENCE);       
        
        SumOfAll= work_group_reduce_add(BlockSum[GetIndex]); //works great  
        
        // BUT CODE BELOW DOES NOT SUM CORRECTLY
        /*
        for(unsigned short stride=128; stride>1; stride >>= 1) 
            
            if(GetIndex < stride)
                BlockSum[GetIndex] += BlockSum[GetIndex + stride];          
            barrier(CLK_LOCAL_MEM_FENCE);           
                       
        if(GetIndex==0)             
            SumOfAll = BlockSum[0] + BlockSum[1];       
        barrier(CLK_LOCAL_MEM_FENCE);
        */
        printf("SumOfAll=%d\n",SumOfAll);

【问题讨论】:

unsigned char GetThreadX = get_local_id(0); 为什么是char 而不是int @AndreasHadjigeorgiou Cos get_local_id(0) 从 0..15 获取值 嗯,我不知道你能做到,有趣!!! @AndreasHadjigeorgiou 为什么白白浪费宝贵的 GPU 私有内存? 是的,这是一个好点!寄存器确实很珍贵。 【参考方案1】:

好的,问题已解决。 BlockSum[256]; 没有声明为 __local 而是作为私有内存(没有__local 地址空间限定符),这意味着每个线程(或核心)都有自己的这些数据的副本,但优化的缩减代码正在寻找对于线程之间的共享本地内存数据,汇总值。此外,变量 int SumOfAll; 也应声明为 __local 并进行初始化或 private 在我的情况下之前没有任何初始化。你选择。

所以工作内核现在看起来像这样。

我希望这种类型的错误能帮助像我这样不谨慎的人。

__kernel void Reduction()

        unsigned char GetThreadX = get_local_id(0); //it takes values from 0..15
        unsigned char GetThreadY = get_local_id(1); //it takes values from 0..15
        unsigned char GetGroup   = get_local_size(0); //16

        //*********************************************************
        //below was the offending code and the root of the problem 
        //**********************************************************
        __local unsigned short  BlockSum[256];      
        int SumOfAll;           
        //**********************************************************
        
        unsigned short GetIndex = GetThreadX + (GetGroup * GetThreadY); // takes values 0..255, group=16        
        
        BlockSum[GetIndex] = 1;             
        barrier(CLK_LOCAL_MEM_FENCE);       
        
        //SumOfAll = work_group_reduce_add(BlockSum[GetIndex]); 
        
        // OPTIMIZED CODE BELOW NOW SUM UP CORRECTLY
        
        for(unsigned short stride=128; stride>1; stride >>= 1) 
            
            if(GetIndex < stride)
                BlockSum[GetIndex] += BlockSum[GetIndex + stride];          
            barrier(CLK_LOCAL_MEM_FENCE);           
                       
        if(GetIndex==0)             
            SumOfAll = BlockSum[0] + BlockSum[1];       
        barrier(CLK_LOCAL_MEM_FENCE);
        
        printf("SumOfAll=%d\n",SumOfAll);
        


        
    

【讨论】:

以上是关于并行缩减无法正常工作的主要内容,如果未能解决你的问题,请参考以下文章

并行 OpenMP 缩减与函数定义?

如何防止 Elastic Beanstalk 上长时间运行的作业在不阻止缩减的情况下终止?

HPA 缩减 kubernetes pod

CUDA中的一个简单的缩减程序

在 std::vector 上的 Openmp 和缩减?

Tidb进行缩减扩容tikv节点