【发布时间】:2021-06-10 09:38:05
【问题描述】:
有这样的代码:
#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 的编译结果完全内联到 @ 中对 kernel 的调用中987654327@。我尝试了很多不同的东西(用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:
另外两个细节:
- 使用 ICC 使用类似的方法(IPO、内联标志等),我获得了类似的结果,即内联 + 优化。我还没有尝试过 Clang。
- 上面的代码,将
kernel内联到main上,只是基本消除了tmp0和tmp1的负载,只是将其相乘的结果与a[0]相加;我知道这很聪明,但我不想要它,我想保留原始代码形式。
【问题讨论】:
-
你想在这里解决什么真正的问题?基准测试?通常没有人想要没有针对调用站点/参数优化的更糟糕的 asm,并且没有办法让 GCC 做你所要求的。所以这似乎是一个 XY 问题,那么你真正想要什么?
-
另外,
cvtss2sd 168(%rdx), %xmm1似乎与您的来源不匹配;您的函数 args 是double*但 GCC 正在发出 float->double 转换指令。看起来您的所有参数都是float*,但在double临时变量上进行数学运算。 (然后您在源代码中修复了该问题,但没有更新 asm。) -
@PeterCordes 是的,实际上,我基本上是在尝试对一些代码进行基准测试。不要误会我的意思,我知道没有人想要更差的性能,我只是想问是否有办法控制如何应用优化。是的,我忘了更新 C 代码,谢谢。
标签: assembly gcc compiler-optimization microbenchmark inlining