【发布时间】:2012-09-18 05:16:29
【问题描述】:
我正在尝试为高度优化的 x86-64 位操作代码编写一个小型库,并且正在摆弄内联 asm。
在测试这个特殊案例时引起了我的注意:
unsigned long test = 0;
unsigned long bsr;
// bit test and set 39th bit
__asm__ ("btsq\t%1, %0 " : "+rm" (test) : "rJ" (39) );
// bit scan reverse (get most significant bit id)
__asm__ ("bsrq\t%1, %0" : "=r" (bsr) : "rm" (test) );
printf("test = %lu, bsr = %d\n", test, bsr);
在 gcc 和 icc 中都可以正常编译和运行,但是当我检查程序集时会发现差异
gcc -S -fverbose-asm -std=gnu99 -O3
movq $0, -8(%rbp)
## InlineAsm Start
btsq $39, -8(%rbp)
## InlineAsm End
movq -8(%rbp), %rax
movq %rax, -16(%rbp)
## InlineAsm Start
bsrq -16(%rbp), %rdx
## InlineAsm End
movq -8(%rbp), %rsi
leaq L_.str(%rip), %rdi
xorb %al, %al
callq _printf
我想知道为什么这么复杂?我正在编写高性能代码,其中指令的数量至关重要。我特别想知道为什么 gcc 在将变量 test 传递给第二个内联 asm 之前会对其进行复制?
使用 icc 编译的相同代码会产生更好的结果:
xorl %esi, %esi # test = 0
movl $.L_2__STRING.0, %edi # has something to do with printf
orl $32832, (%rsp) # part of function initiation
xorl %eax, %eax # has something to do with printf
ldmxcsr (%rsp) # part of function initiation
btsq $39, %rsi #106.0
bsrq %rsi, %rdx #109.0
call printf #111.2
尽管 gcc 决定将我的变量保留在堆栈中而不是寄存器中,但我不明白为什么要在将 test 传递给第二个 asm 之前复制它?
如果我将test 作为第二个 asm 中的输入/输出变量放入
__asm__ ("bsrq\t%1, %0" : "=r" (bsr) , "+rm" (test) );
然后那些线消失了。
movq $0, -8(%rbp)
## InlineAsm Start
btsq $39, -8(%rbp)
## InlineAsm End
## InlineAsm Start
bsrq -8(%rbp), %rdx
## InlineAsm End
movq -8(%rbp), %rsi
leaq L_.str(%rip), %rdi
xorb %al, %al
callq _printf
这是 gcc 搞砸了优化还是我错过了一些重要的编译器开关?我的生产系统确实有 icc,但如果我决定在某个时候分发源代码,那么它也必须能够使用 gcc 进行编译。
使用的编译器:
gcc 版本 4.2.1(基于 Apple Inc. build 5658)(LLVM build 2336.1.00)
icc 版本 12.0.2
【问题讨论】:
-
为什么不对这些操作使用标准的内在函数呢?这样编译器会为您完成大部分工作(而且它可能也会做得更好)。
-
嗯,首先这些并不是我打算使用的唯一说明,它只是示例。 gcc 对它们中的大多数都有内在函数,幸运的是,icc 并没有那么幸运地拥有内在函数,而且我仅使用 icc 编译就有 20-30% 的性能提升。
-
好的 - 但除非有一些特定的东西不能通过内在函数完成,否则我仍然相信如果你允许编译器会比你做得更好。
-
除了我不是在寻找编译器内在函数(我知道在哪里可以找到它们)这一事实之外,它们会产生比我使用的指令多得多的指令,并且在不同的编译器中会产生不同的结果。
__builtin_clzll(0)在使用 gcc 编译时为 64,在 icc 中为 63。我在这个主题中的问题是如何优化内联 asm 的嵌入。 -
您能否提供一个完整的源示例,使 gcc 发出您显示的汇编代码?我已经尝试将您的 sn-p 嵌入到
main()并使用从 3.2.3 到 4.7.2 的各种 gcc 版本对其进行编译,并且没有复制将test放入堆栈中-O3opt 的代码等级。请提供更多背景信息。
标签: optimization gcc x86-64 inline-assembly icc