最快最快的是使用 CPU popcnt 指令,紧随其后的是 SSSE3 代码。最快的便携是bitslice方法,其次是查找表:http ://www.dalkescientific.com/writings/diary/archive/2011/11/02/faster_popcount_update.html
与所有事情一样,您应该对负载进行基准测试。如果太慢,则进行优化。
对于 AMD Phenom II X2 550,使用 gcc 4.7.1(使用g++ -O3 popcnt.cpp -o popcnt -mpopcnt -msse2
):
Bitslice(7) 1462142 我们;cnt = 32500610
Bitslice(24) 1221985 我们;cnt = 32500610
劳拉杜 2347749 我们;cnt = 32500610
SSE2 8 位 790898 us;cnt = 32500610
SSE2 16 位 825568 我们;cnt = 32500610
SSE2 32 位 864665 我们;cnt = 32500610
16 位 LUT 1236739 我们;cnt = 32500610
8 位 LUT 1951629 我们;cnt = 32500610
gcc popcount 803173 我们;cnt = 32500610
gcc popcountll 7678479 我们;cnt = 32500610
FreeBSD 版本 1 2802681 我们;cnt = 32500610
FreeBSD 版本 2 2167031 我们;cnt = 32500610
维基百科#2 4927947 我们;cnt = 32500610
维基百科#3 4212143 我们;cnt = 32500610
HAKMEM 169/X11 3559245 我们;cnt = 32500610
天真 16182699 我们;cnt = 32500610
Wegner/Kernigan 12115119 我们;cnt = 32500610
安德森 61045764 我们;cnt = 32500610
8x 移位并添加 6712049 us;cnt = 32500610
32x班次加6662200 us;cnt = 32500610
对于带有 gcc 4.7.1 的 Intel Core2 Duo E8400(g++ -O3 popcnt.cpp -o popcnt -mssse3
此-mpopcnt
CPU 不支持)
Bitslice(7) 1353007 我们;cnt = 32500610
Bitslice(24) 953044 我们;cnt = 32500610
劳拉杜 534697 我们;cnt = 32500610
SSE2 8 位 458277 我们;cnt = 32500610
SSE2 16 位 555278 我们;cnt = 32500610
SSE2 32 位 634897 我们;cnt = 32500610
SSSE3 414542 我们;cnt = 32500610
16 位 LUT 1208412 我们;cnt = 32500610
8 位 LUT 1400175 我们;cnt = 32500610
gcc popcount 5428396 我们;cnt = 32500610
gcc popcountll 2743358 我们;cnt = 32500610
FreeBSD 版本 1 3025944 我们;cnt = 32500610
FreeBSD 版本 2 2313264 我们;cnt = 32500610
维基百科#2 1570519 我们;cnt = 32500610
维基百科#3 1051828 我们;cnt = 32500610
HAKMEM 169/X11 3982779 我们;cnt = 32500610
天真 20951420 我们;cnt = 32500610
Wegner/Kernigan 13665630 我们;cnt = 32500610
安德森 6771549 我们;cnt = 32500610
8x 移位加 14917323 us;cnt = 32500610
32x 移位加 14494482 us;cnt = 32500610
Bitslice 方法是一种并行机制,一次计算多个(7 或 24 个)机器字,因此它对通用函数具有边际可用性。在http://dalkescientific.com/writings/diary/popcnt.cpp之后:
static inline int popcount_fbsd2(unsigned *buf, int n)
{
int cnt=0;
do {
unsigned v = *buf++;
v -= ((v >> 1) & 0x55555555);
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
v = (v + (v >> 4)) & 0x0F0F0F0F;
v = (v * 0x01010101) >> 24;
cnt += v;
} while(--n);
return cnt;
}
static inline int merging2(const unsigned *data)
{
unsigned count1,count2,half1,half2;
count1=data[0];
count2=data[1];
half1=data[2]&0x55555555;
half2=(data[2]>>1)&0x55555555;
count1 = count1 - ((count1 >> 1) & 0x55555555);
count2 = count2 - ((count2 >> 1) & 0x55555555);
count1+=half1;
count2+=half2;
count1 = (count1 & 0x33333333) + ((count1 >> 2) & 0x33333333);
count2 = (count2 & 0x33333333) + ((count2 >> 2) & 0x33333333);
count1+=count2;
count1 = (count1&0x0F0F0F0F)+ ((count1 >> 4) & 0x0F0F0F0F);
count1 = count1 + (count1 >> 8);
count1 = count1 + (count1 >> 16);
return count1 & 0x000000FF;
}
static inline int merging3(const unsigned *data)
{
unsigned count1,count2,half1,half2,acc=0;
int i;
for(i=0;i<24;i+=3)
{
count1=data[i];
count2=data[i+1];
//w = data[i+2];
half1=data[i+2]&0x55555555;
half2=(data[i+2]>>1)&0x55555555;
count1 = count1 - ((count1 >> 1) & 0x55555555);
count2 = count2 - ((count2 >> 1) & 0x55555555);
count1+=half1;
count2+=half2;
count1 = (count1 & 0x33333333) + ((count1 >> 2) & 0x33333333);
count1 += (count2 & 0x33333333) + ((count2 >> 2) & 0x33333333);
acc += (count1 & 0x0F0F0F0F)+ ((count1>>4) &0x0F0F0F0F);
}
acc = (acc & 0x00FF00FF)+ ((acc>>8)&0x00FF00FF);
acc = acc + (acc >> 16);
return acc & 0x00000FFFF;
}
/* count 24 words at a time, then 3 at a time, then 1 at a time */
static inline int popcount_24words(unsigned *buf, int n) {
int cnt=0, i;
for (i=0; i<n-n%24; i+=24) {
cnt += merging3(buf+i);
}
for (;i<n-n%3; i+=3) {
cnt += merging2(buf+i);
}
cnt += popcount_fbsd2(buf+i, n-i);
return cnt;
}