使用F#与SIMD查找数组值索引时TrailingZeroCount返回结果异常
问题根因
你得到的idx为4而非预期的1,是因为你计算偏移时使用的是字节粒度的结果,没有转换为原始int32类型的粒度:
Sse2.CompareEqual处理Vector128<int>时,匹配的元素会将对应32位空间全部置为1,即1个匹配的int32对应4个连续的0xFF字节- 你将比较结果转为字节向量后调用
Sse2.MoveMask,返回值的每1位对应1个字节的最高位,此时1个int32元素对应4位mask位 - 测试用例中索引为1的int32匹配时,对应mask的第4~7位为1,
BitOperations.TrailingZeroCount返回末尾零的个数就是4
修正方案
只需要将计算得到的字节偏移除以int32的字节长度(4字节)即可得到正确的类型索引,将对应代码修改为:
let idx = BitOperations.TrailingZeroCount matches >>> 2
右移2位等价于整数除法除以4,修改后即可输出预期结果1。如果需要适配其他数值类型,只需要调整右移位数即可:int16对应右移1位(除以2字节)、int64对应右移3位(除以8字节)。
实现效率相关说明
这种矢量化搜索的实现思路是目前.NET下性能最高的方案之一,比普通逐元素遍历性能高3~8倍(取决于使用128位还是256位向量),可以根据场景做进一步优化:
- 如果搜索的数组长度超过
Vector128<int>.Count(即4个元素),需要添加循环逻辑批量处理每一段向量长度的元素 - 支持AVX2指令集的设备可以换成256位向量运算,单次处理8个int32元素,吞吐量翻倍
- 如果需要兼顾不同硬件平台的兼容性,可以使用.NET 7+提供的
Vector<T>做平台无关心矢量化实现,不需要手写硬件Intrinsic,性能和手写Intrinsic的差异在5%以内
内容的提问来源于stack exchange,提问作者Matthew Crews
相关产品推荐
相关产品推荐

