如何用SIMD统计数组中指定字节的出现次数?附代码示例疑问
使用SIMD统计数组中指定字节的出现次数
首先,你已经走对了第一步——用异或来定位匹配字节:当原字节和目标字节(55)相等时,异或结果会是0,这个逻辑完全没问题。接下来的核心是把这些0的位置转换成可计数的标识,再高效累加统计数量,下面一步步给你拆解:
1. 从异或结果生成匹配掩码
异或后值为0的位置就是我们要找的匹配项,我们可以用SIMD的Vector.Equals方法,把异或结果和全0向量做比较,生成一个掩码向量:
var xorResult = Vector.Xor(vBytes, mask); // 匹配的位置会被设为byte类型的最大值0xFF,不匹配则为0 var matchMask = Vector.Equals(xorResult, Vector<byte>.Zero);
其实更直接的方式是跳过异或,直接用Vector.Equals(vBytes, mask)生成同样的掩码,这样少一步运算,效率会更高~
2. 统计掩码中匹配项的数量
现在matchMask里,每个匹配的位置都是0xFF(即255),不匹配的是0。我们需要统计有多少个0xFF,这里有两种高效的实现方式:
方法1:SIMD扩展与累加
把byte向量扩展为更大的整数类型(比如ushort),然后用Vector.Sum累加所有值,最后除以255得到次数:
// 将byte向量扩展为ushort向量(每个0xFF变为0x00FF) var widened = Vector.Widen(matchMask, matchMask); // 累加所有ushort值 var total = Vector.Sum(widened); // 每个匹配项贡献255,所以总和除以255就是次数 int count = (int)(total / 255);
方法2:分组整数累加(适合大数组场景)
如果处理的是超大数组,你可以把byte掩码直接转换为int向量(每4个byte合并为一个int),再累加:
// 将byte掩码转换为int向量(每4个byte打包成一个int) var intMask = Vector.AsVectorInt32(matchMask); // 累加所有int值 var intSum = Vector.Sum(intMask); // 每个匹配的byte贡献255,总和除以255得到次数 int count = intSum / 255;
针对你的示例验证
你的输入向量vBytes中,值为55的字节出现在索引2、6、11、13、14,共5次。用上面的代码计算,最终count会是5,和手动统计的结果完全一致。
完整的数组处理示例(针对大数组)
如果要处理整个数组而非单个向量,你可以参考这个完整实现:
int CountByteSimd(byte[] array, byte target) { int count = 0; var targetVec = new Vector<byte>(target); int vectorSize = Vector<byte>.Count; int i = 0; // 处理完整的SIMD块 for (; i <= array.Length - vectorSize; i += vectorSize) { var vec = new Vector<byte>(array, i); var matchMask = Vector.Equals(vec, targetVec); var intMask = Vector.AsVectorInt32(matchMask); count += Vector.Sum(intMask) / 255; } // 处理剩余的元素(不足一个SIMD向量的部分) for (; i < array.Length; i++) { if (array[i] == target) count++; } return count; }
内容的提问来源于stack exchange,提问作者MaYaN
相关产品推荐
相关产品推荐

