优化开关 - 他们真正做了什么?

use*_*572 4 c++ gcc clang visual-c++

可能每个人都使用某种优化开关(在gcc的情况下,最常见的是-O2我相信).

但是gcc(以及VS,Clang等其他编译器)在这些选项的存在下真的做了什么?

当然没有明确的答案,因为它很大程度上取决于平台,编译器版本等.但是,如果可能的话,我想收集一套"经验法则".我什么时候应该考虑加速代码的一些技巧?何时我应该把工作留给编译器?

例如,对于不同的优化级别,编译器会在这样的(一点点artifficial ......)案例中走多远:

1)sin(3.141592) //是否会在编译时进行评估,还是应该考虑一个查找表来加速计算?

2)int a = 0; a = exp(18), cos(1.57), 2; //编译器是否会评估exp和cos,尽管不需要,因为表达式的值等于2?

3)

for (size_t i = 0; i < 10; ++i) {
  int a = 10 + i;
}
Run Code Online (Sandbox Code Playgroud)

//编译器是否会跳过整个循环,因为它没有可见的副作用?

也许你可以想到其他的例子.

Mat*_* M. 6

如果您想知道编译器的作用,最好的办法是查看编译器文档.例如,对于优化,您可以查看LLVM的Analysis和Transform Passes.

1)sin(3.141592)//它将在编译时进行评估吗?

大概.IEEE浮点计算有非常精确的语义.如果在运行时更改处理器标志,这可能会令人惊讶.

2)int a = 0; a = exp(18),cos(1.57),2;

这取决于:

  • 无论是功能expcos内联或不
  • 如果它们不是,它们是否正确注释(因此编译器知道它们没有副作用)

对于从C或C++标准库中获取的函数,应正确识别/注释它们.

至于计算的消除:

  • -adce:积极的死代码消除
  • -dce:死代码消除
  • -die:死亡指令消除
  • -dse:死店消除

编译器喜欢找到无用的代码:)

3)

2)实际相似.没有使用商店的结果,表达没有副作用.

  • -loop-deletion:删除死循环

而对于决赛:什么不把编译器放到测试中?

#include <math.h>
#include <stdio.h>

int main(int argc, char* argv[]) {
  double d = sin(3.141592);
  printf("%f", d);

  int a = 0; a = (exp(18), cos(1.57), 2); /* need parentheses here */
  printf("%d", a);

  for (size_t i = 0; i < 10; ++i) {
    int a = 10 + i;
  }

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

Clang在编译期间尝试提供帮助:

12814_0.c:8:28: warning: expression result unused [-Wunused-value]
  int a = 0; a = (exp(18), cos(1.57), 2);
                           ^~~ ~~~~
12814_0.c:12:9: warning: unused variable 'a' [-Wunused-variable]
    int a = 10 + i;
        ^
Run Code Online (Sandbox Code Playgroud)

和发出的代码(LLVM IR):

@.str = private unnamed_addr constant [3 x i8] c"%f\00", align 1
@.str1 = private unnamed_addr constant [3 x i8] c"%d\00", align 1

define i32 @main(i32 %argc, i8** nocapture %argv) nounwind uwtable {
  %1 = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([3 x i8]* @.str, i64 0, i64 0), double 0x3EA5EE4B2791A46F) nounwind
  %2 = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([3 x i8]* @.str1, i64 0, i64 0), i32 2) nounwind
  ret i32 0
}
Run Code Online (Sandbox Code Playgroud)

我们注意到:

  • 正如预测的那样,sin计算已在编译时解决
  • 正如预测的那样exp,cos已被完全剥离.
  • 正如预测的那样,循环也被剥离了.

如果您想深入研究编译器优化,我建议您:

  • 学习阅读IR(这非常容易,真的,更多,所以组装)
  • 使用LLVM试用页面来测试您的假设