如何生成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 正确生成了popcntlpopcntq,但由于某种原因 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?

[Test3.3-T3] Sorting (卡常)

tableau应用实战案例(五十五)-如何在Centos中安装Tableau Server

popcnt使用硬件指令和查表法