如何生成sse4.2 popcnt机器指令
Posted
技术标签:
【中文标题】如何生成sse4.2 popcnt机器指令【英文标题】:How to generate a sse4.2 popcnt machine instruction 【发布时间】:2011-06-21 15:02:43 【问题描述】:使用c程序:
int main(int argc , char** argv)
return __builtin_popcountll(0xf0f0f0f0f0f0f0f0);
和编译器行(gcc 4.4 - Intel Xeon L3426):
gcc -msse4.2 poptest.c -o poptest
我没有得到内置的 popcnt 指令,而是编译器生成一个查找表并以这种方式计算 popcount。生成的二进制文件超过 8000 字节。 (哇!)
非常感谢您的帮助。
【问题讨论】:
gcc 因为至少 4.4.7(godbolt 上最旧)启用了-mpopcnt
作为 -msse4.2
的一部分,即使它们具有单独的 CPUID 功能位。 godbolt.org/g/SfcHYh。另外,如果您__builtin_popcountll(argc)
,当您启用优化时,您的程序将不会优化到return 32
。或者只是查看带有 int arg 的函数的 asm,因为您只想查看 asm,而不是运行它。但是,如果您要在本地运行二进制文件,-march=native
是迄今为止最好的选择,因为它设置了-mtune
以及启用指令。
【参考方案1】:
你必须告诉 GCC 为支持的架构生成代码 popcnt指令:
gcc -march=corei7 popcnt.c
或者只是启用对 popcnt 的支持:
gcc -mpopcnt popcnt.c
在您的示例程序中,__builtin_popcountll
的参数是
常量,因此编译器可能会在编译时进行计算
时间并且从不发出 popcnt 指令。即使没有,GCC也会这样做
要求优化程序。
所以尝试传递一些它在编译时不知道的东西:
int main (int argc, char** argv)
return __builtin_popcountll ((long long) argv);
$ gcc -march=corei7 -O popcnt.c && objdump -d a.out | grep '<main>' -A 2
0000000000400454 <main>:
400454: f3 48 0f b8 c6 popcnt %rsi,%rax
400459: c3 retq
【讨论】:
【参考方案2】:你需要这样做:
#include <stdio.h>
#include <smmintrin.h>
int main(void)
int pop = _mm_popcnt_u64(0xf0f0f0f0f0f0f0f0ULL);
printf("pop = %d\n", pop);
return 0;
$ gcc -Wall -m64 -msse4.2 popcnt.c -o popcnt
$ ./popcnt
pop = 32
$
编辑
糟糕——我刚刚用 gcc 4.2 和 ICC 11.1 检查了反汇编输出——虽然 ICC 11.1 正确生成了popcntl
或popcntq
,但由于某种原因 gcc 没有——它改为调用___popcountdi2
。奇怪的。当我有机会时,我会尝试更新版本的 gcc,看看它是否已修复。我想唯一的解决方法是使用 ICC 而不是 gcc。
【讨论】:
非常感谢 Paul 对此进行调查。您使用 (gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3) 的代码仍然会生成一个大型查找表。我将尝试安装icc。很好的提示! 我刚刚从 MacPorts 尝试了 gcc 4.4.6,这似乎生成了popcnt
指令,所以看起来这可能已在 4.4.3 和 4.4.6 之间修复。
所以我的一个朋友插话说我需要使用 objdump 而不是 x86dis 来查找 popcnt 指令。使用您的程序和 objdump 时,我看到: 400533: f3 0f b8 45 f8 popcnt -0x8(%rbp),%eax 所以我认为我现在一切都很好。再次非常感谢。
OK - 我正在使用 gcc -S 生成 asm 源代码并查看它。顺便说一句,如果您的应用程序对性能至关重要,您可能仍想考虑 ICC。祝你好运!【参考方案3】:
对于 GCC 中的 __builtin_popcountll
,您只需添加 -mpopcnt
#include <stdlib.h>
int main(int argc, char **argv)
return __builtin_popcountll(atoi(argv[1]));
与-mpopcnt
$ otool -tvV a.out
a.out:
(__TEXT,__text) section
_main:
0000000100000f66 pushq %rbp
0000000100000f67 movq %rsp, %rbp
0000000100000f6a subq $0x10, %rsp
0000000100000f6e movq %rdi, -0x8(%rbp)
0000000100000f72 movq -0x8(%rbp), %rax
0000000100000f76 addq $0x8, %rax
0000000100000f7a movq (%rax), %rax
0000000100000f7d movq %rax, %rdi
0000000100000f80 callq 0x100000f8e ## symbol stub for: _atoi
0000000100000f85 cltq
0000000100000f87 popcntq %rax, %rax
0000000100000f8c leave
0000000100000f8d retq
没有-mpopcnt
a.out:
(__TEXT,__text) section
_main:
0000000100000f55 pushq %rbp
0000000100000f56 movq %rsp, %rbp
0000000100000f59 subq $0x10, %rsp
0000000100000f5d movq %rdi, -0x8(%rbp)
0000000100000f61 movq -0x8(%rbp), %rax
0000000100000f65 addq $0x8, %rax
0000000100000f69 movq (%rax), %rax
0000000100000f6c movq %rax, %rdi
0000000100000f6f callq 0x100000f86 ## symbol stub for: _atoi
0000000100000f74 cltq
0000000100000f76 movq %rax, %rdi
0000000100000f79 callq 0x100000f80 ## symbol stub for: ___popcountdi2
0000000100000f7e leave
0000000100000f7f retq
注意事项
在使用 POPCNTQ 之前,请务必检查 CPUID 功能位的 ABM 位(位 23)
【讨论】:
你可以只显示一个函数的反汇编,该函数采用int
arg;它会比这更短更清晰。另外,我想你忘了启用优化,因为 -O2 启用了-fomit-frame-pointer
。
天啊。 Perfect is the enemy of the shipped.
以上是关于如何生成sse4.2 popcnt机器指令的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 SSE4.2 和 AVX 指令编译 Tensorflow?
如何使用 SSE4.2 和 AVX 指令编译 Tensorflow?
如何使用 SSE4.2 和 AVX 指令编译 Tensorflow?