OpenCL /尝试了解内核代码

时间:2012-09-07 17:12:05

标签: opencl

我正在研究一个OpenCL代码,该代码模拟了以下教程中的N体问题:

http://www.browndeertechnology.com/docs/BDT_OpenCL_Tutorial_NBody-rev3.html

我的主要问题依赖于内核代码:

   for(int jb=0; jb < nb; jb++) { /* Foreach block ... */

19          pblock[ti] = pos_old[jb*nt+ti]; /* Cache ONE particle position */
20          barrier(CLK_LOCAL_MEM_FENCE); /* Wait for others in the work-group */

21          for(int j=0; j<nt; j++) { /* For ALL cached particle positions ... */
22             float4 p2 = pblock[j]; /* Read a cached particle position */
23             float4 d = p2 - p;
24             float invr = rsqrt(d.x*d.x + d.y*d.y + d.z*d.z + eps);
25             float f = p2.w*invr*invr*invr;
26             a += f*d; /* Accumulate acceleration */
27          }

28          barrier(CLK_LOCAL_MEM_FENCE); /* Wait for others in work-group */
29       }

我不明白在执行过程中究竟发生了什么:内核代码执行n次,其中n是工作项的数量(也是线程数),但在上面的代码部分中,我们使用每个工作组的本地内存(似乎有nb个工作组)

那么,在执行时,直到第一个“障碍”,我是否使用pos_old的全局值在本地填充pblock数组?

总是达到第一个障碍,对于另一个工作组,pblock数组将包含与其他工作组的数组相同的值,因为障碍之前的jb = 0?

似乎这是一种由所有工作组共享这些数组的方法,但这对我来说并不完全清楚。

欢迎任何帮助。

1 个答案:

答案 0 :(得分:0)

请你发布整个内核代码吗?我必须对参数和私有变量做出假设。

组中似乎有多个工作项,ti代表当前工作项。循环执行时,组中的每个项目将仅复制单个元素。通常此副本来自全局数据源。第一道屏障强制工作项等待,直到其他项目复制完毕。这是必要的,因为组中的每个工作项都需要读取从每个其他工作项复制的数据。值不应该相同,因为每个工作项的ti应该不同。 (jb * nt对于第一个循环仍然等于零)


以下是整个内核代码:

__kernel
void 
nbody_sim(
    __global float4* pos ,
    __global float4* vel,
    int numBodies,
    float deltaTime,
    float epsSqr,
    __local float4* localPos,
    __global float4* newPosition,
    __global float4* newVelocity)
{
    unsigned int tid = get_local_id(0);
    unsigned int gid = get_global_id(0);
    unsigned int localSize = get_local_size(0);

    // Number of tiles we need to iterate
    unsigned int numTiles = numBodies / localSize;

    // position of this work-item
    float4 myPos = pos[gid];
    float4 acc = (float4)(0.0f, 0.0f, 0.0f, 0.0f);

    for(int i = 0; i < numTiles; ++i)
    {
        // load one tile into local memory
        int idx = i * localSize + tid;
        localPos[tid] = pos[idx];

        // Synchronize to make sure data is available for processing
        barrier(CLK_LOCAL_MEM_FENCE);

        // calculate acceleration effect due to each body
        // a[i->j] = m[j] * r[i->j] / (r^2 + epsSqr)^(3/2)
        for(int j = 0; j < localSize; ++j)
        {
            // Calculate acceleartion caused by particle j on particle i
            float4 r = localPos[j] - myPos;
            float distSqr = r.x * r.x  +  r.y * r.y  +  r.z * r.z;
            float invDist = 1.0f / sqrt(distSqr + epsSqr);
            float invDistCube = invDist * invDist * invDist;
            float s = localPos[j].w * invDistCube;

            // accumulate effect of all particles
            acc += s * r;
        }

        // Synchronize so that next tile can be loaded
        barrier(CLK_LOCAL_MEM_FENCE);
    }

    float4 oldVel = vel[gid];

    // updated position and velocity
    float4 newPos = myPos + oldVel * deltaTime + acc * 0.5f * deltaTime * deltaTime;
    newPos.w = myPos.w;
    float4 newVel = oldVel + acc * deltaTime;

    // write to global memory
    newPosition[gid] = newPos;
    newVelocity[gid] = newVel;
}

每个工作组都有“numTiles”工作组,其中包含“localSize”工作项。

“gid”是全局索引,“tid”是本地索引。

让我们从循环的第一次迭代开始“for(int i = 0; i&lt; numTiles; ++ i)”with“i = 0”:

如果我举个例子:

numTiles = 4,localSize = 25和numBodies = 100 =工作项数。

然后,在执行时,如果我有gid = 80,那么tid = 5,idx = 5,第一个分配将是:localPos [5] = pos [5]

现在,我接受gid = 5,然后tid = 5和idx = 5,我将获得相同的分配:localPos [5] = pos [5]

因此,根据我的理解,在第一次迭代和第一次“屏障”之后,每个工作项包含相同的局部数组“localPos”,即第一个全局块的子数组,即“pos” [0:24]”。

这是对发生的事情的一个很好的解释吗?