Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

effect of goto on C++ compiler optimization

What are the performance benefits or penalties of using goto with a modern C++ compiler?

I am writing a C++ code generator and use of goto will make it easier to write. No one will touch the resulting C++ files so don't get all "goto is bad" on me. As a benefit, they save the use of temporary variables.

I was wondering, from a purely compiler optimization perspective, the result that goto has on the compiler's optimizer? Does it make code faster, slower, or generally no change in performance compared to using temporaries / flags.

like image 225
unixman83 Avatar asked Apr 30 '12 15:04

unixman83


People also ask

Why should we avoid goto statement in C?

NOTE − Use of goto statement is highly discouraged in any programming language because it makes difficult to trace the control flow of a program, making the program hard to understand and hard to modify. Any program that uses a goto can be rewritten to avoid them.

What is the biggest problem with goto statement?

"The GOTO statement is generally considered to be a poor programming practice that leads to unwieldy programs. Its use should be avoided."

Is goto faster than function call?

Yes, the machine code generated from goto will be a straight JUMP. And this will probably be faster than a function call, because nothing has to be done to the stack (although a call without any variables to pass will be optimized in such a way that it might be just as fast).

What does goto in C do?

The goto statement allows us to transfer control of the program to the specified label .


3 Answers

The part of a compiler that would be affected works with a flow graph. The syntax you use to create a particular flow graph will normally be irrelevant as long as you're writing strictly portable code--if you create something like a while loop using a goto instead of an actual while statement, it's not going to produce the same flow graph as if you used the syntax for a while loop. Using non-portable code, however, modern compilers allow you to add annotations to loops to predict whether they'll be taken or not. Depending on the compiler, you may or may not be able to duplicate that extra information using a goto (but most that have annotation for loops also have annotation for if statements, so a likely taken or likely not taken on the if controlling a goto would generally have the same effect as a similar annotation on the corresponding loop).

It is possible, however, to produce a flow graph with gotos that couldn't be produced by any normal flow control statements (loops, switch, etc.), such conditionally jumping directly into the middle of a loop, depending on the value in a global. In such a case, you may produce an irreducible flow graph, and when/if you do, that will often limit the ability of the compiler to optimize the code.

In other words, if (for example) you took code that was written with normal for, while, switch, etc., and converted it to use goto in every case, but retained the same structure, almost any reasonably modern compiler could probably produce essentially identical code either way. If, however, you use gotos to produce the mess of spaghetti like some of the FORTRAN I had to look at decades ago, then the compiler probably won't be able to do much with it.

like image 199
Jerry Coffin Avatar answered Oct 21 '22 08:10

Jerry Coffin


How do you think that loops are represented, at the assembly level ?

Using jump instructions to labels...

Many compilers will actually use jumps even in their Intermediate Representation:

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;
}

Yields in 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
}

Where br is the branch instruction (a conditional jump).

All optimizations are performed on this structure. So, goto is the bread and butter of optimizers.

like image 31
Matthieu M. Avatar answered Oct 21 '22 10:10

Matthieu M.


I was wondering, from a purely compiler optimzation prespective, the result that goto's have on the compiler's optimizer? Does it make code faster, slower, or generally no change in performance compared to using temporaries / flags.

Why do you care? Your primary concern should be getting your code generator to create the correct code. Efficiency is of much less importance than correctness. Your question should be "Will my use of gotos make my generated code more likely or less likely to be correct?"

Look at the code generated by lex/yacc or flex/bison. That code is chock full of gotos. There's a good reason for that. lex and yacc implement finite state machines. Since the machine goes to another state at state transitions, the goto is arguably the most natural tool for such transitions.

There is a simple way to eliminate those gotos in many cases by using a while loop around a switch statement. This is structured code. Per Douglas Jones (Jones D. W., How (not) to code a finite state machine, SIGPLAN Not. 23, 8 (Aug. 1988), 19-22.), this is the worst way to encode a FSM. He argues that a goto-based scheme is better.

He also argues that there is an even better approach, which is convert your FSM to a control flow diagram using graph theory techniques. That's not always easy. It is an NP hard problem. That's why you still see a lot of FSMs, particularly auto-generated FSMs, implemented as either a loop around a switch or with state transitions implemented via gotos.

like image 40
David Hammen Avatar answered Oct 21 '22 09:10

David Hammen