| # Getting Started with BOLT | |
| ## Input Binary Requirements | |
| BOLT operates on X86-64 and AArch64 ELF binaries. At the minimum, the | |
| binaries should have an unstripped symbol table, and, to get maximum | |
| performance gains, they should be linked with relocations | |
| (`--emit-relocs` or `-q` linker flag). | |
| BOLT disassembles functions and reconstructs the control flow graph | |
| (CFG) before it runs optimizations. Since this is a nontrivial task, | |
| especially when indirect branches are present, we rely on certain | |
| heuristics to accomplish it. These heuristics have been tested on a code | |
| generated with Clang and GCC compilers. The main requirement for C/C++ | |
| code is not to rely on code layout properties, such as function pointer | |
| deltas. Assembly code can be processed too. Requirements for it include | |
| a clear separation of code and data, with data objects being placed into | |
| data sections/segments. If indirect jumps are used for intra-function | |
| control transfer (e.g., jump tables), the code patterns should be | |
| matching those generated by Clang/GCC. | |
| NOTE: BOLT is currently incompatible with the | |
| `-freorder-blocks-and-partition` compiler option. Since GCC8 enables | |
| this option by default, you have to explicitly disable it by adding | |
| `-fno-reorder-blocks-and-partition` flag if you are compiling with | |
| GCC8 or above. | |
| NOTE2: DWARF v5 is the new debugging format generated by the latest LLVM | |
| and GCC compilers. It offers several benefits over the previous DWARF | |
| v4. Currently, the support for v5 is a work in progress for BOLT. While | |
| you will be able to optimize binaries produced by the latest compilers, | |
| until the support is complete, you will not be able to update the debug | |
| info with `-update-debug-sections`. To temporarily work around the | |
| issue, we recommend compiling binaries with `-gdwarf-4` option that | |
| forces DWARF v4 output. | |
| PIE and .so support has been added recently. Please report bugs if you | |
| encounter any issues. | |
| ## Installation | |
| ### Docker Image | |
| You can build and use the docker image containing BOLT using our [docker | |
| file](https://github.com/llvm/llvm-project/blob/main/bolt/utils/docker/Dockerfile). | |
| Alternatively, you can build BOLT manually using the steps below. | |
| ### Manual Build | |
| BOLT heavily uses LLVM libraries, and by design, it is built as one of | |
| LLVM tools. The build process is not much different from a regular LLVM | |
| build. The following instructions are assuming that you are running | |
| under Linux. | |
| Start with cloning LLVM repo: | |
| ```console | |
| > git clone https://github.com/llvm/llvm-project.git | |
| > mkdir build | |
| > cd build | |
| > cmake -G Ninja ../llvm-project/llvm -DLLVM_TARGETS_TO_BUILD="X86;AArch64" -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_ENABLE_PROJECTS="bolt" | |
| > ninja bolt | |
| ``` | |
| `llvm-bolt` will be available under `bin/`. Add this directory to | |
| your path to ensure the rest of the commands in this tutorial work. | |
| ## Optimizing BOLT’s Performance | |
| BOLT runs many internal passes in parallel. If you foresee heavy usage | |
| of BOLT, you can improve the processing time by linking against one of | |
| memory allocation libraries with good support for concurrency. E.g. to | |
| use jemalloc: | |
| ```console | |
| > sudo yum install jemalloc-devel | |
| > LD_PRELOAD=/usr/lib64/libjemalloc.so llvm-bolt .... | |
| ``` | |
| Or if you rather use tcmalloc: | |
| ```console | |
| > sudo yum install gperftools-devel | |
| > LD_PRELOAD=/usr/lib64/libtcmalloc_minimal.so llvm-bolt .... | |
| ``` | |
| ## Usage | |
| For a complete practical guide of using BOLT see {doc}`OptimizingClang`. | |
| ### Step 0 | |
| In order to allow BOLT to re-arrange functions (in addition to | |
| re-arranging code within functions) in your program, it needs a little | |
| help from the linker. Add `--emit-relocs` to the final link step of | |
| your application. You can verify the presence of relocations by checking | |
| for `.rela.text` section in the binary. BOLT will also report if it | |
| detects relocations while processing the binary. | |
| ### Step 1: Collect Profile | |
| This step is different for different kinds of executables. If you can | |
| invoke your program to run on a representative input from a command | |
| line, then check **For Applications** section below. If your program | |
| typically runs as a server/service, then skip to **For Services** | |
| section. | |
| The version of `perf` command used for the following steps has to | |
| support `-F brstack` option. We recommend using `perf` version 4.5 | |
| or later. | |
| #### For Applications | |
| This assumes you can run your program from a command line with a typical | |
| input. In this case, simply prepend the command line invocation with | |
| `perf`: | |
| ```console | |
| $ perf record -e cycles:u -j any,u -o perf.data -- <executable> <args> ... | |
| ``` | |
| #### For Services | |
| Once you get the service deployed and warmed-up, it is time to collect | |
| perf data with LBR (branch information). The exact perf command to use | |
| will depend on the service. E.g., to collect the data for all processes | |
| running on the server for the next 3 minutes use: | |
| ```console | |
| $ perf record -e cycles:u -j any,u -a -o perf.data -- sleep 180 | |
| ``` | |
| Depending on the application, you may need more samples to be included | |
| with your profile. It’s hard to tell upfront what would be a sweet spot | |
| for your application. We recommend the profile to cover 1B instructions | |
| as reported by BOLT `-dyno-stats` option. If you need to increase the | |
| number of samples in the profile, you can either run the `sleep` | |
| command for longer and use `-F<N>` option with `perf` to increase | |
| sampling frequency. | |
| Note that for profile collection we recommend using cycle events and not | |
| `BR_INST_RETIRED.*`. Empirically we found it to produce better | |
| results. | |
| If the collection of a profile with branches is not available, e.g., | |
| when you run on a VM or on hardware that does not support it, then you | |
| can use only sample events, such as cycles. In this case, the quality of | |
| the profile information would not be as good, and performance gains with | |
| BOLT are expected to be lower. | |
| #### With instrumentation | |
| If perf record is not available to you, you may collect profile by first | |
| instrumenting the binary with BOLT and then running it. | |
| ```console | |
| llvm-bolt <executable> -instrument -o <instrumented-executable> | |
| ``` | |
| After you run instrumented-executable with the desired workload, its | |
| BOLT profile should be ready for you in `/tmp/prof.fdata` and you can | |
| skip **Step 2**. | |
| Run BOLT with the `-help` option and check the category “BOLT | |
| instrumentation options” for a quick reference on instrumentation knobs. | |
| ### Step 2: Convert Profile to BOLT Format | |
| NOTE: you can skip this step and feed `perf.data` directly to BOLT | |
| using experimental `-p perf.data` option. | |
| For this step, you will need `perf.data` file collected from the | |
| previous step and a copy of the binary that was running. The binary has | |
| to be either unstripped, or should have a symbol table intact (i.e., | |
| running `strip -g` is okay). | |
| Make sure `perf` is in your `PATH`, and execute `perf2bolt`: | |
| ```console | |
| $ perf2bolt -p perf.data -o perf.fdata <executable> | |
| ``` | |
| This command will aggregate branch data from `perf.data` and store it | |
| in a format that is both more compact and more resilient to binary | |
| modifications. | |
| If the profile was collected without brstacks, you will need to add `-ba` flag to | |
| the command line above. | |
| ### Step 3: Optimize with BOLT | |
| Once you have `perf.fdata` ready, you can use it for optimizations | |
| with BOLT. Assuming your environment is setup to include the right path, | |
| execute `llvm-bolt`: | |
| ```console | |
| $ llvm-bolt <executable> -o <executable>.bolt -data=perf.fdata -reorder-blocks=ext-tsp -reorder-functions=hfsort -split-functions -split-all-cold -split-eh -dyno-stats | |
| ``` | |
| If you do need an updated debug info, then add | |
| `-update-debug-sections` option to the command above. The processing | |
| time will be slightly longer. | |
| For a full list of options see `-help`/`-help-hidden` output. | |
| The input binary for this step does not have to 100% match the binary | |
| used for profile collection in **Step 1**. This could happen when you | |
| are doing active development, and the source code constantly changes, | |
| yet you want to benefit from profile-guided optimizations. However, | |
| since the binary is not precisely the same, the profile information | |
| could become invalid or stale, and BOLT will report the number of | |
| functions with a stale profile. The higher the number, the less | |
| performance improvement should be expected. Thus, it is crucial to | |
| update `.fdata` for release branches. | |
| ## Multiple Profiles | |
| Suppose your application can run in different modes, and you can | |
| generate multiple profiles for each one of them. To generate a single | |
| binary that can benefit all modes (assuming the profiles don’t | |
| contradict each other) you can use `merge-fdata` tool: | |
| ```console | |
| $ merge-fdata *.fdata > combined.fdata | |
| ``` | |
| Use `combined.fdata` for **Step 3** above to generate a universally | |
| optimized binary. | |
| ## Profile Formats | |
| See {doc}`profiles` for comprehensive documentation of all profile formats | |
| accepted by BOLT: perf.data, fdata, YAML, and pre-aggregated. | |
| ## License | |
| BOLT is licensed under the [Apache License v2.0 with LLVM | |
| Exceptions](https://github.com/llvm/llvm-project/blob/main/LICENSE.TXT). |