当我在 Linux 上编译现在的代码时,我收到以下警告:
t614.cu(55): warning: __shared__ memory variable with non-empty constructor or destructor (potential race between threads)
不应忽视此类警告。它与这行代码相关:
__shared__ double3c blockmean[THREADS_PER_BLOCK];
由于存储在共享内存中的这些对象的初始化(通过构造函数)将以某种任意顺序发生,并且在该初始化与也将设置这些值的后续代码之间没有障碍,不可预测的事情(*) 可以发生。
如果我插入一个__syncthreads()
在将构造函数活动与后续代码隔离的代码中,我得到了预期的结果:
__shared__ double3c blockmean[THREADS_PER_BLOCK];
int index = threadIdx.x + blockIdx.x * blockDim.x;
__syncthreads(); // add this line
if (index < m.size_x * m.size_y * m.size_z) blockmean[threadIdx.x] = m.data[index] = double3c(0, 1, 0);
else blockmean[threadIdx.x] = double3c(0,0,0);
__syncthreads();
然而,这仍然给我们留下了警告。解决此问题并使警告消失的修改是分配必要的__shared__
动态大小。将共享内存声明更改为:
extern __shared__ double3c blockmean[];
并修改你的内核调用:
KernelCalculateMeanFieldBlock <<<blocknum, THREADS_PER_BLOCK, THREADS_PER_BLOCK*sizeof(double3c)>>> (m, cu_mean);
这将消除警告,产生正确的结果,并避免共享内存变量上不必要的构造函数流量。 (以及额外的__syncthreads()
上面的描述就不再需要了。)
*关于“不可预测的事情”,如果您通过检查生成的 SASS (库对象转储 http://docs.nvidia.com/cuda/cuda-binary-utilities/index.html#abstract-萨斯...)或 PTX(**) (nvcc -ptx ...),你会看到每个线程初始化entire __shared__
对象数组为零(默认构造函数的行为)。因此,一些线程(即扭曲)可以提前竞争并开始根据以下行填充共享内存区域:
if (index < m.size_x * m.size_y * m.size_z) blockmean[threadIdx.x] = m.data[index] = double3c(0, 1, 0);
然后,当其他扭曲开始执行时,这些线程将清除entire再次共享内存阵列。这种赛车行为会导致不可预测的结果。
** 我通常不建议通过检查 PTX 来判断代码行为,但在本例中它同样具有指导意义。最后的编译阶段不会优化构造函数的行为。