Slow compile times?


#1

I know rustc has a reputation for slow compile times, but I wanted to find out how my experiences compare to others.

I have a ~8,000 line Rust project that, on my little laptop, takes about a minute to compile on 1.22.1, and 3-4 minutes to compile on nightly. Incremental compilation may take that down 10% or so.

If I compile a C project of similar functionality and ~14,000 lines of code, it compiles in 6 seconds. And, most significantly, if I just touch a single file and recompile the project, it compiles in less than half a second.

Is this comparable to what other people are experiencing? My Rust project does use generics pretty heavily and uses macros to generate a lot of code, so that ~8,000 line statistic is misleading, but still, this seems very slow to me. I consider the nightly compiler basically unusable: especially on a project of this relatively small size, I have to be able to make small changes and test them very quickly. 4 minutes is bonkers.


#2

Which C compiler are you using? Not sure what the right magnitude difference should be (template heavy C++ code is probably a better comparison than C), but you can try running cargo check to do typechecking. This won’t help you run the program but can serve as a quick(er) compilation feedback loop. If you’ve tried it already, how long does that take?

There’s a way to make rustc spit out timing info per phase, so can probably jump down to that level to see where the bulk of the time is spent (my hunch is LLVM is a significant portion).


#3

That was with gcc. I actually just tried with clang, and it’s about twice as fast at 3 seconds.

cargo check is indeed a lot faster, at 60 seconds on the nightly compiler. Thanks for pointing that out.

So I did cargo rustc -- -Z time-passes with the nightly compiler, and the results are enlightening:

  time: 0.067	parsing
  time: 0.000	recursion limit
  time: 0.000	crate injection
  time: 0.000	plugin loading
  time: 0.000	plugin registration
  time: 5.917	expansion
  time: 0.000	maybe building test harness
  time: 0.008	maybe creating a macro crate
  time: 0.049	creating allocators
  time: 0.000	checking for inline asm in case the target doesn't support it
  time: 0.016	AST validation
  time: 0.386	name resolution
  time: 0.028	complete gated feature checking
  time: 0.199	lowering ast -> hir
  time: 0.072	early lint checks
  time: 0.449	indexing hir
  time: 0.008	attribute checking
  time: 0.000	load query result cache
  time: 0.034	lifetime resolution
  time: 0.000	looking for entry point
  time: 0.001	looking for plugin registrar
  time: 0.021	loop checking
  time: 0.003	static item recursion checking
  time: 0.035	stability checking
  time: 0.126	type collecting
  time: 0.001	outlives testing
  time: 0.004	impl wf inference
  time: 0.252	coherence checking
  time: 0.001	variance testing
  time: 0.568	wf checking
  time: 0.116	item-types checking
  time: 8.715	item-bodies checking
  time: 0.363	const checking
  time: 0.180	privacy checking
  time: 0.016	intrinsic checking
  time: 0.078	match checking
  time: 1.242	liveness checking
  time: 4.852	borrow checking
  time: 0.002	MIR borrow checking
  time: 0.001	MIR effect checking
  time: 0.029	death checking
  time: 0.000	unused lib feature checking
  time: 0.259	lint checking
  time: 0.000	resolving dependency formats
    time: 2.446	write metadata
    time: 123.392	translation item collection
    time: 0.027	codegen unit partitioning
    time: 0.011	llvm function passes [attalus0]
    time: 0.004	llvm module passes [attalus0]
    time: 0.020	llvm function passes [attalus1]
    time: 0.006	llvm module passes [attalus1]
    time: 0.262	codegen passes [attalus0]
    time: 0.007	llvm function passes [attalus2]
    time: 0.004	llvm module passes [attalus2]
    time: 0.170	codegen passes [attalus2]
    time: 0.020	llvm function passes [attalus15]
    time: 0.006	llvm module passes [attalus15]
    time: 0.020	llvm function passes [attalus3]
    time: 0.006	llvm module passes [attalus3]
    time: 0.581	codegen passes [attalus1]
    time: 0.418	codegen passes [attalus15]
    time: 0.009	llvm function passes [attalus4]
    time: 0.004	llvm module passes [attalus4]
    time: 0.145	codegen passes [attalus4]
    time: 0.016	llvm function passes [attalus6]
    time: 0.008	llvm module passes [attalus6]
    time: 0.520	codegen passes [attalus3]
    time: 0.013	llvm function passes [attalus5]
    time: 0.005	llvm module passes [attalus5]
    time: 0.125	codegen passes [attalus5]
    time: 0.018	llvm function passes [attalus7]
    time: 0.007	llvm module passes [attalus7]
    time: 0.527	codegen passes [attalus6]
    time: 0.015	llvm function passes [attalus8]
    time: 0.007	llvm module passes [attalus8]
    time: 0.648	codegen passes [attalus7]
    time: 0.444	codegen passes [attalus8]
    time: 0.088	llvm function passes [attalus9]
    time: 0.010	llvm module passes [attalus9]
    time: 0.021	llvm function passes [attalus10]
    time: 0.011	llvm module passes [attalus10]
    time: 0.018	llvm function passes [attalus11]
    time: 0.005	llvm module passes [attalus11]
    time: 0.327	codegen passes [attalus10]
    time: 0.017	llvm function passes [attalus12]
    time: 0.008	llvm module passes [attalus12]
    time: 0.605	codegen passes [attalus11]
    time: 0.021	llvm function passes [attalus13]
    time: 0.010	llvm module passes [attalus13]
    time: 0.008	llvm function passes [attalus14]
    time: 0.004	llvm module passes [attalus14]
    time: 4.505	translate to LLVM IR
    time: 0.030	assert dep graph
    time: 0.000	serialize dep graph
  time: 130.645	translation
    time: 0.697	codegen passes [attalus12]
    time: 0.421	codegen passes [attalus14]
    time: 0.945	codegen passes [attalus13]
    time: 2.279	codegen passes [attalus9]
  time: 5.782	LLVM passes
  time: 0.000	serialize work products
  time: 0.115	linking

It seems that a lot of time is being spent in “translation item collection,” and a search reveals lots of discussion about excessive times here for generic code (like this).

Ugh. Just a few weeks ago I had an issue that was giving me 20+ minute compile times. That one I reported and it was quickly fixed. But this current issue has been under discussion for a year, has not been resolved, and has just been reduced in priority.


#4

You’re way ahead with running time-passes.

I take it you have similar type layering code as in the github issue you linked? Curious if sprinkling a Box here and there would cut it down. But yeah, otherwise not sure what to suggest given this is a known issue.


#5

Well, there’s nothing in my code that chains calls in an obvious way like the linked issue. But my intuition is I’m winding up with a similar effect. Yes, I guess I could probably use trait objects in a couple places to address this. I’ll experiment with that. Thanks again.