[ACCEPTED]-C/C++: is GOTO faster than WHILE and FOR?-goto
Generally speaking, for
and while
loops get compiled 9 to the same thing as goto
, so it usually won't 8 make a difference. If you have your doubts, you 7 can feel free to try all three and see which 6 takes longer. Odds are you'll be unable 5 to measure a difference, even if you loop 4 a billion times.
If you look at this answer, you'll 3 see that the compiler can generate exactly 2 the same code for for
, while
, and goto
(only in this 1 case there was no condition).
The only time I've seen the argument made 11 for goto was in one of W. Richard Stevens' articles 10 or books. His point was that in a very time-critical 9 section of code (I believe his example was 8 the network stack), having nested if/else 7 blocks with related error-handling code 6 could be redone using goto in a way that 5 made a valuable difference.
Personally, I'm 4 not good enough a programmer to argue with 3 Stevens' work, so I won't try. goto can be 2 useful for performance-related issues, but 1 the limits of when that is so are fairly strict.
Write short programs, then do this:
gcc -S -O2 p1.c
gcc -S -O2 p2.c
gcc -S -O2 p3.c
Analyze 13 the output and see if there's any difference. Be 12 sure to introduce some level of unpredictability 11 such that the compiler doesn't optimize 10 the program away to nothing.
Compilers do 9 a great job of optimizing these trivial 8 concerns. I'd suggest not to worry about 7 it, and instead focus on what makes you 6 more productive as a programmer.
Speed and 5 efficiency is a great thing to worry about 4 it, but 99% of the time that involves using 3 proper data structures and algorithms... not 2 worrying about whether a for
is faster than 1 a while
or a goto
, etc.
It is probably both compiler, optimiser 25 and architecture specific.
For example the 24 code if(--i) goto loop;
is a conditional test followed by an unconditional branch. A compiler 23 might simply generate corresponding code 22 or it might be smart enough (though a compiler 21 that did not have at least that much smarts 20 may not be worth much), to generate a single 19 conditional branch instruction. while(i--)
on the other hand is already 18 a conditional branch at the source level, so 17 translation to a conditional branch at the 16 machine level may be more likley regardless 15 of the sophistication of the compiler implementation 14 or optimiser.
In the end, the difference 13 is likley to be minute and only relevant 12 if a great many iterations are required, and 11 the way you should answer this question 10 is to build the code for the specific target 9 and compiler (and compiler settings) of 8 interest, and either inspect the resultant 7 machine level code or directly measure execution 6 time.
In your examples the printf() in the 5 loop will dominate any timing in any case; something 4 simpler in the loop would make observations 3 of the differences easier. I would suggest 2 an empty loop, and then declaring i
volatile
to prevent 1 the loop being optimised to nothing.
As long as you're generating the same flow 11 of control as a normal loop, pretty nearly 10 any decent compiler can and will produce 9 the same code whether you use for
, while
, etc. for 8 it.
You can gain something from using goto
, but 7 usually only if you're generating a flow 6 of control that a normal loop simply can't 5 (at least cleanly). A typical example is 4 jumping into the middle of a loop to get 3 a loop and a half construct, which most 2 languages' normal loop statements (including 1 C's) don't provide cleanly.
There is should not be any significant difference 8 between all the loops and the goto. Except 7 the idea, that compiler more probably will 6 not try to optimize the GOTO-things at all.
And 5 there is not a lot of sense trying to optimize 4 compiler-generated stuff in loops. It's 3 more sense to optimize the code inside the 2 loop, or reduce the number of iterations 1 or so on.
I think there will be some code after compiler 3 under nornal condition.
In fact I think goto 2 is very convenient sometimes, although 1 it is hard to read.
There are several niche verticals where 38 goto is still commonly used as a standard 37 practice, by some very very smart folks 36 and there is no bias against goto in those 35 settings. I used to work at a simulations 34 focused company where all local fortran 33 code had tons of gotos, the team was super 32 smart, and the software worked near flawlessly.
So, we 31 can leave the merit of goto aside, and if 30 the question merely is to compare the loops, then 29 we do so by profiling and/or comparing the 28 assembly code. That said however, the question 27 includes statements like printf etc. You 26 can't really have a discussion about loop 25 control logic optimization when doing that. Also, as 24 others have pointed out, the given loops 23 will all generate VERY similar machine codes.
All 22 conditional branches are considered "taken" (true) in 21 pipelined processor architectures anyway 20 until decode phase, in addition to small 19 loops being usually expanded to be loopless. So, in 18 line with Harper's point above, it is unrealistic 17 for goto to have any advantage whatsoever 16 in simple loop control (just as for or while 15 don't have an advantage over each other). GOTO 14 makes sense usually in multiple nested loops 13 or multiple nested ifs, when adding the 12 additional condition checked by goto into 11 EACH of the nested loops or nested ifs is 10 suboptimal.
When optimizing a search kind 9 of operation in a simple loop, using a sentinal 8 is sometimes more effective than anything 7 else. Essentially, by adding a dummy value 6 at the end of the array, you can avoid checking 5 for two conditions (end of array and value 4 found) to be just one condition (value found), and 3 that saves on cmp operations internally. I 2 am unaware if compilers automatically do 1 that or not.
goto Loop:
start_Chicken:
{
++x;
if (x >= loops)
goto end_Chicken;
}
goto start_Chicken;
end_Chicken:
x = 0;
for Loop:
for (int i = 0; i < loops; i++)
{
}
while Loop:
while (z <= loops)
{
++z;
}
z = 0;
While loop 2 in any situation with more mixed tests had 1 as minimal but still better results.
On Linux, I compiled the code below into 35 assembly using both g++ and clang++. For 34 more information on how I did that, see 33 here. (Short version: g++ -S -O3 filename.cpp
clang++ -S -O3 filename.cpp
, and some assembly comments 32 you'll see below to help me out.)
Conclusion/TL;DR 31 at the bottom.
First, I compared label:
and goto
vs. do {} while
. You 30 can't compare a for () {}
loop with this (in good 29 faith), because a for loop always evaluates 28 the condition first. This time around, the 27 condition is evaluated only after the loop 26 code has been executed once.
#include <iostream>
void testGoto()
{
__asm("//startTest");
int i = 0;
loop:
std::cout << i;
++i;
if (i < 100)
{
goto loop;
}
__asm("//endTest");
}
#include <iostream>
void testDoWhile()
{
__asm("//startTest");
int i = 0;
do
{
std::cout << i;
++i;
}
while (i < 100);
__asm("//endTest");
}
In both cases, the 25 assembly is the exact same regardless of 24 goto
or do {} while
, per compiler:
g++:
xorl %ebx, %ebx
leaq _ZSt4cout(%rip), %rbp
.p2align 4,,10
.p2align 3
.L2:
movl %ebx, %esi
movq %rbp, %rdi
addl $1, %ebx
call _ZNSolsEi@PLT
cmpl $100, %ebx
jne .L2
clang++:
xorl %ebx, %ebx
.p2align 4, 0x90
.LBB0_1: # =>This Inner Loop Header: Depth=1
movl $_ZSt4cout, %edi
movl %ebx, %esi
callq _ZNSolsEi
addl $1, %ebx
cmpl $100, %ebx
jne .LBB0_1
# %bb.2:
Then I compared 23 label:
and goto
vs. while {}
vs. for () {}
. This time around, the condition 22 is evaluated before the loop code has been 21 executed even once.
For goto
, I had to invert 20 the condition, at least for the first time. I 19 saw two ways of implementing it, so I tried 18 both ways.
#include <iostream>
void testGoto1()
{
__asm("//startTest");
int i = 0;
loop:
if (i >= 100)
{
goto exitLoop;
}
std::cout << i;
++i;
goto loop;
exitLoop:
__asm("//endTest");
}
#include <iostream>
void testGoto2()
{
__asm("//startTest");
int i = 0;
if (i >= 100)
{
goto exitLoop;
}
loop:
std::cout << i;
++i;
if (i < 100)
{
goto loop;
}
exitLoop:
__asm("//endTest");
}
#include <iostream>
void testWhile()
{
__asm("//startTest");
int i = 0;
while (i < 100)
{
std::cout << i;
++i;
}
__asm("//endTest");
}
#include <iostream>
void testFor()
{
__asm("//startTest");
for (int i = 0; i < 100; ++i)
{
std::cout << i;
}
__asm("//endTest");
}
As above, in all four cases, the 17 assembly is the exact same regardless of 16 goto
1 or 2, while {}
, or for () {}
, per compiler, with just 15 1 tiny exception for g++ that may be meaningless:
g++:
xorl %ebx, %ebx
leaq _ZSt4cout(%rip), %rbp
.p2align 4,,10
.p2align 3
.L2:
movl %ebx, %esi
movq %rbp, %rdi
addl $1, %ebx
call _ZNSolsEi@PLT
cmpl $100, %ebx
jne .L2
Exception 14 for g++: at the end of the goto2 assembly, the 13 assembly added:
.L3:
endbr64
(I presume this extra label 12 was optimized out of the goto
1's assembly.) I 11 would assume that this is completely insignificant 10 though.
clang++:
xorl %ebx, %ebx
.p2align 4, 0x90
.LBB0_1: # =>This Inner Loop Header: Depth=1
movl $_ZSt4cout, %edi
movl %ebx, %esi
callq _ZNSolsEi
addl $1, %ebx
cmpl $100, %ebx
jne .LBB0_1
# %bb.2:
In conclusion/TL;DR: No, there does not appear to be any difference whatsoever between any of the possible equivalent arrangements of label:
and goto
, do {} while
, while {}
, and for () {}
, at least on Linux using g++ 9.3.0 and clang++ 10.0.0.
Note that I did not test 9 break
and continue
here; however, given that the assembly 8 code generated for each of the 4 in any 7 scenario was the same, I can only presume 6 that they would be the exact same for break
and 5 continue
, especially since the assembly is using 4 labels and jumps for every scenario.
To ensure 3 correct results, I was very meticulous in 2 my process and also used Visual Studio Code's 1 compare files feature.
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.