拥有像这样的代码:
#include "kernel.h"
int main() {
...
for (int t = 0; t < TSTEPS; ++t) {
kernel(A,B,C);
}
...
}
其中:
// kernel.h
void kernel(float *__restrict A, float *__restrict B, float *__restrict C);
// kernel.c
#include "kernel.h"
void kernel(float *__restrict A, float *__restrict B, float *__restrict C) {
// some invariant code
float tmp0 = B[42];
float tmp1 = C[42];
// some operations with tmpX, e.g.
A[0] += tmp0 * tmp1;
}
这个想法是独立地编译内核(kernel)
,因为我需要应用一组优化,而我不感兴趣主程序(main)
。此外,我不想要任何其他类型的循环或者函数内/间的优化:我只想将内核(kernel)
的编译结果完全地内联到主程序(main)
中对内核(kernel)
的调用中。我尝试了很多不同的方法(使用inline
和__attribute__((always_inline))
等提示),但唯一可以内联的方式是:
gcc -c -O3 -flto kernel.c
gcc -O1 -flto kernel.o main.c
为 kernel
生成以下汇编代码:
kernel:
.LFB0:
.cfi_startproc
endbr64
vxorps %xmm1, %xmm1, %xmm1
vcvtss2sd 168(%rsi), %xmm1, %xmm0
vcvtss2sd 168(%rdx), %xmm1, %xmm2
vcvtss2sd (%rdi), %xmm1, %xmm1
vfmadd132sd %xmm2, %xmm1, %xmm0
vcvtsd2ss %xmm0, %xmm0, %xmm0
vmovss %xmm0, (%rdi)
ret
.cfi_endproc
当kernel
调用应该在main
中时,生成的代码如下:
...
1092: f3 0f 10 0d 76 0f 00 movss 0xf76(%rip),%xmm1 # 2010 <_IO_stdin_used+0x10>
1099: 00
109a: f3 0f 10 00 movss (%rax),%xmm0
109e: b8 10 27 00 00 mov $0x2710,%eax
10a3: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
10a8: f3 0f 58 c1 addss %xmm1,%xmm0
10ac: 83 e8 01 sub $0x1,%eax
10af: 75 f7 jne 10a8 <main+0x28>
10b1: 48 8d 35 4c 0f 00 00 lea 0xf4c(%rip),%rsi # 2004 <_IO_stdin_used+0x4>
10b8: bf 01 00 00 00 mov $0x1,%edi
10bd: b8 01 00 00 00 mov $0x1,%eax
10c2: f3 0f 5a c0 cvtss2sd %xmm0,%xmm0
...
这显然很聪明,也可能是LTO的重点。但我想摆脱任何形式的优化,仅仅内联那些独立编译函数。除了手动编写之外,有没有其他“正式”的方法来实现这一点?使用
-O0
编译main
根本不会内联,甚至不用-finline-functions
也是如此。我还尝试“拒绝”由-O1
引入的所有优化标志,但我无法关闭链接时优化。这些结果是在gcc 9.3.1
和gcc 10.2.0
下得到的(对于这个测试,它们之间存在微小差异)。
编辑0: 两个细节:
- 使用类似的方法(IPO、内联标志等)使用ICC时,我获得类似的结果,即内联+优化。我还没有尝试过Clang。
- 上述代码,将
kernel
内联到主函数中,只是基本上省略了对tmp0
和tmp1
的加载,并将其乘积的结果添加到a[0]
中;我知道这很聪明,但我不想要它,我想保留原始代码形式。
cvtss2sd 168(%rdx), %xmm1
似乎不符合您的源代码。您的函数参数是double*
,但GCC正在发出float-> double的转换指令。这看起来像您将所有参数设为float *
,但在double
临时变量上进行数学计算。(然后您在源代码中进行了修复,但没有更新汇编代码。) - Peter Cordes