那样的话,就没法解释了,因为atomicAdd(&addr, 1);是一个特定的行为,会被自动识别并且编译为如下步骤(NV所称的,自动的为了减少原子操作的竞争所进行的优化),
(1)在atomicAdd所在的行进行warp投票(vote.all), 选出当前的warp中的所有存活线程,
(2)统计warp中所有激活的线程的数量(值:1-32),记录为V
(3)判断当前线程是否为warp中第一个激活的线程,如果是,执行1次old = atomicAdd(..., V);
------到此你的测试流程结束-----
(4)如果atomicAdd的返回值还需要继续使用(即这是一个ATOM,而不是RED),将激活的线程的返回值传递给每个人,每个人进行warp内SP计算之前的激活线程数量,获取到正确分布的(而且是固定顺序分布的)old + 1... old + 32
也就是这种情况下,32个线程实际上只会执行1次L2上的原子操作。并没有32次。这个特性是从CUDA 8.0+引入的软件实现的特性(和硬件无关)。
所以我很奇怪你的结果,这将是无法解释的。所以,也许nsight报告的相关这个值,看看就好,不反应实际情况了。(你可以用nvcc -arch sm_86 your.cu -cubin -o your.cubin, cubojdump --dump-sass your.cubin, 反编译,来验证这个编译器的“模拟一次原子操作”的行为)。
我按照上述步骤进行了操作,得到的结果如下:
.headerflags @"EF_CUDA_SM86 EF_CUDA_PTX_SM(EF_CUDA_SM86)"
/*0000*/ IMAD.MOV.U32 R1, RZ, RZ, c[0x0][0x28] ; /* 0x00000a00ff017624 */
/* 0x000fc400078e00ff */
/*0010*/ S2R R0, SR_LANEID ; /* 0x0000000000007919 */
/* 0x000e220000000000 */
/*0020*/ VOTEU.ALL UR4, UPT, PT ; /* 0x0000000000047886 */
/* 0x000fe200038e0000 */
/*0030*/ MOV R2, c[0x0][0x160] ; /* 0x0000580000027a02 */
/* 0x000fe20000000f00 */
/*0040*/ UFLO.U32 UR5, UR4 ; /* 0x00000004000572bd */
/* 0x000fe200080e0000 */
/*0050*/ POPC R5, UR4 ; /* 0x0000000400057d09 */
/* 0x000e620008000000 */
/*0060*/ IMAD.MOV.U32 R3, RZ, RZ, c[0x0][0x164] ; /* 0x00005900ff037624 */
/* 0x000fc800078e00ff */
/*0070*/ ISETP.EQ.U32.AND P0, PT, R0, UR5, PT ; /* 0x0000000500007c0c */
/* 0x001fe2000bf02070 */
/*0080*/ ULDC.64 UR4, c[0x0][0x118] ; /* 0x0000460000047ab9 */
/* 0x000fd80000000a00 */
/*0090*/ @P0 RED.E.ADD.STRONG.GPU [R2.64], R5 ; /* 0x000000050200098e */
/* 0x002fe2000c10e184 */
/*00a0*/ EXIT ; /* 0x000000000000794d */
/* 0x000fea0003800000 */
/*00b0*/ BRA 0xb0; /* 0xfffffff000007947 */
/* 0x000fc0000383ffff */
/*00c0*/ NOP; /* 0x0000000000007918 */
/* 0x000fc00000000000 */
/*00d0*/ NOP; /* 0x0000000000007918 */
/* 0x000fc00000000000 */
/*00e0*/ NOP; /* 0x0000000000007918 */
/* 0x000fc00000000000 */
/*00f0*/ NOP; /* 0x0000000000007918 */
/* 0x000fc00000000000 */
/*0100*/ NOP; /* 0x0000000000007918 */
/* 0x000fc00000000000 */
/*0110*/ NOP; /* 0x0000000000007918 */
/* 0x000fc00000000000 */
/*0120*/ NOP; /* 0x0000000000007918 */
/* 0x000fc00000000000 */
/*0130*/ NOP; /* 0x0000000000007918 */
/* 0x000fc00000000000 */
/*0140*/ NOP; /* 0x0000000000007918 */
/* 0x000fc00000000000 */
/*0150*/ NOP; /* 0x0000000000007918 */
/* 0x000fc00000000000 */
/*0160*/ NOP; /* 0x0000000000007918 */
/* 0x000fc00000000000 */
/*0170*/ NOP; /* 0x0000000000007918 */
/* 0x000fc00000000000 */
虽然我看不懂这个汇编代码,不过似乎的确是有
VOTEU.ALL这种指令,我修改为atomicAdd(&addr, 10)似乎还是同样的流程,什么情况下会编译成这样的汇编?一个warp内的线程对同样位置的显存数据做常量原子加、减?这块内容有官方文档嘛?想去学习下