[英]When should I use CUDA's built-in warpSize, as opposed to my own proper constant?
nvcc设备代码可以访问内置值warpSize
,该值设置为执行内核的设备的扭曲大小(即在可预见的将来为32)。 通常,您只能将其与常量区分开来-但是,如果您尝试声明一个长度为warpSize的数组,则会抱怨它是非常量的(使用CUDA 7.5)。
因此,至少出于这个目的,您有动力拥有类似( 编辑 )的东西:
enum : unsigned int { warp_size = 32 };
标头中的某处。 但是,现在-我更喜欢哪个,什么时候? : warpSize
还是warp_size
?
编辑: warpSize
显然是PTX中的编译时常量。 问题仍然存在。
让我们直接讲几点。 扭曲大小不是编译时间常数,不应视为一个。 它是特定于体系结构的运行时即时常量 (对于迄今为止的所有体系结构,其值恰好为32)。 曾几何时,旧的Open64编译器确实向PTX发出了一个常量,但是至少在6年前,如果我的记忆没有让我失望,它就会改变。
该值可用:
warpSize
,其中不是编译时间常数 (在这种情况下,编译器会发出PTX WARP_SZ
变量)。 WARP_SZ
PTX汇编器中,它是运行时立即数 不要声明您自己的变形大小常数,这只是在麻烦。 尺寸为扭曲大小倍数的内核数组的正常使用情况是使用动态分配的共享内存。 您可以在运行时从主机API读取扭曲大小来获取它。 如果您有一个静态声明的内核,则需要从扭曲大小开始调整尺寸,使用模板并在运行时选择正确的实例。 后者似乎是不必要的剧院,但是对于在实践中几乎从未出现过的用例来说,这样做是正确的。 这是你的选择。
与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
,它不是编译时已知的常量。 同样,如果共享内存的数量取决于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代码的其余部分可以使用它,而没有任何语法开销。 在决定添加对较新架构的支持的那一天,您只需要更改这一段代码即可。
问题未解决?试试以下方法:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.