googlefonts / googlefonts/fontc

Enable Link-Time Optimization (LTO) and codegen-units = 1 (+ some Profile-Guided Optimization (PGO) benchmarks)

Open
#1,359 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
193
Forks
21
Avg merge
1d 17h
Merged PRs (30d)
65

Description

Hi! I found the project after reading an [article](https://developer.chrome.com/blog/memory-safety-fonts) - thank you for your work!

I noticed that in the `Cargo.toml` file Link-Time Optimization (LTO) for the project is not enabled. I suggest switching it on since it will reduce the binary size (always a good thing to have) and will likely improve the application's performance. If you want to read more about LTO and its possible modes, I recommend starting from [this](https://doc.rust-lang.org/rustc/codegen-options/index.html#lto) Rustc documentation.

I think you can enable LTO only for the Release builds so as not to sacrifice the developers' experience while working on the project, since LTO consumes an additional amount of time to finish the compilation routine. In this case, we can create a dedicated `[profile.optimized-dev]` profile where LTO will be disabled (so developers experience will not be affected). If we enable it on the Cargo profile level for the Release profile, users, who install the tools with `cargo install`, will get the LTO-optimized version of the app "automatically". E.g., check `cargo-outdated` Release [profile](https://github.com/kbknapp/cargo-outdated/blob/master/Cargo.toml#L48). You also could be interested in other optimization options like `codegen-units = 1` - it also brings improvements over the current defaults.

Basically, it can be enabled with the following lines:

```
[profile.release]
codegen-units = 1
lto = true
```

I have made quick tests (AMD Ryzen 5900x, Fedora 41, Rust 1.85.1, the latest version of the project at the moment, `cargo build --release` command) - here are the results. I don't know which binaries are more or less important so I report all the results.

| Binary\Build mode | fontc | fontc_crater | fea-lsp | highlight | otl-normalizer | libascii_plist_derive.so |
| --- | --- | --- | --- | --- | --- | --- |
| Release | 20 Mib | 5.6 Mib | 7.3 Mib | 813 Kib | 1.8 Mib | 3.5 Mib |
| Release + `codegen-units = 1` + ThinLTO | 16 Mib | 4.5 Mib | 5.2 Mib | 708 Kib | 1.4 Mib | 3.3 Mib |
| Release + `codegen-units = 1` + FatLTO | 14 Mib | 4.2 Mib | 4.8 Mib | 636 Kib | 1.3 Mib | 3.3 Mib |

Clean build times:

* Release: 41s
* Release + `codegen-units = 1` + ThinLTO: 1m 09s
* Release + `codegen-units = 1` + FatLTO: 1m 46s

I also did quick performance benchmarks with a command from the README file:

```
hyperfine --warmup 25 --runs 250 --prepare 'rm -rf build/' 'taskset -c 0 original_release/fontc --source OswaldFont/sources/Oswald.glyphs' 'taskset -c 0 thin_lto_release/fontc --source OswaldFont/sources/Oswald.glyphs' 'taskset -c 0 fat_lto_release/fontc --source OswaldFont/sources/Oswald.glyphs'
Benchmark 1: taskset -c 0 original_release/fontc --source OswaldFont/sources/Oswald.glyphs
Time (mean ± σ): 106.8 ms ± 1.4 ms [User: 77.6 ms, System: 28.4 ms]
Range (min … max): 101.5 ms … 113.4 ms 250 runs

Benchmark 2: taskset -c 0 thin_lto_release/fontc --source OswaldFont/sources/Oswald.glyphs
Time (mean ± σ): 100.7 ms ± 1.4 ms [User: 71.3 ms, System: 28.7 ms]
Range (min … max): 94.9 ms … 105.3 ms 250 runs

Benchmark 3: taskset -c 0 fat_lto_release/fontc --source OswaldFont/sources/Oswald.glyphs
Time (mean ± σ): 100.1 ms ± 1.4 ms [User: 70.2 ms, System: 29.2 ms]
Range (min … max): 94.5 ms … 104.4 ms 250 runs

Summary
taskset -c 0 fat_lto_release/fontc --source OswaldFont/sources/Oswald.glyphs ran
1.01 ± 0.02 times faster than taskset -c 0 thin_lto_release/fontc --source OswaldFont/sources/Oswald.glyphs
1.07 ± 0.02 times faster than taskset -c 0 original_release/fontc --source OswaldFont/sources/Oswald.glyphs
```
where we also can see improvements in performance from enabling LTO.

In addition to Fat LTO and CG1 I decided to apply Profile-Guided Optimization (PGO) to the `fontc` compiler. Since it's a compiler, I decided that PGO can help with performance improvement even more (as I found many times in other PGO [benchmarks](https://github.com/zamazan4ik/awesome-pgo) for many other apps including compilers). So I trained `fontc` on the same workload as above with `cargo pgo` (`cargo pgo build` + a training run + `cargo pgo optimize` commands), and ran the benchmarks again. Here are the results:

```
hyperfine --warmup 25 --runs 250 --prepare 'rm -rf build/' 'taskset -c 0 original_release/fontc --source OswaldFont/sources/Oswald.glyphs' 'taskset -c 0 thin_lto_release/fontc --source OswaldFont/sources/Oswald.glyphs' 'taskset -c 0 fat_lto_release/fontc --source OswaldFont/sources/Oswald.glyphs' 'taskset -c 0 ./fontc_fat_lto_and_pgo_optimized --source OswaldFont/sources/Oswald.glyphs'
Benchmark 1: taskset -c 0 original_release/fontc --source OswaldFont/sources/Oswald.glyphs
Time (mean ± σ): 107.8 ms ± 2.2 ms [User: 78.3 ms, System: 28.7 ms]
Range (min … max): 104.5 ms … 133.9 ms 250 runs

Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs.

Benchmark 2: taskset -c 0 thin_lto_release/fontc --source OswaldFont/sources/Oswald.glyphs
Time (mean ± σ): 102.4 ms ± 1.9 ms [User: 73.2 ms, System: 28.4 ms]
Range (min … max): 95.2 ms … 109.4 ms 250 runs

Benchmark 3: taskset -c 0 fat_lto_release/fontc --source OswaldFont/sources/Oswald.glyphs
Time (mean ± σ): 100.9 ms ± 1.9 ms [User: 71.9 ms, System: 28.3 ms]
Range (min … max): 94.2 ms … 108.9 ms 250 runs

Benchmark 4: taskset -c 0 ./fontc_fat_lto_and_pgo_optimized --source OswaldFont/sources/Oswald.glyphs
Time (mean ± σ): 94.5 ms ± 1.9 ms [User: 65.2 ms, System: 28.6 ms]
Range (min … max): 88.5 ms … 104.8 ms 250 runs

Summary
taskset -c 0 ./fontc_fat_lto_and_pgo_optimized --source OswaldFont/sources/Oswald.glyphs ran
1.07 ± 0.03 times faster than taskset -c 0 fat_lto_release/fontc --source OswaldFont/sources/Oswald.glyphs
1.08 ± 0.03 times faster than taskset -c 0 thin_lto_release/fontc --source OswaldFont/sources/Oswald.glyphs
1.14 ± 0.03 times faster than taskset -c 0 original_release/fontc --source OswaldFont/sources/Oswald.glyphs
```

At least in this example, we see further improvements from applying LTO and PGO together. However, more tests are appreciated like using more fonts as a training set, etc.

Thank you.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with Cargo.toml and the existing release profile, then run the README benchmark command used with hyperfine. Compare release, codegen-units = 1, LTO, and the proposed PGO workflow, including build time, binary size, and runtime across representative workloads. Done means the supported optimization settings and benchmark results are agreed and reproducible.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
build-system, performance
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.