LLVM Propeller, a profile-guided optimizer that relinks your whole program
PROPELLER: Profile Guided Optimizing Large Scale LLVM-based Relinker
At a glance
- What is it?
- Propeller is Google's profile-guided, relinking optimizer for warehouse-scale applications, built on LLVM. It takes a profile of a running binary and produces two artifacts, a compiler profile and a linker profile, so that a later link of the same program can be arranged around the code that actually matters.
- Who is it for?
- Propeller suits a team building a very large binary, such as a monorepo service or a warehouse-scale application, that can collect a profile from a representative workload and re-link with it. It does not suit a small binary, where the link-time layout hardly matters, and it is a research project with no releases, so you are building from a branch.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 29 days ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A linker that rearranges the program after profiling
Propeller is described as a profile-guided, relinking optimizer for warehouse-scale applications, built on top of LLVM, providing a framework for computing whole-program optimizations for applications built with LLVM.
The phrase that matters is relinking. A conventional profile-guided optimization flow profiles a build, feeds the profile back to the compiler, and rebuilds. The profile informs decisions inside each translation unit, such as which branches are likely and which functions should be inlined.
Propeller's approach adds a stage after that. It takes a profile of an already-built binary and produces a linker profile, which is then used to arrange the program at link time. The unit of optimisation is the whole program rather than the function or the translation unit, and the input is the profile of a real run rather than a training corpus of representative runs.
The reason this matters at warehouse scale is a specific and well-known effect. A large binary's hot code and its cold code are interleaved in the final layout, so the instruction cache, the branch predictor and the page tables all suffer, and the same function can end up split across pages that are never fetched together. A linker that knows which parts of the program are hot can lay the hot parts out together and push the cold parts out of the way. The payoff grows with binary size, which is why the project targets warehouse-scale applications rather than a command line tool.
The repository is Google's, the primary language is C++, and the licence is Apache-2.0. The homepage given for the project is not a documentation site but the PDF of a design document, Propeller_RFC.pdf on the plo-dev branch, which tells you something about where the project is: the design is still being written up rather than frozen into a manual.
There are no GitHub releases. The last push to the repository was on 2026-09-24, so the project is being worked on, and what you consume is a branch rather than a tagged version.
Two profiles come out of one run
The tool produces two artifacts from a single input profile, and the split is the core of the design.
The compiler profile is `cc_profile.txt` and the linker profile is `ld_profile.txt`. The README says the compiler profile is used by LLVM to guide optimizations and is described in the Propeller Profile Format document, `propeller_profile_format.md`.
So the same execution profile is projected into two different consumers. The compiler profile is fed back into the LLVM optimisation pipeline, in the same spirit as the instrumentation profile that a conventional PGO flow produces, and it guides the same class of decisions about inlining, branch layout and code placement. The linker profile is the new part, because it is consumed at link time and describes the layout of the program rather than the internals of a function.
That split also explains the name of the generating tool, `generate_propeller_profiles`, plural. It is not a linker, it is not a compiler, it is the step that turns an observed execution into the two files the rest of the toolchain consumes.
The command line shows the four inputs and outputs.
./generate_propeller_profiles \
--binary=/path/to/profiled/binary \
--profile=/path/to/input/profile.txt \
--cc_profile=/path/to/out/cc_profile.txt \
--ld_profile=/path/to/out/ld_profile.txtThe binary is needed as well as the profile, which is a detail with consequences. To produce a linker profile you need to know the layout of the binary that was profiled, since the profile refers to addresses or symbols in that specific build, and the tool has to map the observed code back to the functions and sections it will place in the new link. That is why the binary and the profile have to come from the same run, and why the workflow below pairs a profiled build with a relink of the same source.
The input profile is a text file, and the format of the profile is documented separately in the profile format document. A text profile is a deliberate choice for a tool that runs inside a build pipeline, since it is inspectable, diffable and easy to pass around between machines.
One consequence of the whole design is that this is a two-build workflow. You build and instrument, run the workload, generate the two profiles, and then build again with the profiles in hand. The README's quickstart is structured around exactly that sequence.
Building it, with CMake or Bazel
The build section is the part of the README with the most operational detail, and it starts with the platform requirement.
The supported operating system is Ubuntu 22.04 or newer, and that is the only row in the table. So Propeller is a Linux tool as things stand, which is not surprising for a linker extension aimed at large server binaries, but it does mean the project is not directly usable on macOS or Windows without a Linux environment.
The build system pulls in most dependencies automatically, but a few packages have to be installed by hand.
# Common dependencies
sudo apt install -y wget lsb-release software-properties-common gnupgThe compiler requirement is stated in a comment and it is specific: Propeller builds with Clang 19.0.0 or newer. The instructions fetch the LLVM apt script and run it for version 19, then point the environment at the resulting binaries.
# Propeller builds with Clang 19.0.0 or newer.
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 19
export CC=clang-19
export CXX=clang++-19The export of `CC` and `CXX` matters more than it looks. Propeller instruments and analyses LLVM IR, and the analysis it performs is tied to a particular LLVM version, so a build against a different LLVM is likely to fail at compile time or, worse, to produce subtly different output. Pinning the compiler is the mechanism for keeping the two in step.
Three development libraries are needed for the CMake build, which tells you what the tool actually uses from the system.
# For the CMake build
sudo apt-get update && sudo apt-get install -y \
libelf-dev \
libssl-dev \
libzstd-dev`libelf-dev` is the one that explains the design. ELF is the executable format on Linux, and a tool that reads a running binary to map profile data back onto functions and sections needs to parse the binary format directly. The other two are compression and crypto libraries, which is consistent with a tool that reads and writes profile data and needs to handle compressed profile inputs.
Both build systems are supported. The CMake build requires CMake 3.24 or newer, and the Bazel build requires Bazel 5.0 or newer.
cmake -G Ninja -B build
ninja -C build generate_propeller_profilesThe first target is the same generator you then run, so the build produces the profile generator rather than a library, which tells you where the entry point is. The Bazel equivalent is `bazel build //propeller:generate_propeller_profiles`, and the test suites are `ninja -C build test` and `bazel test //propeller/...:all`.
Where this fits, and what it costs
Propeller is not a competitor to profile-guided optimisation, and treating it as one leads to the wrong conclusion.
Standard PGO already works, and it works well. You instrument a build, run a representative workload, and rebuild with the profile, and the compiler lays out functions and branches according to what the profile says. What PGO cannot do is reorganise the program after the fact, because the compiler's decisions are made per translation unit and the linker places the resulting objects without knowing which functions are hot.
The difference in approach is therefore about the unit of optimisation and the moment of the decision. PGO optimises within a translation unit at compile time, and the link is a fixed ordering of the results. Propeller computes a whole-program view from the profile of a complete binary and uses it at link time, so a function that is cold in isolation but sits between two hot functions can be moved, and sections that are never touched can be grouped together so they are never fetched.
The cost is a second build and a profiling run. You need a representative workload, and the profile is only as good as that workload, and the binary has to be rebuilt for the layout to change, which means your build time roughly doubles for the optimised variant.
The other cost is integration. The compiler profile plugs into the existing LLVM machinery, but the linker profile has to be consumed by whatever performs the link, and a project with a custom link step, a proprietary linker, or a large build system of its own has work to do that no amount of configuration avoids. The README documents the generation side in detail and does not document the consumption side in the excerpt available, which is where a reader should look next.
The alternatives are three, and they differ in where the layout knowledge comes from. Doing nothing is the baseline, and for a binary that fits comfortably in the instruction cache it costs little. Standard PGO with a linker that already does function ordering, which some toolchains offer, gets part of the way without a new tool. And a custom linker written for the specific application, which is what a few large companies do, gets the same result with far more work.
For a binary large enough that its layout is a performance problem, Propeller is the option that keeps the work in open source tooling rather than in a bespoke linker.
The project state and how to evaluate it
Two things in the repository tree tell you what stage this is at.
The first is the `ArtifactEvaluation/` directory, with its own README that the main README links to for artifact evaluation. That is the machinery of a systems research paper: the setup needed to reproduce the experimental results, checked in so a reader can rerun them. It is a strong signal that the project is intended to be evaluated as research rather than adopted as a product.
The second is the homepage pointing at `Propeller_RFC.pdf` on the `plo-dev` branch rather than at a documentation site. An RFC, a request for comments, on a development branch, means the design is still being discussed. The repository layout supports that reading, with a `Docs/` directory that is not the main entry point, an `llvm_ext.bzl` file that suggests a Bazel extension layer for LLVM rules, a `MODULE.bazel` for Bazel module resolution, and a `Bazel/` directory alongside a `CMake/` one for the two build systems.
There is also an `AUTHORS` file, which is standard practice in the LLVM ecosystem, and the `propeller/` directory at the root which is the main package in both build configurations.
The practical evaluation advice follows from all this. There are no GitHub releases, so there is no version to pin and no changelog to read, and the branch you build from is the branch you get. The design document is the closest thing to a specification, and the profile format document is the closest thing to an API contract. If you are going to try this, the profile format is the file to read first, because the format is what your build system has to produce or consume, and it is the part most likely to change.
The licence is Apache-2.0, which is the same licence LLVM itself uses, so the code can live alongside LLVM tooling in a build without a licensing mismatch.
Editorial conclusion
Propeller suits a team building a very large binary, such as a monorepo service or a warehouse-scale application, that can collect a profile from a representative workload and re-link with it. It does not suit a small binary, where the link-time layout hardly matters, and it is a research project with no releases, so you are building from a branch. Verify first that your toolchain is Clang 19 or newer with the LLVM the build expects, that you can collect a profile from a build of the same binary you intend to relink, and that your linker setup can consume the linker profile, since that is the part that has to integrate with your actual build.
Frequently asked questions
What is LLVM Propeller?
It is a profile-guided, relinking optimizer for warehouse-scale applications, built on LLVM, which provides a framework for whole-program optimizations for applications built with LLVM. It is developed by Google and licensed Apache-2.0.
What does generate_propeller_profiles produce?
Two files. The compiler profile, cc_profile.txt, is used by LLVM to guide optimizations and its format is described in the Propeller Profile Format document, and the linker profile, ld_profile.txt, carries the layout information used at link time. Both are generated from a profile of a running binary.
How do I build Propeller from source?
On Ubuntu 22.04 or newer, with Clang 19.0.0 or newer and the libelf-dev, libssl-dev and libzstd-dev packages for the CMake build. The repository provides both CMake and Bazel configurations, the CMake build needing CMake 3.24 or newer and the Bazel build needing Bazel 5.0 or newer.
How is Propeller different from ordinary profile-guided optimisation?
Ordinary PGO feeds a profile back to the compiler so it can make decisions within each translation unit, and the link is a fixed ordering of the results. Propeller computes a whole-program view from the profile of a complete binary and uses it at link time, so hot and cold code can be laid out together across the whole program.
Is LLVM Propeller ready to use in production?
There are no GitHub releases and the last push to the repository was on 2026-09-24, so you would be building from a branch. The repository also carries an ArtifactEvaluation directory for reproducing experimental results, and the project homepage points at a design document on a development branch, which places it in the research stage.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/google-llvm-propeller)Was this useful?