GCC Compiler Optimization Flags
-O1, O2, O3
The GNU Compiler Collection, commonly known as GCC has a set of compilers and development for most distros. It has support for primarily C and C++ and other languages as well. Our main focus is on the various optimization flags.
Note: We shall consider only `C++` for simplicity.
Levels of Optimization
There are majorly three levels of optimizations that are frequently used with increasing order of optimization level. They are:
-O1
-O2
-O3
By default, -O optimization is applied where the compiler tries to reduce code size and execution time and provide much execution time. Then we can see minor/major improvements based on the code and assembly generated. Besides these, GCC also provides many other optimization flags. The entire list is available here.
Assembly?
Yes, you heard it right the C++ code written gets compiled initially by an Assembler and generates an object file `.o`. We can see the magic of these optimization flags in the generated assembly. Let’s consider the problem of calculating the sum of the first 10001 natural numbers. We can say it using the formula (n*(n+1)/2). But let’s write a naive way of calculating it using a simple for loop and see how the compiler arrives at the direct answer by activating different optimization flags.
int calc_sum()
{
int ans = 0;
for(int i = 0; i < 10001; ++i)
ans += i;
return ans;
}When we compile it without any flag we can see a naive code translated to assembly. I'm glad to be from ECE to understand the assembly code partially 🤧
Now let’s see the magic of the GCC compiler and how it translates the code by enabling the -O1 flag.
Boom, we can see that it directly loads the end value inside a register, then subtracts one for `(end - 1)` in the for loop. Before returning, a direct calculation `50005000` without any looping.
Amazing right but this is at least not the end, we can further improve the optimization by enabling the `-O2` and `-O3` flags. For this snippet, the assembly code is the same.
🎉 Direct computation of the result just by storing the exact required value in the register. All this is not magic but the various other flags that get enabled during the activation of various optimizations.
Tradeoffs!
When we need something, we need to lose something. In this context, if we need to get accurate results, then it’s recommended practice to use the defaults. The reason is numerous flags get enabled and difficult to understand the root cause of the failure. Or else you can turn on the desired optimization level, which could have a greater compilation time but faster execution time.
You can find the code here with the generated assembly without any installation. Also, you can play with all flags by enabling the respective flags like -O3, etc, in the right panel on the placeholder named `Compiler options…`
Stay connected for more such content 🤝





