[英]Why is my CUDA warp shuffle sum using the wrong offset for one shuffle step?
编辑:我已在https://developer.nvidia.com/nvidia_bug/3711214将此作为错误提交。
我正在编写一个数值模拟程序,它在发布模式下给出了略微不正确的结果,但在调试模式下看似正确的结果。 原始程序使用 curand 进行随机采样,但我已将其简化为更简单且更具确定性的 MVCE,它启动单个 kernel 的 1 个块 * 1 个经纱(32 个线程),其中每个线程:
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <stdio.h>
__global__ void test_kernel()
{
int cSteps = 0;
int cIters = 0;
float pos = 0;
//curandState localState = state[threadIdx.x];
while (true) {
float rn = threadIdx.x * 0.01 + 0.001;
pos += rn;
cSteps++;
if (pos > 1.0f) {
pos = 0;
cIters++;
if (cSteps > 1024) {
break;
}
}
}
printf(" 0: Th %d cI %d\n", threadIdx.x, cIters);
__syncthreads();
cSteps += __shfl_xor_sync(0xffffffff, cSteps, 1, 32);
cIters += __shfl_xor_sync(0xffffffff, cIters, 1, 32);
printf(" 1: Th %2d cI %d\n", threadIdx.x, cIters);
cSteps += __shfl_xor_sync(0xffffffff, cSteps, 2, 32);
cIters += __shfl_xor_sync(0xffffffff, cIters, 2, 32);
printf(" 2: Th %2d cI %d\n", threadIdx.x, cIters);
cSteps += __shfl_xor_sync(0xffffffff, cSteps, 4, 32);
cIters += __shfl_xor_sync(0xffffffff, cIters, 4, 32);
printf(" 4: Th %2d cI %d\n", threadIdx.x, cIters);
cSteps += __shfl_xor_sync(0xffffffff, cSteps, 8, 32);
cIters += __shfl_xor_sync(0xffffffff, cIters, 8, 32);
printf(" 8: Th %2d cI %d\n", threadIdx.x, cIters);
cSteps += __shfl_xor_sync(0xffffffff, cSteps, 16, 32);
cIters += __shfl_xor_sync(0xffffffff, cIters, 16, 32);
printf("16: Th %2d cI %d\n", threadIdx.x, cIters);
}
int main()
{
test_kernel <<<1, 32>>> ();
return 0;
}
在调试模式下,随机播放按预期工作。 我看到每个线程都有自己的值:
0: Th 0 cI 2
0: Th 1 cI 12
0: Th 2 cI 22
0: Th 3 cI 32
0: Th 4 cI 41
// ...
在第一次 shuffle xor 1 之后,每对线程都同意相同的数字:
1: Th 0 cI 14
1: Th 1 cI 14
1: Th 2 cI 54
1: Th 3 cI 54
在 shuffle xor 2 之后,每组四个线程都同意:
2: Th 0 cI 68
2: Th 1 cI 68
2: Th 2 cI 68
2: Th 3 cI 68
2: Th 4 cI 223
2: Th 5 cI 223
2: Th 6 cI 223
2: Th 7 cI 223
等等。 在最后一次 shuffle 之后,warp 中的所有线程都同意相同的值 (4673)。
一旦我启用发布模式,我得到的结果是微妙的垃圾。 进入 shuffle 的值是相同的,并且第一轮 shuffle 之后的值与 debug build 一致(并且在每对中都像以前一样一致)。 一旦我执行 shuffle xor 2,结果就会分崩离析:
2: Th 0 cI 28
2: Th 1 cI 28
2: Th 2 cI 108
2: Th 3 cI 108
2: Th 4 cI 186
2: Th 5 cI 186
2: Th 6 cI 260
2: Th 7 cI 260
事实上,如果将随机序列替换为这个特定的损坏序列,则调试构建(和手动检查)将产生的确切 output :
printf(" 0: Th %d cI %d\n", threadIdx.x, cIters);
__syncthreads();
cSteps += __shfl_xor_sync(0xffffffff, cSteps, 1, 32);
cIters += __shfl_xor_sync(0xffffffff, cIters, 1, 32);
printf(" 1: Th %2d cI %d\n", threadIdx.x, cIters);
cSteps += __shfl_xor_sync(0xffffffff, cSteps, 1, 32); // 2 changed to 1
cIters += __shfl_xor_sync(0xffffffff, cIters, 1, 32); // 2 changed to 1
printf(" 2: Th %2d cI %d\n", threadIdx.x, cIters);
cSteps += __shfl_xor_sync(0xffffffff, cSteps, 4, 32);
cIters += __shfl_xor_sync(0xffffffff, cIters, 4, 32);
printf(" 4: Th %2d cI %d\n", threadIdx.x, cIters);
cSteps += __shfl_xor_sync(0xffffffff, cSteps, 8, 32);
cIters += __shfl_xor_sync(0xffffffff, cIters, 8, 32);
printf(" 8: Th %2d cI %d\n", threadIdx.x, cIters);
cSteps += __shfl_xor_sync(0xffffffff, cSteps, 16, 32);
cIters += __shfl_xor_sync(0xffffffff, cIters, 16, 32);
output 的完整差异在这里。
软硬件环境如下:
GA103 3080Ti(移动),制造商推荐的时钟,16 G VRAM。 机器似乎没有与其他 Cuda 程序一起损坏(使用 primegrid-CUDA 测试并通过双重检查验证了任务)
CUDA 11.0
MVSC 主机编译器 14.29.30133
完整的调试命令行如下:
"C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.0\bin\nvcc.exe" -gencode=arch=compute_52,code=\"sm_52,compute_52\" --use-local-env -ccbin "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\bin\HostX86\x64" -x cu -I"C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.0\include" -I"C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.0\include" -G --keep-dir x64\Debug -maxrregcount=0 --machine 64 --compile -cudart static -g -DWIN32 -DWIN64 -D_DEBUG -D_CONSOLE -D_MBCS -Xcompiler "/EHsc /W3 /nologo /Od /Fdx64\Debug\vc142.pdb /FS /Zi /RTC1 /MDd " -o x64\Debug\kernel.cu.obj "C:\Users\[username]\source\repos\BugRepro\BugRepro\kernel.cu"
完整的发布命令行如下:
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.0\bin\nvcc.exe" -gencode=arch=compute_52,code=\"sm_52,compute_52\" --use-local-env -ccbin "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\bin\HostX86\x64" -x cu -I"C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.0\include" -I"C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.0\include" --keep-dir x64\Release -maxrregcount=0 --machine 64 --compile -cudart static -DWIN32 -DWIN64 -DNDEBUG -D_CONSOLE -D_MBCS -Xcompiler "/EHsc /W3 /nologo /O2 /Fdx64\Release\vc142.pdb /FS /Zi /MD " -o x64\Release\kernel.cu.obj "C:\Users\[username]\source\repos\BugRepro\BugRepro\kernel.cu"
我没有解决办法尝试的事情:
让每个线程写入全局 memory 然后在主机 CPU 上求和确实会产生正确的结果。
用调用__shfl_sync
和手动计算的通道 ID 替换所有 shuffle 是可行的。 仅用__shfl_sync
替换损坏的 shuffle xor 2不会。 用__shfl_sync __shfl_sync
第一个 shuffle xor 1 (正常工作)似乎可以解决它。 (这两种变通方法适用于我的 MVCE;我还没有机会评估它们是否适用于整个程序)
// unexpectedly working
int id = threadIdx.x;
printf(" 0: Th %d cI %d\n", threadIdx.x, cIters);
__syncthreads();
cSteps += __shfl_sync(0xffffffff, cSteps, id ^ 1, 32);
cIters += __shfl_sync(0xffffffff, cIters, id ^ 1, 32);
printf(" 1: Th %2d cI %d\n", threadIdx.x, cIters);
cSteps += __shfl_xor_sync(0xffffffff, cSteps, 2, 32);
cIters += __shfl_xor_sync(0xffffffff, cIters, 2, 32);
printf(" 2: Th %2d cI %d\n", threadIdx.x, cIters);
cSteps += __shfl_xor_sync(0xffffffff, cSteps, 4, 32);
cIters += __shfl_xor_sync(0xffffffff, cIters, 4, 32);
printf(" 4: Th %2d cI %d\n", threadIdx.x, cIters);
cSteps += __shfl_xor_sync(0xffffffff, cSteps, 8, 32);
cIters += __shfl_xor_sync(0xffffffff, cIters, 8, 32);
printf(" 8: Th %2d cI %d\n", threadIdx.x, cIters);
cSteps += __shfl_xor_sync(0xffffffff, cSteps, 16, 32);
cIters += __shfl_xor_sync(0xffffffff, cIters, 16, 32);
printf("16: Th %2d cI %d\n", threadIdx.x, cIters);
即使我有一个解决方法,我担心我仍然会在某个地方遇到未定义的行为,我的修复可能很脆弱。
任何人都可以阐明这一点吗? 我的程序中确实有UB吗? 这是一个已知的编译器错误吗?
据 CUDA 工程团队确认,这是一个编译器错误。 正如他们的通信所证实的那样,修复即将推出:
该修复针对的是 CUDA 11 之后的未来主要 CUDA 版本。在最新的 R515 在线之后,JIT 修复可能会在驱动程序分支中稍早一些。
他们还确认关闭优化可以解决问题; 他们没有评论任何在优化仍然有效的情况下可靠工作的解决方法。
以下解决方法在我的硬件和编译器上对我有用,但 YMMV:
__shfl_sync
代替shfl_add_sync
或shfl_xor_sync
__reduce_add_sync
问题未解决?试试以下方法:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.