计数位数最快的方法
可能重复:
如何计算一个32位整数中的设置位数?
给一个无符号的char类型值,计算其中的总位数。最快的方法是什么? 我写了如下三个函数,最好的方法是什么,可以有人提出一个更快的方法?(我只想要非常快的方法)
const int tbl[] =
{
#define B2(n) n, n+1, n+1, n+2
#define B4(n) B2(n), B2(n+1), B2(n+1), B2(n+2)
#define B6(n) B4(n), B4(n+1), B4(n+1), B4(n+2)
B6(0), B6(1), B6(1), B6(2)
};
char naivecount (unsigned char val)
{
char cnt = 0;
while (val)
{
cnt += (val & 1);
val = val >> 1;
}
return cnt;
}
inline tableLookUp(int val)
{
assert(val >= 0 && val <= 255);
return tbl[val];
}
int asmCount(int val)
{
int res = 0;
asm volatile("xor %0, %0nt"
"begin:nt"
"cmp $0x0, %1nt"
"jle endnt"
"movl %1, %%ecxnt"
"and $0x1, %%ecxnt"
"addl %%ecx, %0nt"
"shrl %1nt"
"jmp beginnt"
"end:"
: "=r"(res)
: "r" (val));
return res;
}
编辑:
我已经测试了所有的方法,最快的一个是使用popcntl
指令。在popcntl
指令的平台上,我将使用表查找。
如果你想手工编码,试试这个:
#include <stdint.h>
int popcnt8(uint8_t x) {
x = (x & 0x55) + (x >> 1 & 0x55);
x = (x & 0x33) + (x >> 2 & 0x33);
x = (x & 0x0f) + (x >> 4 & 0x0f);
return x;
}
在x86上,这编译为(AT&T语法):
popcnt8:
movl %edi, %eax
shrb %dil
andl $85, %eax
andl $85, %edi
addl %eax, %edi
movl %edi, %eax
shrb $2, %dil
andl $51, %eax
andl $51, %edi
addl %eax, %edi
movl %edi, %eax
shrb $4, %dil
andl $15, %eax
addl %edi, %eax
movzbl %al, %eax
ret
将此与gcc用内在函数生成的内容进行比较:
#include <stdint.h>
int popcnt8_intrin(uint8_t x) { return __builtin_popcount(x); }
在具有SSE 4.2的x86上:
popcnt8_intrin:
movzbl %dil, %eax
popcntl %eax, %eax
ret
这不是最佳的; 铛产生:
popcnt8_intrin:
popcntl %edi,%eax
ret
将计算减少为一个(!)指令。
在没有SSE 4.2的x86上:
popcnt8_intrin:
subq $8, %rsp
movzbl %dil, %edi
call __popcountdi2
addq $8, %rsp
ret
gcc实质上是在这里调用它的库。 不太理想。 铛更好一点:
popcnt8_intrin: # @popcnt8_intrin
movl %edi, %eax
shrl %eax
andl $85, %eax
subl %eax, %edi
movl %edi, %eax
andl $858993459, %eax # imm = 0x33333333
shrl $2, %edi
andl $858993459, %edi # imm = 0x33333333
addl %eax, %edi
movl %edi, %eax
shrl $4, %eax
addl %edi, %eax
andl $252645135, %eax # imm = 0xF0F0F0F
imull $16843009, %eax, %eax # imm = 0x1010101
shrl $24, %eax
ret
clang计算整个32位数的popcnt。 这不是最佳的imho。
如果你没有做过如此多的比较和分支,那么你的汇编代码会更快。
但显然,最快的方法是进行字节查找,特别是因为您只处理256个值(可以使用朴素方法编写值列表,然后只需要一个static const table[256] = { ... }; return table[value];
在你的函数中。
基准不同的解决方案。
如果你的汇编代码比编译器生成的代码慢,我不会感到惊讶!
编辑:你的汇编代码会稍微快一点做:
int asmCount(int val)
{
int res = 0;
asm volatile("begin:nt"
"movl %1, %%ecxnt"
"and $0x1, %%ecxnt"
"addl %%ecx, %0nt"
"shrl %1nt"
"jnz beginnt"
"end:"
: "=r"(res)
: "r" (val)
: "ecx"); // Important: clobbers ecx!
return res;
}
我删除了异或(res = 0应该这样做),并比较(当然,如果val为零,我们执行一些额外的指令,但对于设置了高位的任何东西,情况会更糟糕,因为这是两个额外的指令每次迭代,意味着可能有16个额外的指令 - 其中一个是分支!),并在循环结束时将跳转改为jnz。 这可能大概是编译器在第一种情况下生成的。 试图在简单代码上击败编译器并不那么容易!
链接地址: http://www.djcxy.com/p/72595.html上一篇: Fastest way to count bits
下一篇: How to count the number of ones in a byte without a loop in c?