【问题标题】:C++ SIMD: accumulate POPCOUNTs of uint64_t elements in an arrayC++ SIMD:在数组中累积 uint64_t 元素的 POPCOUNT
【发布时间】:2017-02-26 02:09:30
【问题描述】:

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

#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    
}

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

【问题讨论】:

标签: c++ sse simd


【解决方案1】:

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

【讨论】:

  • 谢谢,你知道我应该包含什么头文件以便我可以打电话给_mm_popcnt_u64吗?
  • @Cauchy #include "nmmintrin.h"。完整引用Intel intrinsics
  • 我收到一个错误/usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/smmintrin.h: In function ‘__m128i _mm_mullo_epi32(__m128i, __m128i)’: /usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/smmintrin.h:328:22: error: ‘__v4su’ was not declared in this scope return (__m128i) ((__v4su)__X * (__v4su)__Y); ^ /usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/smmintrin.h:328:29: error: expected ‘)’ before ‘__X’ return (__m128i) ((__v4su)__X * (__v4su)__Y);
  • 我究竟如何才能访问这两个部分?
  • @Cauchy 我不知道你的编译错误,因为我的 mm 头文件不使用 __v4su,但要访问这两个部分,你可以查看 &lt;emmintrin.h&gt;__m128i 的定义.这是一个联合,使用 m128i_i64 访问两个 64 位半部分。
【解决方案2】:

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

简短的回答:您可以像这样使用 Muła popcount 函数:

 __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 调用的结果是一个 128 位计数器,由两个 64 位计数器组成,您必须将它们相加。可以在最后对两个 64 位计数器求和,以节省计算时间。

【讨论】:

    猜你喜欢
    • 2015-11-27
    • 2019-07-30
    • 1970-01-01
    • 2021-11-27
    • 2022-11-02
    • 1970-01-01
    • 1970-01-01
    • 2020-02-13
    • 1970-01-01
    相关资源
    最近更新 更多