In this lesson, you will learn how to identify and measure performance bottlenecks during macro expansion by leveraging internal compiler timing tools to optimize your build pipeline.
When you develop complex procedural macros, the time spent in the proc-macro crate can quickly become a significant portion of your total build time. Rust provides a built-in flag to track exactly how long each macro takes to expand, allowing you to move from guessing about performance to making data-driven optimizations.
Using -Z macro-backtrace and time-passes
To inspect the cost of your macros, you must use the nightly toolchain. The compiler provides a flag called -Z time-passes which outputs a detailed breakdown of the time spent in each compilation phase. When combined with -Z macro-backtrace, you gain granular insight into which specific macro calls are the slowest.
You can trigger this analysis by running your build with the following environment variables:
RUSTFLAGS="-Z time-passes" cargo build
The output will show a tree of execution times. You are looking specifically for the entries under expand or proc_macro_expand. If a specific macro name appears frequently or shows a high wall-clock time, that is your primary target for optimization.
Identifying Bottlenecks
Once you have identified a slow macro, look for these common performance traps:
- Excessive I/O: Reading external files or network resources inside the macro (which should be avoided in favor of build-time caches).
- Heavy Token Stream Iteration: Repeatedly re-parsing the entire
TokenStreamwhen partial parsing would suffice. - Over-allocation: Creating large intermediate data structures in memory instead of streaming the output directly to the
TokenStream.
If you identify a bottleneck, consider memoizing macro results using an external build script or simplifying the logic to reduce recursive expansion. By tracking the proc_macro_expand pass, you can verify if your refactoring actually reduced the compiler's workload.
Key Takeaway: Use the -Z time-passes compiler flag to pinpoint slow macro expansions and treat them as performance-critical code paths within your build pipeline.