假设我有一堆带有一堆块的CUDA内核,并假设在同一个对称多处理器上的另一个块之后安排了一个块(也就是说,所有warp的共享内存区域相同的单元).目前,NVIDIA没有在API或每GPU文档中指定执行之间共享内存会发生什么.但实际上,关于块的共享内存内容,下列哪一项呢?:
它处于同一状态,最后一个预定的块留下了它.
这是空白的.
它包含不可预见的垃圾.
为了缩小可能出现的情况的变化,请具体参考每个块使用最大可能共享内存量的情况 - 在Kepler GPU上为48 KB.
NVIDIA不公布此级别的硬件行为,因此您应该将其视为未定义(如@datenwolf所述).当然,虽然给定块看到的共享内存的内容不是随机的.并且硬件没有必要花时间清理内存.
GPU可以在每个SM上同时运行多个块.同时为给定内核运行的块数取决于各种因素.因此,例如,如果共享内存是限制因素,则每个SM将运行尽可能多的块以适应共享内存.因此,如果共享内存为48K且块需要10K,则可以使用40K同时运行4个块.所以,如果你有一个带有8个SM的设备,我的猜测是给定块的共享内存将有32(4*8)个可能的固定位置.因此,当计划新块时,它将被分配给其中一个位置,并查看在该位置运行的前一个块所留下的共享内存.
API无法阻止块检测它在哪个位置运行.块的调度是动态确定的,可能很难预测.
如果GPU用于显示,它可能同时运行其他内核(着色器),可能以奇怪和奇妙的方式覆盖CUDA内核中的块之间的共享内存.甚至CUDA也可能在幕后运行其他内核.
编辑:
我写了一个小程序来测试(包含在下面).程序将一个块应存储在共享内存中的整数数作为参数.然后它启动100,000个块,每个块有一个线程.每个块检查其共享内存是否已初始化.如果它已初始化,则该块不再执行任何操作.如果未初始化,则块初始化内存并增加全局计数.初始化模式是一个递增的数字序列,以避免部分重叠的初始化共享内存缓冲区似乎是有效的.
在GTX660(Kepler,CC 3.0,5个SM)上配置了48K共享内存,CC 3.0 Release版本,我得到了以下结果:
C:\rd\projects\cpp\test_cuda\Release>test_cuda.exe 10000
Shared memory initializations: 5
我跑了几次,每次都得到相同的结果.这与我最初的猜测相匹配,因为10000个整数占用~40K,因此每个SM可以有一个并发块的空间,并且该设备有5个SM.
但是,当我将共享内存减少到2500个整数(~10K),期望获得20次初始化并运行几次时,我得到了不同的高数字:
Shared memory initializations: 32,822
Shared memory initializations: 99,996
Shared memory initializations: 35,281
Shared memory initializations: 30,748
因此,在这种情况下,我对固定位置的猜测完全无效.
然后我尝试将共享内存减少到100个整数(在48K中可以容纳122个块)并且始终如一:
Shared memory initializations: 480
因此,再次,不是预期的数字,并且令人惊讶的是,即使每个块使用的共享内存量较小,但可能的变化明显较少.
看起来,如果你决定用脚射击自己,你可以使用一个大的共享内存块来保持一致:)此外,这是在一个也用于显示的GPU上运行,Windows 7带有Aero(A GPU加速主题)并且看起来渲染不会干扰因为桌面在内核运行时冻结.
程序:
#include "cuda_runtime.h" #include <iostream> #include <sstream> using namespace std; #define assertCudaSuccess(ans) { _assertCudaSuccess((ans), __FILE__, __LINE__); } inline void _assertCudaSuccess(cudaError_t code, char *file, int line) { if (code != cudaSuccess) { fprintf(stderr,"CUDA Error: %s %s %d\n", cudaGetErrorString(code), file, line); exit(code); } } __global__ void shared_memory_persistence_test(int n_shared_ints); __device__ int init_cnt_d(0); int main(int argc, char* argv[]) { cout.imbue(locale("")); int n_shared_ints; stringstream(string(argv[1])) >> n_shared_ints; shared_memory_persistence_test<<<dim3(100, 1000), 1, n_shared_ints * sizeof(int)>>>(n_shared_ints); assertCudaSuccess(cudaPeekAtLastError()); assertCudaSuccess(cudaDeviceSynchronize()); int init_cnt_h; assertCudaSuccess(cudaMemcpyFromSymbol(&init_cnt_h, init_cnt_d, sizeof(int), 0, cudaMemcpyDeviceToHost)); cout << "Shared memory initializations: " << init_cnt_h << endl; return 0; } __global__ void shared_memory_persistence_test(int n_shared_ints) { extern __shared__ int shared[]; for (int i(0); i < n_shared_ints; ++i) { if (shared[i] != i) { for (int i(0); i < n_shared_ints; ++i) { shared[i] = i; } atomicAdd(&init_cnt_d, 1); break; } } }