我正在尝试在 Mac 应用程序中的一系列相当复杂的计算内核中使用 SIMD 组缩减/前缀函数。我需要分配一些线程组内存来协调同一线程组中的 SIMD 组。因此,该数组的容量应取决于
[[simdgroups_per_threadgroup]]
,但这不是编译时值,因此它不能用作数组维度。
现在,根据各种 WWDC 会话视频,管道对象上的
threadExecutionWidth
应返回 SIMD 组大小,然后我可以使用计算编码器上的 setThreadgroupMemoryLength:atIndex:
分配适当数量的内存。
这在某些硬件上一致工作(例如 Apple M1,
threadExecutionWidth
似乎总是报告 32),但我遇到的配置中 threadExecutionWidth
与明显的 SIMD 组大小不匹配,导致由于越界访问而导致运行时错误。 (例如,在 Intel UHD Graphics 630 上,对于某些复杂内核,threadExecutionWidth
= 16,尽管 SIMD 组大小似乎为 32)
所以:
如果后者至少是正确的,我大概可以相信
threadExecutionWidth
对于最微不足道的内核?或者我应该向 GPU 提交一个简单的内核并返回 [[threads_per_simdgroup]]
?
我怀疑这个问题可能发生在 Metal 提供“奇数”(非 pow2)最大线程组大小的内核中,尽管在我遇到的情况下,最大线程组大小报告为 896,它是32,所以它并不是使用最大线程组大小和 SIMD 组大小之间的最大公分母来实现
threadExecutionWidth
。
我从来没有找到一个特别令人满意的解决方案,但我至少找到了一个有效的解决方案:
threadExecutionWidth
开始。simdgroups_per_threadgroup
的实际值进行比较。如果匹配,那就太好了,运行内核的其余部分。device
参数内存缓冲区中的反馈/错误报告变量/字段中的实际SIMD大小。然后提前退出计算内核。
device
内存中的状态检查计算内核是否提前退出。如果是这样,请检查报告的 SIMD 组大小,调整缓冲区分配,然后使用新值重新运行内核。对于真正偏执的人来说,明智的做法是在步骤 2 中进行检查,将其设置为下限或上限,或者可能是一个范围,而不是相等性检查:例如,分配的内存对于 SIMD 组大小最多或是安全的来自 N 个线程。这样,如果线程组缓冲区分配应该 change
simdgroups_per_threadgroup
(😱),你最终不会在值之间来回跳跃,没有任何进展。
还要注意您在 SIMD 组中所做的操作:并非所有 GPU 型号都支持 SIMD 组缩减功能,即使它们支持 SIMD 排列,因此如有必要,请为此类旧 GPU 提供替代版本的内核。
最后,我发现大多数 GPU 报告的 SIMD 组大小为 32 个线程,但 2015 年 MacBook Pro 的 Intel Iris Graphics 6100 报告
simdgroups_per_threadgroup
(和 threadExecutionWidth
)值为 8。(事实并非如此)不支持 SIMD 缩减函数,但支持 SIMD 排列函数,包括 simd_ballot()
,它几乎与某些算法的缩减一样有效。)