How to Use cargo-llvm-lines to Understand Generic Code Bloat

`cargo-llvm-lines` analyzes your compiled binary to report the number of LLVM IR lines per function, allowing you to pinpoint exactly which generic implementations are causing code bloat.

cargo-llvm-lines analyzes your compiled binary to report the number of LLVM IR lines per function, allowing you to pinpoint exactly which generic implementations are causing code bloat. By filtering for functions with high line counts, you can identify specific generic type combinations that are inflating your binary size and decide whether to refactor them using specialization, trait objects, or manual monomorphization.

First, install the tool and run it on your project in release mode to generate a baseline report. The output lists functions sorted by size, making it easy to spot generic functions that have been instantiated many times with different types.

cargo install cargo-llvm-lines
cargo llvm-lines --release

Once you have the output, look for functions with high "lines" counts that appear multiple times with different type parameters. For example, if you see std::vec::Vec<T>::push appearing with dozens of different T types, it indicates your code is heavily monomorphizing vector operations. To get a more granular view of which crates or modules are contributing most to the bloat, use the --crates flag.

cargo llvm-lines --release --crates

If you identify a specific generic function causing issues, you can use the --filter flag to isolate it and see exactly which type instantiations are present. This helps you verify if the bloat is coming from your own code or a dependency.

cargo llvm-lines --release --filter "my_crate::my_generic_function"

When you find a problematic generic, the solution usually involves reducing the number of unique type instantiations. If the function is generic over a trait, consider using a trait object (Box<dyn Trait>) to trade a small runtime cost for a significant reduction in compile-time monomorphization. Alternatively, if the function is generic over a specific type like Vec<T>, you might refactor to use a single concrete type or introduce a wrapper that handles the generic logic once, rather than inlining it everywhere.

For dependencies causing bloat, you can often disable specific features in Cargo.toml that trigger unnecessary generic expansions. If the bloat is unavoidable due to complex generic logic, you can use #[inline] attributes strategically to prevent the compiler from generating separate LLVM IR for every instantiation, though this may increase compile times. The key is to use cargo-llvm-lines iteratively: make a change, run the tool again, and verify the line count drops before committing the refactor.