cuda-sdk的nbody代码中的线程管理

时间:2012-07-09 04:18:24

标签: cuda

当我在Cuda-SDK中读取nbody代码时,我在代码中经历了一些行,我发现它与他们在GPUGems3“使用CUDA进行快速N体仿真”中的论文略有不同。

我的问题是:首先,为什么blockIdx.x仍然参与从全局加载内存到共享内存,如下面的代码所示?

for (int tile = blockIdx.y; tile < numTiles + blockIdx.y; tile++)
{
    sharedPos[threadIdx.x+blockDim.x*threadIdx.y] =
        multithreadBodies ?
        positions[WRAP(blockIdx.x + q * tile + threadIdx.y, gridDim.x) * p + threadIdx.x] : //this line
        positions[WRAP(blockIdx.x + tile,                   gridDim.x) * p + threadIdx.x];  //this line

    __syncthreads();

    // This is the "tile_calculation" function from the GPUG3 article.
    acc = gravitation(bodyPos, acc);

    __syncthreads();
}

根据纸张不应该是这样吗?我想知道为什么

    sharedPos[threadIdx.x+blockDim.x*threadIdx.y] =
        multithreadBodies ?
        positions[WRAP(q * tile + threadIdx.y, gridDim.x) * p + threadIdx.x] :
        positions[WRAP(tile,                   gridDim.x) * p + threadIdx.x];

其次,在每个主体的多个线程中为什么还要涉及threadIdx.x?它不应该是一个固定值或根本不涉及,因为总和只是由于threadIdx.y

if (multithreadBodies)
{
    SX_SUM(threadIdx.x, threadIdx.y).x = acc.x; //this line
    SX_SUM(threadIdx.x, threadIdx.y).y = acc.y; //this line
    SX_SUM(threadIdx.x, threadIdx.y).z = acc.z; //this line

    __syncthreads();

    // Save the result in global memory for the integration step
    if (threadIdx.y == 0)
    {
        for (int i = 1; i < blockDim.y; i++)
        {
            acc.x += SX_SUM(threadIdx.x,i).x; //this line
            acc.y += SX_SUM(threadIdx.x,i).y; //this line
            acc.z += SX_SUM(threadIdx.x,i).z; //this line
        }
    }
}

任何人都可以向我解释这个吗?是否为更快的代码进行了某种优化?

2 个答案:

答案 0 :(得分:4)

我是此代码和论文的作者。编号的答案对应于您编号的问题。

  1. 本文未提及到WRAP宏的blockIdx.x偏移量,因为这是一个微优化。我甚至不确定它是否值得。目的是确保不同的SM访问不同的DRAM存储体而不是同时在同一个存储体上进行全部冲击,以确保在这些负载期间最大化存储器吞吐量。如果没有blockIdx.x偏移量,则所有同时运行的线程块将同时访问同一地址。由于整体算法是计算而不是带宽限制,因此这绝对是次要的优化。可悲的是,它使代码更加混乱。

  2. 如你所说,总和在threadIdx.y之间,但每个线程需要单独计算一个总和(每个线程计算一个单独体的引力)。因此,我们需要使用threadIdx.x索引(概念上的2D)共享内存数组的右列。

  3. 要回答SystmD的问题(不是真的正确),gridDim.y在(默认/常见)1D区块案例中只有1个。

答案 1 :(得分:0)

1) 在同步每个块的线程(使用__syncthreads())之前,将数组SharedPos加载到每个块的共享存储器(即每个块)中。根据算法,blockIdx.x是图块的索引。

每个线程(索引threadIdx.x threadIdx.y)加载共享数组SharedPos的一部分。 blockIdx.x指的是tile的索引(没有多线程)。

2) acc是body索引的float3 blockIdx.x * blockDim.x + threadIdx.x(参见integrateBodies函数的开头)

我发现multithreadBodies = true在这个总和中有一些问题,q> 4(128个体,p = 16,q = 8 gridx = 8)。 (使用GTX 680)。在整个blockDim.y中没有完成一些总和......

我改变了代码以避免这种情况,它有效,但我不知道为什么......

if (multithreadBodies)
{
    SX_SUM(threadIdx.x, threadIdx.y).x = acc.x;
    SX_SUM(threadIdx.x, threadIdx.y).y = acc.y;
    SX_SUM(threadIdx.x, threadIdx.y).z = acc.z;

    __syncthreads();


        for (int i = 0; i < blockDim.y; i++) 
        {
            acc.x += SX_SUM(threadIdx.x,i).x;
            acc.y += SX_SUM(threadIdx.x,i).y;
            acc.z += SX_SUM(threadIdx.x,i).z;
        }

}

另一个问题: 在第一个循环中:

for (int tile = blockIdx.y; tile < numTiles + blockIdx.y; tile++) 
{
}

我不知道为什么因为grid.y = 1而使用了blockIdx.y。

3)对于更快的代码,我使用异步H2D和D2D内存副本(我的代码只使用引力内核)。