【问题标题】:How to generate a sse4.2 popcnt machine instruction如何生成sse4.2 popcnt机器指令
【发布时间】: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 以及启用指令。

标签: gcc optimization bit-manipulation hammingweight


【解决方案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.
    猜你喜欢
    • 2016-02-16
    • 2012-04-28
    • 2017-05-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多