简短的问题
为什么我从带有浮点数的Metal 2.1中的simd_min
和simd_max
函数中得到未定义的行为?
更新:似乎这仅发生在Radeon Pro 560X GPU上,而不发生在Intel UHD Graphics 630上。
背景
根据Metal Shading Language Guide第5.14节,常见的标量或向量,整数或浮点类型支持simd_min
和simd_max
函数。
对于simd_max
,规范规定:
T simd_max(T data)
返回所有数据的最大值 SIMD组中的活动线程和 将结果广播到中的所有活动线程 SIMD组。
测试用例
要对此进行测试,我正在执行以下测试内核,其输入缓冲区为128个随机浮点数,范围为0..100:
kernel void simdMaxDebugKernel(
const device float *buffer [[ buffer(0) ]],
device float *output [[ buffer(1) ]],
uint id [[ thread_position_in_grid ]])
{
output[id] = simd_max(buffer[id]);
}
通过检查,将128值缓冲区分为两个64值SIMD组。因此,我希望输出中的前64个值将分别设置为第一个SIMD组和最后一个SIMD组的最大值。
测试结果
我得到了一些意想不到的结果:
inputs [simd_float1] 128 values
[0] Float 94.3006362
[1] Float 98.1107177
[2] Float 85.3725891
[3] Float 45.1457863
...
[63] Float 36.5486336
[64] Float 56.5494308
[65] Float 45.6249847
[66] Float 34.8077431
actual [simd_float1] 128 values
[0] Float 94.3006362
[1] Float NaN
[2] Float -3.80461845E+20
[3] Float 0.0000000000000000000000000000000000000212763294
...
[63] Float 0
[64] Float 56.5494308
[65] Float -2467.3457
[66] Float 0.0000000000010178117
...
expectedMax simd_float1 99.4676971
在我看来,每个SIMD组的第一个SIMD通道的值都只是被复制,其余的未定义。
相比之下,如果按如下所示使用到uint
的转换,则内核的行为符合预期:
output[id] = (float)simd_max((uint)buffer[id]);
→
actual [simd_float1] 128 values
[0] Float 99
[1] Float 99
[2] Float 99
...
[63] Float 99
[64] Float 96
[65] Float 96
...
测试配置