与 talonmies 的回答相反,我发现warp_size
恒定是完全可以接受的。使用的唯一原因warpSize
是使代码与可能具有不同大小的扭曲的未来硬件向前兼容。但是,当此类硬件到来时,内核代码很可能还需要进行其他更改以保持效率。CUDA 不是一种与硬件无关的语言——相反,它仍然是一种相当低级的编程语言。生产代码使用随时间变化的各种内在函数(例如__umul24
)。
当我们得到不同的经纱尺寸(例如 64)时,很多事情都会改变:
- 遗嘱
warpSize
显然需要调整
- 许多经线级别的内在函数需要调整其签名,或生成新版本,例如
int __ballot
,虽然int
不需要是 32 位,但最常见的是!
迭代操作,例如扭曲级别减少,将需要调整其迭代次数。我从未见过有人写:
for (int i = 0; i < log2(warpSize); ++i) ...
在通常是时间关键的代码中,这将过于复杂。
warpIdx
并且需要调整laneIdx
计算。threadIdx
目前,我看到的最典型的代码是:
warpIdx = threadIdx.x/32;
laneIdx = threadIdx.x%32;
这简化为简单的右移和掩码操作。但是,如果换成32
这个warpSize
突然就变成了相当昂贵的操作!
同时,warpSize
在代码中使用会阻止优化,因为形式上它不是编译时已知的常量。此外,如果共享内存的数量取决于warpSize
这会迫使您使用动态分配的 shmem(根据 talonmies 的回答)。但是,这种语法使用起来很不方便,尤其是当您有多个数组时——这会迫使您自己进行指针运算并手动计算所有内存使用量的总和。
为此使用模板warp_size
是部分解决方案,但会在每个函数调用中增加一层语法复杂性:
deviceFunction<warp_size>(params)
这混淆了代码。样板文件越多,代码就越难阅读和维护。
我的建议是有一个头文件来控制所有特定于模型的常量,例如
#if __CUDA_ARCH__ <= 600
//all devices of compute capability <= 6.0
static const int warp_size = 32;
#endif
现在您的 CUDA 代码的其余部分可以使用它而无需任何语法开销。当您决定添加对更新架构的支持时,您只需要更改这段代码。