C ++ SIMD:在数组中累积uint64_t元素的POPCOUNT

时间:2017-02-26 02:09:30

标签: c++ sse simd

我正在尝试使用SSE指令为数组中的POPCOUNT整数累积uint64_t
这是我的代码:

#include <emmintrin.h>
#include <nmmintrin.h>
#include <chrono>

int main()
{    
  uint64_t data[4] = {1,1,1,1};
  uint64_t data2[4] = {1,0,1,0};
  __m128i* ptr = (__m128i*) data;
  __m128i* ptr2 = (__m128i*) data2;

  int total = 0;
  for (int i = 0; i < 2; ++i, ++ptr, ++ptr2)
    total += popcount(_mm_and_si128(*ptr, *ptr2)); // This doesn't work    
}

我需要等效POPCOUNT函数,该函数对_mm_and_si128的输出进行操作,因此我可以将所有POPCOUNT积累到total变量中。

2 个答案:

答案 0 :(得分:1)

POPCOUNT不适用于SSE寄存器。您需要将_mm_and_sil128的结果保存到内存中,然后在两半上使用POPCOUNT_mm_popcnt_u64),因为POPCOUNT指令最多限制为64位操作数。

答案 1 :(得分:1)

我们有一篇关于这个主题的完整研究论文:Faster Population Counts using AVX2 Instructions。尽管有这个头衔,但它也涵盖了SSE。有关相关软件库,请参阅hamming_weight。它包括各种快速功能来完成这类工作。

简短的回答:你可以像这样使用Mułapopcount函数:

 __m128i popcount(__m128i v) {
    const __m128i lookup = _mm_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
);
   __m128i low_mask = _mm_set1_epi8(0x0f);
   __m128i lo  = _mm_and_si128(v, low_mask);
   __m128i hi  = _mm_and_si128(_mm_srli_epi16(v, 4), low_mask);
   __m128i popcnt1 = _mm_shuffle_epi8(lookup, lo);
   __m128i popcnt2 = _mm_shuffle_epi8(lookup, hi);
  return _mm_sad_epu8(_mm_add_epi8(popcnt1, popcnt2), _mm_setzero_si128());
}

popcount调用的结果是一个由两个64位计数器组成的128位计数器,您必须将它们加起来。总结两个64位计数器可以在最后完成,以节省计算时间。