# Pgo

> Use when a C or C++ binary needs profile-guided optimization with GCC or Clang: instrumented or sampled profiles, merging, the two-stage build, or BOLT. Not for base flags: use gcc or clang.

- Skill: `majiayu000/pgo` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds add majiayu000/pgo`
- Raw SKILL.md: https://api.skillmd.com/api/skills/majiayu000/pgo/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: majiayu000 (https://skillmd.com/u/majiayu000)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/majiayu000/pgo

---


# Profile-guided optimization

## Contract

| Field | Bound contract |
|---|---|
| Trigger | The user asks how to apply PGO, how `-fprofile-generate` and `-fprofile-use` fit together, how to collect a representative profile, or how BOLT differs from PGO. |
| Authority | Read-only. The skill emits the build sequence, collection commands, and a measurement plan to chat; the user runs them. Rollback is not needed. No remote mutation. |
| Side effect | Chat output. |
| Done | A two-stage build sequence for the user's compiler is reported with the profile format named, the workload checklist is applied, and the speedup claim rests on a before-and-after measurement command rather than a number. |

## Inputs

- Compiler and version: required. GCC 16.2 or Clang 23.1.0 are current stable; each has its own profile format.
- A representative workload the instrumented binary can run: required.
- Build system: required for wiring; CMake shown below.
- Whether `perf` with branch sampling is available: required for SamplePGO and BOLT.
- Baseline flags already in use: required; PGO layers on `-O2` or `-O3`.

## Procedure

1. Decide whether PGO applies. Standard optimization comes first: `-O2` or `-O3`, and `-march=` for the deployment CPU. PGO pays off on code where static heuristics guess wrong: branch-heavy paths, many hot and cold functions, call-heavy code where inlining decisions matter. Do not promise a percentage; the gain is measured in step 7. Done when: the user knows PGO is a candidate and what will decide it.
2. Run the GCC flow. Instrument: `gcc -O2 -fprofile-generate -fprofile-dir=./pgo-data prog.c -o prog`. Run the workload; `.gcda` files land in `./pgo-data/`. Rebuild: `gcc -O2 -fprofile-use -fprofile-dir=./pgo-data -fprofile-correction prog.c -o prog`. Keep the same output name in both builds, because GCC derives the `.gcda` file name from the object name and a renamed second build reports `-Wmissing-profile`. `-fprofile-correction` reconciles counts from multi-threaded runs; include it. Done when: the three commands share one output name.
3. Run the Clang instrumented flow. Instrument: `clang -O2 -fprofile-instr-generate prog.c -o prog_instr`. Run the workload; it writes `default.profraw`, or per-process files with `LLVM_PROFILE_FILE="prog-%p.profraw"`. Merge: `llvm-profdata merge -output=prog.profdata *.profraw`. Rebuild: `clang -O2 -fprofile-instr-use=prog.profdata prog.c -o prog_pgo`. Weight workloads with `-weighted-input=3,a.profraw -weighted-input=1,b.profraw`; inspect with `llvm-profdata show --all-functions prog.profdata`; compare two profiles with `llvm-profdata overlap a.profdata b.profdata`. Done when: the merged `.profdata` exists and the rebuild command names it.
4. Add context-sensitive PGO when inlining decisions matter: take the stage-1 `.profdata`, build `clang -O2 -fprofile-use=stage1.profdata -fcs-profile-generate`, run the workload, merge the new `.profraw` files into `cs.profdata`, and build the final binary with `-fprofile-use=cs.profdata`. Done when: the second-stage profile feeds the final build.
5. Use sampling when instrumentation overhead is unacceptable. Build with `-fno-omit-frame-pointer`, record `perf record -b -e cycles:u ./prog < workload`, convert `perf script -F ip,brstack > perf.script`, then `llvm-profgen --binary=./prog --perfscript=perf.script --output=prog.profdata`, and rebuild with `-fprofile-sample-use=prog.profdata`. GCC's sampling counterpart is `-fauto-profile=<file>` with a profile converted from `perf` data. Done when: the sample profile feeds the rebuild.
6. Apply BOLT after PGO when instruction-cache layout is the remaining cost. Link with `-Wl,--emit-relocs`. Collect `perf record -e cycles:u -j any,u -o perf.data -- ./prog < workload`, convert `perf2bolt -p perf.data -o perf.fdata ./prog`, or instrument with `llvm-bolt prog -instrument -o prog.instr` and run it (profile lands in `/tmp/prof.fdata`). Optimize: `llvm-bolt prog -o prog.bolt -data=perf.fdata -reorder-blocks=ext-tsp -reorder-functions=cdsort -split-functions -split-all-cold -split-eh -dyno-stats`. GCC 8 and later need `-fno-reorder-blocks-and-partition` at compile time for BOLT. Done when: the BOLT binary exists.
7. Measure. `hyperfine --runs 5 "./prog_baseline < workload" "./prog_pgo < workload" --export-markdown results.md`, or `perf stat` on each binary. Report the measured ratio and the noise across runs; a PGO or BOLT build that does not beat the baseline on the deployment workload is discarded. Done when: the numbers come from the user's workload.
8. Wire CMake with two options, `PGO_INSTRUMENT` adding `-fprofile-instr-generate` to compile and link options and `PGO_USE` adding `-fprofile-instr-use=<path>` to both, driven by two configure-build cycles into separate build directories. Done when: the two build directories and the profile path are named.

## Failure and recovery

| Failure class | Behavior |
|---|---|
| Workload not representative | Apply the checklist: covers the hot paths, runs long enough to exercise them, weights production scenarios higher, excludes debug builds and one-off startup unless startup matters. A tiny synthetic benchmark trains the wrong layout. |
| `-Wmissing-profile` from GCC | The second build's object name differs from the first; use one output name. |
| `perf record -b` unsupported | The CPU or VM lacks branch sampling; use instrumented PGO instead of SamplePGO and BOLT. |
| `llvm-bolt` not installed | BOLT builds as an LLVM project (`-DLLVM_ENABLE_PROJECTS=bolt`); report it missing rather than substituting. |
| Profile format mismatch | `-fprofile-instr-*` and `-fprofile-generate`/`-fprofile-use` are different formats in Clang; do not mix them across stages. |

## Output

A chat report with the ordered build, run, merge, and rebuild commands for the user's compiler, the optional CS-PGO, SamplePGO, and BOLT steps when they apply, the workload checklist verdict, and the measurement command whose result decides whether the build ships.

