如何在AVX寄存器中实现最快的64位分段popcount计算?
寻求AVX2寄存器中64位分段popcount的高效计算方法
我正在找一种更高效的方式,计算256位AVX寄存器里每个64位分段的popcount(二进制中1的个数)。
当前我的实现方案如下:
const __m256i lookup = _mm256_setr_epi8( /* 0 */ 0, /* 1 */ 1, /* 2 */ 1, /* 3 */ 2, /* 4 */ 1, /* 5 */ 2, /* 6 */ 2, /* 7 */ 3, /* 8 */ 1, /* 9 */ 2, /* a */ 2, /* b */ 3, /* c */ 2, /* d */ 3, /* e */ 3, /* f */ 4, /* 0 */ 0, /* 1 */ 1, /* 2 */ 1, /* 3 */ 2, /* 4 */ 1, /* 5 */ 2, /* 6 */ 2, /* 7 */ 3, /* 8 */ 1, /* 9 */ 2, /* a */ 2, /* b */ 3, /* c */ 2, /* d */ 3, /* e */ 3, /* f */ 4 ); __m256i popcount_avx2_64(const __m256i vec) { const __m256i low_mask = _mm256_set1_epi8(0x0f); const __m256i lo = _mm256_and_si256(vec, low_mask); const __m256i hi = _mm256_and_si256(_mm256_srli_epi16(vec, 4), low_mask); const __m256i popcnt1 = _mm256_shuffle_epi8(lookup, lo); const __m256i popcnt2 = _mm256_shuffle_epi8(lookup, hi); __m256i local = _mm256_setzero_si256(); local = _mm256_add_epi8(local, popcnt1); local = _mm256_add_epi8(local, popcnt2); const __m256i mask2 = _mm256_set1_epi64x(0xff); __m256i ret; ret = _mm256_add_epi8(local, _mm256_srli_epi32(local, 8)); ret = _mm256_add_epi8(ret, _mm256_srli_epi32(ret, 16)); ret = _mm256_add_epi8(ret, _mm256_srli_epi64(ret, 32)); ret = _mm256_and_si256(ret, mask2); return ret; }
这段代码通过查表获取每个uint8_t分段的popcount,随后在每个64位分段内累加这些值,改编自开源的SSE-popcount实现。
和原实现的用途不一样,我不需要计算大数值的汉明权重,而是要处理海量(比如2^30个)64位数值,筛选出汉明权重低于指定固定阈值的条目。
感谢@chtz的建议,我现在的优化方案如下:
const __m256i lookup = _mm256_setr_epi8( /* 0 */ 0, /* 1 */ 1, /* 2 */ 1, /* 3 */ 2, /* 4 */ 1, /* 5 */ 2, /* 6 */ 2, /* 7 */ 3, /* 8 */ 1, /* 9 */ 2, /* a */ 2, /* b */ 3, /* c */ 2, /* d */ 3, /* e */ 3, /* f */ 4, /* 0 */ 0, /* 1 */ 1, /* 2 */ 1, /* 3 */ 2, /* 4 */ 1, /* 5 */ 2, /* 6 */ 2, /* 7 */ 3, /* 8 */ 1, /* 9 */ 2, /* a */ 2, /* b */ 3, /* c */ 2, /* d */ 3, /* e */ 3, /* f */ 4 ); const __m256i low_mask = _mm256_set1_epi8(0x0f); __m256i popcount_avx2_64(const __m256i vec) noexcept { const __m256i lo = _mm256_and_si256(vec, low_mask); const __m256i hi = _mm256_and_si256(_mm256_srli_epi16(vec, 4), low_mask); const __m256i popcnt1 = _mm256_shuffle_epi8(lookup, lo); const __m256i popcnt2 = _mm256_shuffle_epi8(lookup, hi); const __m256i local = _mm256_add_epi8(popcnt2, popcnt1); const __m256i ret =_mm256_sad_epu8 (local, _mm256_setzero_si256()); return ret; }
内容的提问来源于stack exchange,提问作者PingFloyd
相关产品推荐
相关产品推荐

