You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: content/learning-paths/servers-and-cloud-computing/gcc-lto/_index.md
+10-7Lines changed: 10 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,19 +1,18 @@
1
1
---
2
-
title: LTO Optimization With GCC
2
+
title: Optimize performance using Link-Time Optimization with GCC
3
3
4
4
draft: true
5
5
cascade:
6
6
draft: true
7
7
8
-
minutes_to_complete: 10
8
+
minutes_to_complete: 15
9
9
10
-
who_is_this_for: This is an introductory topic intended for developers who want to learn about improving application performance
11
-
using link-time optimization (LTO) with the GCC toolchain.
10
+
who_is_this_for: This is an introductory topic for developers who want to improve application performance using link-time optimization (LTO) with the GCC toolchain.
12
11
13
12
learning_objectives:
14
-
- Understand the key concepts behind link-time optimization (LTO)
15
-
- Learn how to enable and use LTO with the GCC compiler
16
-
- Develop intuition about the types of performance improvements LTO can provide
13
+
- Learn how link-time optimization works and when to use it
14
+
- Enable and configure LTO with GCC compiler flags
15
+
- Understand the performance and code size trade-offs of LTO
Copy file name to clipboardExpand all lines: content/learning-paths/servers-and-cloud-computing/gcc-lto/background.md
+6-6Lines changed: 6 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,19 +11,19 @@ layout: learningpathall
11
11
### Optimizations and Their Scope of Operation
12
12
Compiler optimizations can be categorized by the scope of code they are able to analyze and transform.
13
13
14
-
Some optimizations operate entirely within the scope of a single function. For example, dead code elimination can remove variables or instructions that are unused within a function body, without requiring any knowledge of how the rest of the program behaves.
14
+
Some optimizations operate entirely within the scope of a single function. For example, dead code elimination removes variables or instructions that are unused within a function body, without requiring knowledge of how the rest of the program behaves.
15
15
16
-
Other optimizations require visibility beyond a single function. For instance, if a function is consistently called with a constant value for one of its parameters, the compiler may be able to apply interprocedural constant propagation. Because such optimizations depend on assumptions about how code is used elsewhere, the compiler must apply them conservatively.
16
+
Other optimizations require visibility beyond a single function. For instance, if a function is consistently called with a constant value for one of its parameters, the compiler can apply interprocedural constant propagation. Because such optimizations depend on assumptions about how code is used elsewhere, the compiler must apply them conservatively to avoid breaking correctness.
17
17
18
-
Functions that are not visible outside the translation unit in which they are defined (for example, those declared static) give the compiler enough information to make these decisions at compile time. In contrast, functions that are exported from a shared library may be called from unknown code, making it impossible for the compiler to safely apply many interprocedural optimizations during compilation.
18
+
Functions that aren't visible outside the translation unit where they're defined (for example, those declared `static`) give the compiler enough information to make optimization decisions at compile time. Functions that are exported from a shared library may be called from unknown code, making it unsafe for the compiler to apply many interprocedural optimizations during compilation.
19
19
20
20
Between these two extremes are functions that are used across multiple translation units within a program, but whose complete usage becomes known only when the final executable is produced. These cases are where link-time optimization (LTO) is most effective, as it allows the compiler to analyze the whole program as a single unit and apply optimizations that would otherwise be unavailable.
21
21
22
22
### Link-Time Optimization and Intermediate Code Representation
23
23
Under normal compilation, once a translation unit has been processed, GCC emits an object file (.o). This object file contains machine code for the translation unit, along with relocation information, symbol metadata, and data required for linking. At this stage, the compiler has already committed to specific instruction sequences and discarded most of its internal analysis state.
24
24
25
-
By committing early to machine code, the compiler significantly limits its ability to perform optimizations that depend on cross-file visibility once the linker combines object files into a final executable.
25
+
By committing early to machine code, the compiler significantly limits its ability to perform optimizations that depend on cross-file visibility when the linker combines object files into a final executable.
26
26
27
-
When link-time optimization(LTO) is enabled, GCC changes this behavior. Instead of discarding its internal representation after compilation, GCC preserves an intermediate representation of the program. Specifically, GCC serializes its GIMPLE intermediate representationinto a bytecode format and embeds it into special sections of the object file.
27
+
When link-time optimization(LTO) is enabled, GCC changes this behavior. Instead of discarding its internal representation after compilation, GCC preserves an intermediate representation of the program. Specifically, GCC serializes its GIMPLE intermediate representation—a language-independent tree-based representation used internally by GCC—into a bytecode format and embeds it into special sections of the object file.
28
28
29
-
At link time, the compiler is invoked again, this time acting as an LTO-aware front end that reads the GIMPLE bytecode from all participating object files. With visibility into the entire program, GCC can perform whole-program optimizations before generating the final machine code.
29
+
At link time, the compiler is invoked again, this time acting as an LTO-aware front end that reads the GIMPLE bytecode from all participating object files. With visibility into the entire program, GCC can perform whole-program optimizations, such as aggressive function inlining and interprocedural constant propagation, before generating the final machine code.
Copy file name to clipboardExpand all lines: content/learning-paths/servers-and-cloud-computing/gcc-lto/performance-uplift.md
+18-12Lines changed: 18 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,41 +8,47 @@ layout: learningpathall
8
8
9
9
## Comparing Performance
10
10
11
-
The potential benefits of link-time optimization can be illustrated through a performance comparison using the SPEC CPU®2017 integer (SPECint2017) benchmark suite. In this example, benchmarks were compiled with GCC 15.1, with and without LTO, and executed on an Arm Neoverse V2 CPU running Ubuntu 20.04 LTS.
11
+
The potential benefits of link-time optimization can be illustrated through a performance comparison using the [SPEC CPU®2017](https://www.spec.org/cpu2017/) integer rate (SPECint2017 rate) benchmark suite. SPEC CPU is an industry-standard benchmark widely used by CPU vendors and compiler teams to evaluate and compare performance. While these benchmarks don't necessarily reflect the characteristics of every real-world application, they provide a standardized way to measure optimization impact across different configurations.
12
+
13
+
In this example, benchmarks were compiled with GCC 15.1, with and without LTO, and executed on an Arm Neoverse V2 CPU running Ubuntu 20.04 LTS.
12
14
13
15
Common optimization flags between runs were as follows:
Across the benchmark suite, enabling LTO resulted in an improvement in the geometric mean score of approximately 3.4%. Some workloads benefited more noticeably, including:
20
-
*`gmcf` (+11%)
21
+
Across the benchmark suite, enabling LTO resulted in an improvement in the geometric mean score of approximately 3.4%. Some workloads benefited more noticeably:
22
+
*`mcf` (+11%)
21
23
*`deepsjeng` (+9.9%)
22
24
*`leela` (+6.6%)
23
-
These results highlight how workloads with significant cross-module interaction can benefit from the additional visibility provided by LTO.
25
+
26
+
These results highlight how workloads with significant cross-module interaction—particularly those with many function calls across translation units—benefit from the additional whole-program visibility provided by LTO.
24
27
25
28

26
29
27
-
### Code-size Considerations
30
+
### Code Size Considerations
28
31
29
-
While performance improvements are often the primary motivation for enabling LTO, its impact is not limited to execution speed. Link-time optimization can also significantly affect the final code size of an executable.
32
+
While performance improvements are often the primary motivation for enabling LTO, its impact isn't limited to execution speed. Link-time optimization can also significantly affect the final code size of an executable, with results varying depending on the application's structure.
30
33
31
-
As shown in figure 2, the use of LTO can have considerable impact on the final code size of the resulting executable.
34
+
As shown in Figure 2, LTO can have considerable impact on the final code size of the resulting executable, with some benchmarks showing reductions and others showing increases.
One common source of code size reduction with LTO is cross-translation-unit dead code elimination. With whole-program visibility, the compiler can determine whether non-static functions or global variables are ever referenced anywhere in the final executable.
38
-
Without LTO, such symbols must be retained conservatively, as they may be referenced by other translation units or during linking. With LTO enabled, the compiler can make a definitive decision and eliminate unused functions and variables, reducing the size of the resulting binary.
40
+
One common source of code size reduction with LTO is cross-translation-unit dead code elimination. With whole-program visibility, the compiler determines whether non-`static` functions or global variables are ever referenced anywhere in the final executable.
41
+
42
+
Without LTO, such symbols must be retained conservatively because they may be referenced by other translation units during linking. With LTO enabled, the compiler makes a definitive decision and eliminates unused functions and variables, reducing the size of the resulting binary. This is particularly effective for libraries where only a subset of the exported functions are actually used by the application.
39
43
40
44
#### Potential Code Size Increase
41
45
42
-
While global visibility often enables code size reductions, some LTO-driven optimizations can lead to larger binaries when they are deemed profitable for performance.
46
+
While global visibility often enables code size reductions, some LTO-driven optimizations lead to larger binaries when they're deemed profitable for performance.
47
+
43
48
Examples include:
44
49
* Aggressive loop unrolling when iteration counts are known in specific call paths
45
50
* Increased function inlining when call relationships are well understood
46
51
47
-
In addition, LTO enables function cloning. When a function exhibits multiple common usage patterns, the compiler may generate specialized versions optimized for frequent cases, while retaining a generic version for less common ones. Although this approach preserves correctness and improves performance for hot paths, it can introduce code duplication and increase overall binary size.
48
-
As with other interprocedural optimizations, these trade-offs reflect the compiler’s attempt to balance performance gains against code size growth, and the net effect depends heavily on the structure of the application.
52
+
LTO also enables function cloning (also called function specialization). When a function exhibits multiple common usage patterns, the compiler may generate specialized versions optimized for frequent cases, while retaining a generic version for less common ones. Although this approach preserves correctness and improves performance for hot paths, it introduces code duplication and increases overall binary size.
53
+
54
+
These trade-offs reflect the compiler's cost model, which balances performance gains against code size growth. The net effect depends heavily on the application's structure. For performance-critical applications, the speed improvements typically outweigh the code size increase.
In this case, each object file contains LTO information, and the final link step performs whole-program optimization before generating machine code.
20
+
Each object file contains LTO information embedded in special sections. The final link step performs whole-program optimization before generating machine code.
21
21
22
-
For small programs, this can be simplified into a single command:
22
+
For small programs, you can simplify this into a single command:
Both approaches produce an executable that benefits from link-time optimization across all translation units.
26
+
Both approaches produce an executable that benefits from link-time optimization across all translation units. The `-O2` optimization level is recommended as a baseline, though you can also use `-O3` for more aggressive optimizations.
27
27
28
28
### Modifying LTO behaviour
29
29
#### Flexible object files
30
30
31
-
When compiling with `-flto`, GCC normally emits **slim LTO objects**. These object files contain only GCC’s internal intermediate representation and no conventional machine code. As a result, they can only be linked by an LTO-capable linker invocation.
32
-
In some cases—such as when building libraries intended for broader reuse, it may be desirable to retain conventional object code alongside the LTO data. This can be achieved using the `-ffat-lto-objects` flag, for example:
31
+
When compiling with `-flto`, GCC normally emits **slim LTO objects**. These object files contain only GCC's internal intermediate representation (GIMPLE bytecode) and no conventional machine code. As a result, they can only be linked by an LTO-capable linker invocation.
32
+
33
+
For libraries intended for broader reuse—or when you need compatibility with non-LTO builds—you can retain conventional object code alongside the LTO data using the `-ffat-lto-objects` flag:
33
34
```bash
34
35
gcc -c -O2 -flto -ffat-lto-objects component-1.c
35
36
```
36
37
With this option enabled, GCC emits both:
37
-
* The intermediate representation used for LTO, and
38
+
* The intermediate representation used for LTO
38
39
* The non-LTO object code that would normally be produced
39
40
40
-
Such fat LTO objects provide greater compatibility at the cost of increased object file size.
41
+
Fat LTO objects provide greater compatibility at the cost of increased object file size (typically 1.5-2x larger). Use this option when building static or shared libraries that might be linked without LTO.
41
42
42
43
#### Parallelization
43
-
Link-time optimization can be computationally expensive, especially for larger programs. GCC supports parallelizing LTO to reduce build times.
44
+
Link-time optimization can be computationally expensive, especially for larger programs. GCC supports parallelizing LTO to reduce build times by distributing the optimization work across multiple CPU cores.
44
45
45
-
This behavior is controlled through the `-flto` flag:
46
+
Control this behavior through the `-flto` flag:
46
47
47
-
* -flto=auto enables automatic parallelization based on available system resources
48
-
* -flto=<n> explicitly specifies the number of parallel LTO jobs to run
48
+
*`-flto=auto` enables automatic parallelization based on available system resources
49
+
*`-flto=<n>` explicitly specifies the number of parallel LTO jobs to run
When parallelization is enabled, GCC partitions the program into multiple units of roughly equal size. The compiler attempts to minimize cross-partition references, which could otherwise reduce the effectiveness of certain whole-program optimizations.
55
+
When parallelization is enabled, GCC partitions the program into multiple units of roughly equal size. The compiler attempts to minimize cross-partition references, which could otherwise reduce the effectiveness of certain whole-program optimizations. For best results, set the parallelization level to match the number of available CPU cores.
55
56
56
57
#### Caching
57
-
During iterative development, repeatedly recompiling with LTO can increase build times. GCC provides support for caching intermediate LTO results to speed up incremental builds.
58
-
This can be enabled using the `-flto-incremental=<path>` option. For example:
58
+
During iterative development, repeatedly recompiling with LTO can increase build times. GCC provides support for caching intermediate LTO results to speed up incremental builds by reusing previously computed optimization information.
59
+
60
+
Enable this using the `-flto-incremental=<path>` option:
When enabled, GCC stores intermediate results in the specified directory, allowing subsequent builds to reuse previous work where possible and significantly reduce edit–compile cycles.
64
+
When enabled, GCC stores intermediate optimization results in the specified directory. Subsequent builds reuse previous work where possible, significantly reducing edit–compile cycle times. The cache directory grows over time, so you may need to clean it periodically during development.
0 commit comments