我正在研究需要大量共享内存的 N 体问题。
基本上,有N
独立的任务,每个任务使用 4 个双精度变量,即 32 个字节。单个任务由一个线程执行。
为了快速起见,我一直在为这些变量使用共享内存(假设寄存器也被线程使用)。由于N
在编译时不知道任务的数量,因此共享内存是动态分配的。
网格的维度和共享内存的计算取决于
N
块大小:const size_t BLOCK_SIZE = 512; const size_t GRID_SIZE = (N % BLOCK_SIZE) ? (int) N/BLOCK_SIZE : (int) N/BLOCK_SIZE +1; const size_t SHARED_MEM_SIZE = BLOCK_SIZE * 4 * sizeof(double);
然后使用这 3 个变量启动内核。
kernel_function<<<GRID_SIZE, BLOCK_SIZE, SHARED_MEM_SIZE>>>(N, ...);
对于 small N
,这可以正常工作,并且内核执行时不会出错。
但是如果超过N = 1500
,内核启动失败(以下消息多次出现):
========= Invalid __global__ write of size 8
=========
========= Program hit cudaErrorLaunchFailure (error 4) due to "unspecified launch failure" on CUDA API call to cudaLaunch.
据我了解,这是由于试图写入超出已分配共享内存的界限。当在内核中将全局内存复制到共享内存中时,就会发生这种情况:
__global__ void kernel_function(const size_t N, double *pN, ...)
{
unsigned int idx = threadIdx.x + blockDim.x * blockIdx.x;
if(idx<N)
{
extern __shared__ double pN_shared[];
for(int i=0; i < 4; i++)
{
pN_shared[4*idx + i] = pN[4*idx + i];
}
...
}
}
此错误仅在 时发生,因此当共享内存N > 1500
总量超过48kB ( ) 时。
无论网格和块大小如何,此限制都是相同的。1500 * 4 * sizeof(double) = 1500 * 32 = 48000
如果我正确理解了 CUDA 的工作原理,那么网格使用的共享内存的累积量不限于48kB,这只是单个线程块可以使用的共享内存的限制。
这个错误对我来说毫无意义,因为累积的共享内存量只会影响流式多处理器之间的网格调度方式(此外,GPU 设备有 15 个 SM 可供使用)。