goto对C++编译器优化的影响

uni*_*n83 20 c++ optimization goto compiler-optimization

使用goto现代C++编译器有哪些性能优势或惩罚?

我正在编写一个C++代码生成器,使用goto它会使编写更容易.没有人会触及生成的C++文件,所以不要让我全部"goto is bad".作为一个好处,他们可以节省临时变量的使用.

从纯粹的编译器优化角度来看,我想知道goto对编译器优化器的结果是什么?与使用临时/标志相比,它是否使代码更快,更慢或通常没有性能变化.

Jer*_*fin 22

受影响的编译器部分使用流程图.用于创建特定流图的语法通常是无关紧要的 - 如果while使用goto而不是实际while语句创建类似循环的东西,它根本不会影响优化器(到那时,产生的语法)流程图将早已消失).

但是,有可能生成一个流程图,其中gotos不能由任何正常的流程控制语句(循环,开关等)产生.在这种情况下,您可能会生成不可缩减的流程图,以及何时/如果你这样做,往往会限制编译器优化代码的能力.

换句话说,如果(举例来说)你把代码与正常写for,while,switch等,并转换为使用goto在任何情况下,但保留了相同的结构,几乎任何合理的现代编译器可能会产生基本相同的代码,通过办法.但是,如果你使用gotos来生成像几十年前我必须看到的FORTRAN的大部分意大利面,那么编译器可能无法用它做很多事情.

  • Turing完整性是否确保无论使用`goto`创建的任何混乱,我们可以创建一个没有它的等价物? (2认同)
  • @ unixman83:也许一个例子可以帮助它更有意义.考虑循环提升 - 如果每次迭代都有相同的效果,则将计算移出循环.要做到这一点,编译器必须能够找出什么是"循环"的边界,并且必须确保在不执行它所放置的代码的情况下不会跳到循环的中间.在它之前.没有它,它就无法将代码提升出循环. (2认同)

Dav*_*men 6

从纯粹的编译器优化预测中,我想知道goto对编译器优化器的结果是什么?与使用临时/标志相比,它是否使代码更快,更慢或通常没有性能变化.

你为什么在乎?您主要关注的是让代码生成器创建正确的代码.效率远不如正确性重要.你的问题应该是"我使用gotos会使我生成的代码更可能或更不可能正确吗?"

查看lex/yacc或flex/bison生成的代码.那段代码充满了getos.这是有充分理由的.lex和yacc实现有限状态机.由于机器在状态转换时进入另一个状态,因此goto可以说是这种转换的最自然的工具.

通过whileswitch语句周围使用循环,有一种简单的方法可以在许多情况下消除这些.这是结构化代码.Per Douglas Jones(Jones DW,How(not)编码有限状态机,SIGPLAN Not.23,8(1988年8月),19-22.),这是编码FSM的最差方法.他认为基于goto的方案更好.

他还认为,有一种更好的方法,即使用图论技术将FSM转换为控制流程图.这并不容易.这是NP难题.这就是为什么你仍然会看到很多FSM,特别是自动生成的FSM,实现为围绕交换机的循环或通过gotos实现的状态转换.

  • 这当然是首要关注的问题.然而,避免"goto"的决定也可以被视为过早的悲观化.无论你是否使用它们都是一种决定,因为所有的决定都应该考虑手边的证据.避免它们的传统决定是基于这里不适用的证据(可维护性),因此需要其他原因. (2认同)

Mat*_* M. 5

您如何认为在装配级别表示循环?

使用跳转指令标签 ......

许多编译器实际上甚至会在他们的中级表示中使用跳转:

int loop(int* i) {
  int result = 0;
  while(*i) {
    result += *i;
  }
  return result;
}

int jump(int* i) {
  int result = 0;
  while (true) {
    if (not *i) { goto end; }
    result += *i;
  }

end:
  return result;
}
Run Code Online (Sandbox Code Playgroud)

LLVM中的收益率:

define i32 @_Z4loopPi(i32* nocapture %i) nounwind uwtable readonly {
  %1 = load i32* %i, align 4, !tbaa !0
  %2 = icmp eq i32 %1, 0
  br i1 %2, label %3, label %.lr.ph..lr.ph.split_crit_edge

.lr.ph..lr.ph.split_crit_edge:                    ; preds = %.lr.ph..lr.ph.split_crit_edge, %0
  br label %.lr.ph..lr.ph.split_crit_edge

; <label>:3                                       ; preds = %0
  ret i32 0
}

define i32 @_Z4jumpPi(i32* nocapture %i) nounwind uwtable readonly {
  %1 = load i32* %i, align 4, !tbaa !0
  %2 = icmp eq i32 %1, 0
  br i1 %2, label %3, label %.lr.ph..lr.ph.split_crit_edge

.lr.ph..lr.ph.split_crit_edge:                    ; preds = %.lr.ph..lr.ph.split_crit_edge, %0
  br label %.lr.ph..lr.ph.split_crit_edge

; <label>:3                                       ; preds = %0
  ret i32 0
}
Run Code Online (Sandbox Code Playgroud)

哪里br分支指令(条件跳转).

所有优化都在此结构上执行.那么,goto是优化者的面包和黄油.

  • 我想问题是使用goto是否会阻止编译器推断某些优化. (4认同)
  • @Voo:它适用于达夫的设备吗?尽管没有字面上的“goto”,但使用“switch”在循环中间跳转是相当“非结构化”的。关键是,我相信 C 和 C++ 足够复杂,对于为“goto”实现生成的任何控制流图,可以找到不带“goto”的等效实现来生成相同的控制流图。 (2认同)