c - 我如何理解-O3 的优化?

标签 c gcc optimization

<分区>

我目前有两个函数 AB

在没有任何标志的情况下编译时,AB 快。

但是当使用-O1-O3 编译时,BA 快得多。

我想将该功能移植到其他语言,所以看来 A 是更好的选择。

但如果我能理解 -O3 是如何加速函数 B 的,那就太好了。是否有任何好的方法至少可以稍微了解 -O3 所做的优化类型?

最佳答案

-O3-O2 的作用相同,而且:

  • 函数的内联部分。
  • 执行函数克隆以增强过程间常量传播。
  • 在 Graphite 外部执行循环交换。这可以提高循环嵌套的缓存性能,并允许进行进一步的循环优化,如矢量化。例如,循环:
for (int i = 0; i < N; i++)
  for (int j = 0; j < N; j++)
    for (int k = 0; k < N; k++)
      c[i][j] = c[i][j] + a[i][k]*b[k][j];

转化为

for (int i = 0; i < N; i++)
  for (int k = 0; k < N; k++)
    for (int j = 0; j < N; j++)
      c[i][j] = c[i][j] + a[i][k]*b[k][j];
  • 对可行循环应用展开和堵塞转换。在循环嵌套中,这会按某个因子展开外循环并融合生成的多个内循环。
  • 剥离有足够信息的循环,它们不会滚动太多。它还会打开完整的循环剥离(即完全移除具有少量恒定迭代次数的循环)。
  • 执行预测性共享优化,即重复使用在之前的循环迭代中执行的计算(尤其是内存加载和存储)。
  • 分割通向循环后缘的路径。这可以改进死代码消除和公共(public)子表达式消除。
  • 提高大循环体的缓存性能,并允许进行进一步的循环优化,例如并行化或矢量化。
  • 将具有循环不变条件的分支移出循环,并在两个分支上重复循环(根据条件的结果进行修改)。
  • 如果循环遍历具有可变步幅的数组,请创建另一个版本的循环,假定步幅始终为一个。例如:
for (int i = 0; i < n; ++i)
  x[i * stride] = …;

变成:

if (stride == 1)
  for (int i = 0; i < n; ++i)
    x[i] = …;
else
  for (int i = 0; i < n; ++i)
    x[i * stride] = …;

例如下面的代码:

unsigned long apply(unsigned long (*f)(unsigned long, unsigned long), unsigned long a, unsigned long b, unsigned long c) {
    for (unsigned long i = 0; i < b; i++)
        c = f(c, a);
    return c;
}

unsigned long inc(unsigned long a, unsigned long b) { return a + 1; }
unsigned long add(unsigned long a, unsigned long b) { return apply(inc, 0, b, a); }

优化添加功能:

英特尔语法

add:
  lea rax, [rsi+rdi]
  ret

美国电话电报公司:

add:
  leaq (%rsi,%rdi), %rax
  ret

没有 -O3 输出是:

英特尔语法

add:
  push rbp
  mov rbp, rsp
  sub rsp, 16
  mov QWORD PTR [rbp-8], rdi
  mov QWORD PTR [rbp-16], rsi
  mov rdx, QWORD PTR [rbp-8]
  mov rax, QWORD PTR [rbp-16]
  mov rcx, rdx
  mov rdx, rax
  mov esi, 0
  mov edi, OFFSET FLAT:inc
  call apply
  leave
  ret

美国电话电报公司:

add:
  pushq %rbp
  movq %rsp, %rbp
  subq $16, %rsp
  movq %rdi, -8(%rbp)
  movq %rsi, -16(%rbp)
  movq -8(%rbp), %rdx
  movq -16(%rbp), %rax
  movq %rdx, %rcx
  movq %rax, %rdx
  movl $0, %esi
  movl $inc, %edi
  call apply
  leave
  ret

您可以使用 -S 标志和 -masm=intel 比较函数 AB 的输出汇编器>.

此答案基于 GCC documentation ,您可以从中学到更多。

关于c - 我如何理解-O3 的优化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57536701/

相关文章:

c++ - 编译器警告转换

c++ - Cgal二次规划目标函数

c - GTK in C - 如何控制 TreeView 中的列宽?

c - 指定线程访问数组中的哪些位置

gcc - ARM GCC 是否有汇编 'REV' 指令的内置函数?

c - `vmovaps' 的段错误

c++ - 如何使用指针从其他函数访问局部变量?

c - C中的单向管道

mysql - mysql中自定义函数在上面代码里面添加处理时间

c++ - C++中的缓存(或多或少适合初学者)