标签:arc 支持 性能 处理 lin 循环 代码 blog 密码
popcnt是“population count”的缩写,该操作一般翻译为“位1计数”,即统计有多少个“为1的位”。例如,十六进制数“FF”,它有8个为1的位,即“popcnt(0xFF) = 8”。popcnt主要应用在密码学与通信安全,例如计算汉明重量(Hamming weight)。
x86体系最初是没有硬件popcnt指令的,只能靠软件计算。2008年底,Intel发布了Nehalem架构的处理器,增加了SSE4.2指令集,其中就有硬件popcnt指令。虽然它名义上是属于SSE4.2指令集,但它并不使用XMM寄存器(SSE的128位寄存器),而是使用GPR寄存器(General-Purpose Registers,通用寄存器)。甚至它的CPUID标志位也不是SSE4.2(CPUID.01H:ECX.SSE4_2[bit 20]),而是专门的POPCNT标志位(CPUID.01H:ECX.POPCNT[bit 23])。
参考[C++] 测试硬件popcnt(位1计数)指令与各种软件算法,利用模板实现静态多态优化性能和popcount 算法分析这两篇文章
对比 popcount 的各种算法,高效在于能利用并行计算,去掉循环,使用减法和模运算。
通过减1的循环算法(parse/dense)在知道数只有三五位为1(或0)的话,其实效率也不赖。
查表法的效率挺不错的,如果没有硬件指令的支持,选用这个是可以的。
Hacker‘s Delight 中的算法,在开源项目中广为引用。
总的来说,硬件指令最快,查表其次,然后是Hacker‘s Delight里的hacker_popcnt实现
gcc的5.50.6 X86 Built-in Functions中提到
The following built-in functions are changed to generate new SSE4.2 instructions when -msse4.2 is used.
int __builtin_popcount (unsigned int)
Generates the popcntl machine instruction.
int __builtin_popcountl (unsigned long)
Generates the popcntl or popcntq machine instruction, depending on the size of unsigned long.
int __builtin_popcountll (unsigned long long)
Generates the popcntq machine instruction.
但事实上在编译时加入-mpopcnt
(同时会定义 __POPCNT__宏)即可让这些函数生成对应的硬件指令。另外也可以在c文件中添加一行#pragma GCC target ("popcnt")
来使其生成硬件指令。当没有生成硬件指令的时候,其会调用gcc用软件实现的popcnt函数(该函数是采用了Hacker‘s Delight这本书里的hacker_popcnt算法)
也就是说gcc的内置popcnt函数对应着硬件指令实现和hacker_popcnt实现。而我想优先使用硬件指令实现,其次是查表法实现。所以就有了如下代码
static inline uint32_t popcnt32(uint32_t v)
{
#ifdef __POPCNT__
return __builtin_popcount(v);
#else
static const uint32_t countTable[256] = {
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8};
return (countTable[(uint8_t)v] + countTable[(uint8_t)(v >> 8)]) + (countTable[(uint8_t)(v >> 16)] + countTable[(uint8_t)(v >> 24)]);
#endif
}
static inline uint32_t popcnt64(uint64_t v)
{
#ifdef __POPCNT__
return __builtin_popcountll(v);
#else
return popcnt32((uint32_t)v) + popcnt32((uint32_t)(v >> 32));
#endif
}
static inline uint32_t popcnt_array(uint32_t *x, uint32_t len)
{
uint32_t cnt0 = 0, cnt1 = 0, seg = len >> 2, res = len & 0x3;
uint64_t *X = (uint64_t *)x;
seg <<= 1;
for (uint32_t i = 0; i < seg; i += 2)
{
cnt0 += popcnt64(X[i]);
cnt1 += popcnt64(X[i + 1]);
}
for (uint32_t i = 1; i <= res; i++)
{
cnt0 += popcnt32(x[len - i]);
}
return cnt0 + cnt1;
}
标签:arc 支持 性能 处理 lin 循环 代码 blog 密码
原文地址:https://www.cnblogs.com/Tifa-Best/p/13819042.html