Skip to content

Commit 3534833

Browse files
authored
Merge pull request #2863 from pareenaverma/content_review
Tech review of the GCC LTO LP
2 parents 8793615 + 6babce4 commit 3534833

4 files changed

Lines changed: 53 additions & 42 deletions

File tree

content/learning-paths/servers-and-cloud-computing/gcc-lto/_index.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
---
2-
title: LTO Optimization With GCC
2+
title: Optimize performance using Link-Time Optimization with GCC
33

44
draft: true
55
cascade:
66
draft: true
77

8-
minutes_to_complete: 10
8+
minutes_to_complete: 15
99

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.
1211

1312
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
1716

1817
prerequisites:
1918
- A recent version of the GCC toolchain
@@ -40,6 +39,10 @@ further_reading:
4039
title: Gentoo Wiki LTO
4140
link: https://wiki.gentoo.org/wiki/LTO
4241
type: website
42+
- resource:
43+
title: SPEC CPU 2017 Benchmark Suite
44+
link: https://www.spec.org/cpu2017/
45+
type: website
4346

4447
### FIXED, DO NOT MODIFY
4548
# ================================================================================

content/learning-paths/servers-and-cloud-computing/gcc-lto/background.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,19 @@ layout: learningpathall
1111
### Optimizations and Their Scope of Operation
1212
Compiler optimizations can be categorized by the scope of code they are able to analyze and transform.
1313

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.
1515

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.
1717

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.
1919

2020
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.
2121

2222
### Link-Time Optimization and Intermediate Code Representation
2323
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.
2424

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.
2626

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 into 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.
2828

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.

content/learning-paths/servers-and-cloud-computing/gcc-lto/performance-uplift.md

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,41 +8,47 @@ layout: learningpathall
88

99
## Comparing Performance
1010

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.
1214

1315
Common optimization flags between runs were as follows:
1416

1517
```c
1618
-mcpu=native -Ofast -fomit-frame-pointer --param ipa-cp-eval-threshold=1 --param ipa-cp-unit-growth=80
1719
```
1820

19-
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%)
2123
* `deepsjeng` (+9.9%)
2224
* `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.
2427

2528
![SPECint LTO performance gains#center](specint_lto_improv.png "Figure 1. Performance uplift to Specint2017")
2629

27-
### Code-size Considerations
30+
### Code Size Considerations
2831

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.
3033

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.
3235

3336
![SPECint LTO code size reduction#center](specint_lto_size.png "Figure 2. Code size reduction to Specint2017")
3437

3538
#### Potential Code Size Reduction
3639

37-
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.
3943

4044
#### Potential Code Size Increase
4145

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+
4348
Examples include:
4449
* Aggressive loop unrolling when iteration counts are known in specific call paths
4550
* Increased function inlining when call relationships are well understood
4651

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.

content/learning-paths/servers-and-cloud-computing/gcc-lto/request-lto.md

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,54 +9,56 @@ layout: learningpathall
99
## Deploying LTO
1010

1111
### A Simple Use Case
12-
Using link-time optimization with GCC can be as simple as passing the `-flto` flag during compilation and linking.
12+
You can enable link-time optimization with GCC by passing the `-flto` flag during both compilation and linking.
1313

14-
For a traditional, stepwise build of an executable, you would compile each translation unit with LTO enabled:
14+
For a traditional, stepwise build of an executable, compile each translation unit with LTO enabled:
1515
```bash
1616
gcc -c -O2 -flto component-1.c
1717
gcc -c -O2 -flto component-2.c
1818
gcc -o myprog -flto -O2 component-1.o component-2.o
1919
```
20-
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.
2121

22-
For small programs, this can be simplified into a single command:
22+
For small programs, you can simplify this into a single command:
2323
```bash
2424
gcc -o myprog -flto -O2 component-1.c component-2.c
2525
```
26-
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.
2727

2828
### Modifying LTO behaviour
2929
#### Flexible object files
3030

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:
3334
```bash
3435
gcc -c -O2 -flto -ffat-lto-objects component-1.c
3536
```
3637
With this option enabled, GCC emits both:
37-
* The intermediate representation used for LTO, and
38+
* The intermediate representation used for LTO
3839
* The non-LTO object code that would normally be produced
3940

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.
4142

4243
#### 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.
4445

45-
This behavior is controlled through the `-flto` flag:
46+
Control this behavior through the `-flto` flag:
4647

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
4950

5051
For example:
5152
```bash
5253
gcc -O2 -flto=4 -o myprog component-1.c component-2.c
5354
```
54-
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.
5556

5657
#### 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:
5961
```bash
6062
gcc -O2 -flto -flto-incremental=lto-cache -c component-1.c
6163
```
62-
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

Comments
 (0)