我正在尝试使用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
变量中。
答案 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位计数器可以在最后完成,以节省计算时间。