Parallel processing 在块中查找最大值的OpenCL屏障

Parallel processing 在块中查找最大值的OpenCL屏障,parallel-processing,max,opencl,gpgpu,barrier,Parallel Processing,Max,Opencl,Gpgpu,Barrier,我在Nvidia的开发者网站上找到了一段OpenCL内核示例代码 函数maxOneBlock的目的是找出数组maxValue的最大值,并将其存储到maxValue[0] 我完全理解循环部分,但对unroll部分感到困惑:为什么在完成每个步骤后,unroll部分不需要同步线程 e、 g:当一个线程完成localId和localId+32的比较时,它如何确保其他线程将其结果存储到localId+16 内核代码: void maxOneBlock(__local float maxValue[],

我在Nvidia的开发者网站上找到了一段OpenCL内核示例代码 函数
maxOneBlock
的目的是找出数组
maxValue
的最大值,并将其存储到maxValue[0]

我完全理解循环部分,但对
unroll
部分感到困惑:为什么在完成每个步骤后,unroll部分不需要同步线程

e、 g:当一个线程完成localId和localId+32的比较时,它如何确保其他线程将其结果存储到localId+16

内核代码:

void maxOneBlock(__local float maxValue[],
                 __local int   maxInd[])
{
    uint localId   = get_local_id(0);
    uint localSize = get_local_size(0);
    int idx;
    float m1, m2, m3;

    for (uint s = localSize/2; s > 32; s >>= 1)
    {
        if (localId < s) 
        {
            m1 = maxValue[localId];
            m2 = maxValue[localId+s];
            m3 = (m1 >= m2) ? m1 : m2;
            idx = (m1 >= m2) ? localId : localId + s;
            maxValue[localId] = m3;
            maxInd[localId] = maxInd[idx];
        }
        barrier(CLK_LOCAL_MEM_FENCE);
    }

    // unroll the final warp to reduce loop and sync overheads
    if (localId < 32)
    {
        m1 = maxValue[localId];
        m2 = maxValue[localId+32];
        m3 = (m1 > m2) ? m1 : m2;
        idx = (m1 > m2) ? localId : localId + 32;
        maxValue[localId] = m3;
        maxInd[localId] = maxInd[idx];


        m1 = maxValue[localId];
        m2 = maxValue[localId+16];
        m3 = (m1 > m2) ? m1 : m2;
        idx = (m1 > m2) ? localId : localId + 16;
        maxValue[localId] = m3;
        maxInd[localId] = maxInd[idx];

        m1 = maxValue[localId];
        m2 = maxValue[localId+8];
        m3 = (m1 > m2) ? m1 : m2;
        idx = (m1 > m2) ? localId : localId + 8;
        maxValue[localId] = m3;
        maxInd[localId] = maxInd[idx];

        m1 = maxValue[localId];
        m2 = maxValue[localId+4];
        m3 = (m1 > m2) ? m1 : m2;
        idx = (m1 > m2) ? localId : localId + 4;
        maxValue[localId] = m3;
        maxInd[localId] = maxInd[idx];

        m1 = maxValue[localId];
        m2 = maxValue[localId+2];
        m3 = (m1 > m2) ? m1 : m2;
        idx = (m1 > m2) ? localId : localId + 2;
        maxValue[localId] = m3;
        maxInd[localId] = maxInd[idx];

        m1 = maxValue[localId];
        m2 = maxValue[localId+1];
        m3 = (m1 > m2) ? m1 : m2;
        idx = (m1 > m2) ? localId : localId + 1;
        maxValue[localId] = m3;
        maxInd[localId] = maxInd[idx];
    }
}
void maxOneBlock(uu local float maxValue[],
__本地int maxInd[]
{
uint localId=get\u local\u id(0);
uint localSize=get\u local\u size(0);
int-idx;
浮子m1、m2、m3;
对于(uint s=localSize/2;s>32;s>>=1)
{
if(localId=m2)?m1:m2;
idx=(m1>=m2)?localId:localId+s;
最大值[localId]=m3;
maxInd[localId]=maxInd[idx];
}
屏障(CLK_本地_MEM_围栏);
}
//展开最终扭曲以减少循环和同步开销
如果(局部ID<32)
{
m1=最大值[localId];
m2=最大值[localId+32];
m3=(m1>m2)?m1:m2;
idx=(m1>m2)?localId:localId+32;
最大值[localId]=m3;
maxInd[localId]=maxInd[idx];
m1=最大值[localId];
m2=最大值[localId+16];
m3=(m1>m2)?m1:m2;
idx=(m1>m2)?localId:localId+16;
最大值[localId]=m3;
maxInd[localId]=maxInd[idx];
m1=最大值[localId];
m2=最大值[localId+8];
m3=(m1>m2)?m1:m2;
idx=(m1>m2)?localId:localId+8;
最大值[localId]=m3;
maxInd[localId]=maxInd[idx];
m1=最大值[localId];
m2=最大值[localId+4];
m3=(m1>m2)?m1:m2;
idx=(m1>m2)?localId:localId+4;
最大值[localId]=m3;
maxInd[localId]=maxInd[idx];
m1=最大值[localId];
m2=最大值[localId+2];
m3=(m1>m2)?m1:m2;
idx=(m1>m2)?localId:localId+2;
最大值[localId]=m3;
maxInd[localId]=maxInd[idx];
m1=最大值[localId];
m2=最大值[localId+1];
m3=(m1>m2)?m1:m2;
idx=(m1>m2)?localId:localId+1;
最大值[localId]=m3;
maxInd[localId]=maxInd[idx];
}
}
为什么展开部分在每个步骤完成后不需要同步线程

样品不正确,每个步骤后确实需要屏障


看起来这个示例是用warp同步风格编写的,这是一种利用NVIDIA硬件上warp的底层执行机制的方法,但是,如果底层执行机制发生更改或存在编译器优化,不正确的同步将导致它中断。

即使它是以扭曲同步样式编写的,
unroll
部分也需要在每个步骤后限制线程。i、 e.第一步限位32螺纹,第二步限位16螺纹。。。等等。但事实并非如此,所有32个线程都运行了整个
unroll
代码。是的,但它们的结果没有被使用:它们只是免费做额外的工作。作者没有在每次迭代中禁用半线程,而是选择让它们运行。它使代码更简单,不会影响性能或最终结果。这是一种比较常见的技术。但这并不意味着障碍是可选的,在这方面,样本是错误的。