When compiling image@0.25.10 without default features we expected that to be a rather quick affair of 7 additional dependencies, the slowest being num-traits with a build dependency. Instead, it's slow enough for issues being opened in our own issue tracker.
We investigated further and, with perf, found ~12% of build time spent in consteval of which almost all samples fell into InterpCx::layout_of queries. That was curious since we use a few const tables but definitely not of extreme size nor complicated computation and really only with primitive types. Through instrumenting a custom compiler build (printf debugging the types being queried), we found this pattern at the top of the summary:
RDTSC count type
-------
2968652 48376 u8
2975486 75647 usize
3372234 2024 std::num::NonZero<usize>
3494159 65639 FnDef(DefId(2:705 ~ core[93c5]::f64::{impl#0}::to_bits), [])
6016422 107343 ()
7589954 145971 FnDef(DefId(21:947 ~ pxfm[d8cc]::common::fmla), [])
10072816 175205 i64
15194244 290049 i32
22053787 392617 u32
22284234 322397 bool
40392452 615785 u64
58157921 2110 std::alloc::Layout
179631693 2996946 f64
Since there are far fewer than 2 million (monomorphized) lines in the whole dependency tree, this points to layouts being evaluated highly redundantly—once per evaluation of at least some statements / expression parts rather than what we'd rather want once per unique definition in MIR.
Meta
rustc --version --verbose:
rustc 1.95.0 (59807616e 2026-04-14)
binary: rustc
commit-hash: 59807616e1fa2540724bfbac14d7976d7e4a3860
commit-date: 2026-04-14
host: x86_64-unknown-linux-gnu
release: 1.95.0
LLVM version: 22.1.2
Development History
In PR #156718 I tried the idea of making the queries themselves faster by special-casing primitive types, which have known layouts that can be pre-interned thus replacing a function call with a few moves. This did yield a 2% but ultimately a weird case to make when the problematic behavior is the repetition of queries themselves.
Further instrumentation with the source of each layout call revealed that the vast majority was coming from InterpCx::layout_of_local which is itself more curious due to locals already having a cache attached to it per local that apparently is not hit, most of the time. As pointed out (#156718 (comment)) the design would explicitly not work for small const functions called multiple times due to it being attached to the frame of a call, not more directly to a MIR body.
count source:type
------
164879 layout_of_local:i64
227240 layout_of_local:bool
270325 layout_of_local:u32
579206 layout_of_local:u64
2860447 layout_of_local:f64
When compiling
image@0.25.10without default features we expected that to be a rather quick affair of 7 additional dependencies, the slowest beingnum-traitswith a build dependency. Instead, it's slow enough for issues being opened in our own issue tracker.We investigated further and, with
perf, found ~12% of build time spent in consteval of which almost all samples fell intoInterpCx::layout_ofqueries. That was curious since we use a few const tables but definitely not of extreme size nor complicated computation and really only with primitive types. Through instrumenting a custom compiler build (printf debugging the types being queried), we found this pattern at the top of the summary:Since there are far fewer than 2 million (monomorphized) lines in the whole dependency tree, this points to layouts being evaluated highly redundantly—once per evaluation of at least some statements / expression parts rather than what we'd rather want once per unique definition in MIR.
Meta
rustc --version --verbose:Development History
In PR #156718 I tried the idea of making the queries themselves faster by special-casing primitive types, which have known layouts that can be pre-interned thus replacing a function call with a few moves. This did yield a 2% but ultimately a weird case to make when the problematic behavior is the repetition of queries themselves.
Further instrumentation with the source of each layout call revealed that the vast majority was coming from
InterpCx::layout_of_localwhich is itself more curious due to locals already having a cache attached to it per local that apparently is not hit, most of the time. As pointed out (#156718 (comment)) the design would explicitly not work for small const functions called multiple times due to it being attached to the frame of a call, not more directly to a MIR body.