WIP: All About BOLT Profile
Intro
BOLT (Binary Optimization and Layout Tool) is a post-link profile-guided code layout optimizer.
This post will attempt to cover several dimensions of BOLT profiling:
- profiling workload selection,
- profiling support in software and hardware,
- profile types and formats,
- profile lifecycle and handling in BOLT tools,
- real-world usage scenarios.
The profile plays a crucial role in its effectiveness. BOLT’s raison d’être is that it matches binary profile to the binary directly, thus augmenting addressing the semantical gap of compiler-based PGO. Compiler works with a program representation
In compiler PGO, the profile has to describe program execution at program representation level that compiler manipulates a higher level (source or IR), which causes reduced profile accuracy thus making the profile inaccurate when it reaches the low level representation. Late (CS IR) and flow/context-sensitive profiles reduce the gap but don’t close it.
BOLT is a form of PGO (profile-guided optimizations) which has the advantage of identity profile mapping: the profile is collected from and mapped to the same semantical level
that works directly on executables which improves execution efficiency through profile-guided code layout optimizations. and which implements profile-guided code layout optimizations , a form of profile-guided optimizations (PGO). leveraging post-link optimizations that imp is a binary optimization and layout tool, which is a form of profile-guided optimization (PGO)
This post attempts to cover the profile lifecycle, explain its handling in BOLT, and answer various practical questions.
In the end, I include ideas improving BOLT’s ease of use through profile collection domain and how it can be extended to make better use of existing and extra profiles.
This document is not assisted by AI, so all mistakes, typos and inaccuracies are my own.
Getting the profile
This paragraph is about what should be profiled and how.
Workload selection
Sampling
IP
Branch stack
Tracing
HW (ETM/PT)
SW
Instrumentation
Workload selection
The very first question that needs to be answered when using BOLT is what needs to be profiled. The profile
Do’s and dont’s
Do profile your actual workload
works best results when optimization works best when the profile comes from the same workload that is going to be
This recommendation extends to
(or benchmarks) – no train/test split.
Don’t profile tests.
Tests by design exercise corner cases – code paths that are not exercised often. Including them in the profile bloats hot code and
Server profiling
GWP, Strobelight, Perforator
Client profiling
Compilers and interpreters
Clang: hello world, clang-bolt. Flang. MLIR?
CPython: pyperformance.
Rust: llvm, cargo-pgo
Android
Gentoo
Hardware profiling
Sampling events
- Events (cycles, taken branches, calls/rets)
- PEBS, skid
- Coverage vs accuracy
Branch stacks
- No stacks: basic IP sampling
- 1-deep: SPE
- Fixed depth: LBR/BRBE, which branches
- Synthesized depth: PT/ETM and conversion (itrace)
- Extra information: branch type, mispredictions, latency
Augmenting
- Imputting
- Infer-fall-throughs
Extra profiles
- Memory
Sanitizing hardware profiles
- Sample profile quality, mismatching traces
- SKL LBR erratum: duplicate LBR TOS, BAT mode
- ETM pid misattribution, heatmap
Software-based profiling
Instrumentation
- Options
- Sections and in-memory metadata
Tracing
External profiles
Profile conversion and profile types
Perf2bolt
Parallel processing and memory
Profile types
Unsymbolized
- Perf data
- Perfscript
- Pre-aggregated
Symbolized
- fdata
- YAML
- symbols
Input
Perf data
- Simpleperf
- Memory profile
Perf script
Pre-aggregated
- eBPF
- Aggregation in SQL
- Multi-DSO
AutoFDO/CSSPGO profiles
Function order and selective instrumentation
Output
Unsymbolized
- Perfscript
- Pre-aggregated
Symbolized
- fdata: legacy + memory profile
- YAML: rich + stale matching
- merge-data
Profile reading and attaching
Steps
- Parsing: lite mode and mayHaveProfile
- PreCFG: indirect control flow
- CFG
Reading
- Traces
- DataAggregator/DataReader classes
Attaching
- External fall-throughs, traces/returns
- Fix-block-counts
- Basic samples/MCF
Profile staleness
Assessing profile, its quantity and quality
Inspecting
heatmap
- unsymbolized
- PGO and BOLT evaluation
- perf2bolt mode
- Working set size
print-cfg
- edge filtering
dump-cg
Quantitative evaluation
- Profile density
- Density thresholds and function coverage heuristics
Profile quality vs flow graphs
- Continuity
- Flow conservation
- Basic samples
- MCF, edge overlap, TSP score
Real world concerns
Stale profile matching
- YAML metadata explained
- Knobs
- Pseudo probe matching
- Cross-ISA matching
- Inference
Comparing and representativeness
- boltdiff
Profile representativeness
For PGO systems, the following flowchart shows the relationship between “optimization” (input) and “production” (actual) profiles.
-> Input/Optimization Profile
|
v
Input Program -> PGO/BOLT -> Optimized Program
|
v
Actual/Production Profile
To be practical, BOLT and AutoFDO/CSSPGO are designed to work with profiles captured from production usage, thus the general flow looks like the following:
source: ... --- old --- new
\ \
v v
binary: previous current
\ ^
v /
profile: production
AutoFDO/CSSPGO can consume production profile collected directly from the previous binary build, thanks to source/IR matching making it easier to tolerate source drift and profile staleness.
In case of BOLT, in theory it’s possible to use the profile collected from the previous binary build but interaction with refreshed PGO makes it impractical, so that a flow with separate no-BOLT binary yields better resulting performance:
source: ... --- old --- new
\ \
v v
binary: previous current
\ ^
v /
profile: production
For compiler PGO, as the profile is matched to source/IR level, later stage optimizations do not inhibit its ability to consume the profile from drastically different binary. However, compiler PGO is better at tolerating source drift and profile staleness compared to because of slower pace of change compared to waterfall changes in the binary an important distinction between BOLT and compiler PGO is the ability to tolerate staleness. AutoFDO/CSSPGO are designed to work directly with production profile collected from the previous workload build, tolerating source drift and profile staleness:
Unlike AutoFDO/CSSPGO that are designed to work with the profile collected from an actual (“production”) workload execution, BOLT profile typically comes from separate build (no-bolt or pre-bolt) running with a subset of inputs/traffic (“dedicated profiling tier”). This creates a split between the profile that’s used for optimization (opt profile) and actual (production) usage.
- Opt/prod profiles: in case of BOLT, the profile typically comes from separate binary build (no-BOLT/pre-BOLT) running on a subset of inputs/traffic, ideally
- Optimization profile: the profile that’s used to optimize the binary.
- Production profile: profile collected from the workload execution on real inputs.
- Partition score
Continuous profile
- YAML BAT
- Continuous profile as secondary
Interaction with PGO
- Zero-gap profiling
- BOLT-compatible AutoFDO
- Continuous BOLT and profiling optimized binaries
- CSSPGO/pseudo probes
Profiling evolution
Instrumentation
BOLT instrumentation can be improved with the following changes:
- Unblocking continuous profiling by producing YAML profile instead of fdata.
Explicit fdata->YAML conversion step is currently required to replicate continuous profiling with instrumentation. This is cumbersome as this step requires either preserving or rebuilding no-BOLT binary and can’t be done without it. Decoupling instrumentation from fdata profile and adding support for producing YAML profile with hashes, perhaps in a way similar to BAT, would address this user experience gap.
- Reducing space overheads in multi-pid scenario by switching to offline merging and symbolization.
fdata profiles are highly redundant with respect to function names. For instance, BOLT-instrumented Clang when compiling LLVM sources as training data has been observed to produce many multi-GB fdata files that can fill up the disk. One solution is dumping raw counters map and deferring symbolization to an offline step after merging multiple profiles. This is somewhat similar to llvm-profdata merge.
Continuous BOLT
- Via driver
- In compiler/linker
Dynamic BOLT
- OCOLOS
- Reoptimization
Multi-BOLT
- Cross-DSO inlining
Context-sensitive BOLT
- Inlining
- Data-flow optimizations
Runtime BOLT
- TSX