我想获取256位向量之间的8位垂直SIMD比较的结果,并将比特打包到每个32位元素的最低字节中,以便在最低字节上进行vpshufb查找。使用AVX-512 (如果使用512位向量,则用蒙面移动替换& ),这并不困难:
__m256i cmp_8_into_32(__m256i a, __m256i b) {
return _mm256_popcnt_epi32(_mm256_cmpeq_epi8(a, b)
& _mm256_set1_epi32(0xff0f0301 /* can be any order */));
}根据uops.info,这是三个uop,假设是完美的调度,吞吐量为1--不错。唉,vpopcntd不在AVX2里。做这个手术的最佳方法是什么?我能想到的最好的方法是在索引7,8和15,16处掩蔽位对,然后执行两个恒量vpsrld和一个vpor。这是6 uop,吞吐量为2.5 ish。不错,但我不知道还有什么更好的。
发布于 2022-10-20 19:59:16
使用1乘:
__m256i cmp_8_into_32(__m256i a, __m256i b) {
__m256i cmp = _mm256_cmpeq_epi8(a, b);
__m256i msk = _mm256_and_si256(cmp, _mm256_set1_epi32(0x08040201));
__m256i hsum = _mm256_madd_epi16(msk, _mm256_set1_epi8(1));
return _mm256_srli_epi16(hsum, 8);
}32位乘法(_mm256_mullo_epi32)不使用,因为它是缓慢的.
如果结果不需要“在车道”,那么您可以在比较之后立即使用_mm256_packs_epi16处理一次两倍多的数据。如果您不需要所有可能的状态(例如,我们不关心最低字节匹配),那么每条指令可以执行4倍的操作。如果vpshufb查找的结果被聚集在一起,那么可能还有其他可能的优化。
发布于 2022-10-20 07:58:50
以下是chtz的评论(谢谢!),我意识到这其实很简单:
__m256i cmp_8_into_32_1(__m256i a, __m256i b) {
const __m256i weights = _mm256_set1_epi32(0x08040201);
const __m256i all_n1 = _mm256_set1_epi16(-0x1);
__m256i cmp = _mm256_cmpeq_epi8(a, b);
__m256i hsum16 = _mm256_maddubs_epi16(weights, cmp);
return _mm256_madd_epi16(hsum16, all_n1);
}彼得·科德斯的建议节省了额外的vpand。这两个multiply指令都运行在端口0或1上,因此它的吞吐量与原始的基于popcount的解决方案相同,尽管延迟约为11而不是5。
https://stackoverflow.com/questions/74134353
复制相似问题