If you want to understand why specific futures are large, you may be interested in the unstable compiler option -Zprint-type-sizes
, which prints a report of the size and fields of every type in your crate — including the autogenerated types for closures and futures. I recently contributed some improvements to the feature so that it prints more details useful for analyzing futures. For example, if I run it on a part of my game project,
cargo +nightly rustc --lib -p all-is-cubes-content -- -Zprint-type-sizes
I get a report including this future:
type: `{async block@all-is-cubes-content/src/template.rs:283:18: 290:10}`: 4920 bytes, alignment: 8 bytes
discriminant: 1 bytes
variant `Unresumed`: 4913 bytes
upvar `.ingredients__parameters`: 32 bytes, offset: 0 bytes, alignment: 8 bytes
upvar `.progress`: 56 bytes
padding: 4825 bytes
upvar `.ingredients__template`: 1 bytes, alignment: 1 bytes
variant `Suspend0`: 4912 bytes
upvar `.ingredients__parameters`: 32 bytes, offset: 0 bytes, alignment: 8 bytes
upvar `.progress`: 56 bytes
local `.__awaitee`: 4824 bytes, type: {async fn body of template::UniverseTemplate::build<NoTime>()}
padding: 1 bytes
upvar `.ingredients__template`: 1 bytes, alignment: 1 bytes
variant `Returned`: 4913 bytes
upvar `.ingredients__parameters`: 32 bytes, offset: 0 bytes, alignment: 8 bytes
upvar `.progress`: 56 bytes
padding: 4825 bytes
upvar `.ingredients__template`: 1 bytes, alignment: 1 bytes
variant `Panicked`: 4913 bytes
upvar `.ingredients__parameters`: 32 bytes, offset: 0 bytes, alignment: 8 bytes
upvar `.progress`: 56 bytes
padding: 4825 bytes
upvar `.ingredients__template`: 1 bytes, alignment: 1 bytes
end padding: 6 bytes
Each “variant” of this type is one of the states the future can be in, and each Suspend*
variant is an await
point; the __awaitee
(not a real variable name, but one filled in for the benefit of debuggers and such) is the future that's being await
ed. So, in this case, we learn that the async
block at line 283 made a future that is 4920 bytes, but most of that, 4825 bytes, is the future of the UniverseTemplate::build()
function. Therefore, we should ignore this function as not very substantial, and go take a closer look at build()
.
For stack frames in general, Clippy has the large_stack_frames
lint, but it's very imprecise because it doesn't know when code generation figures out that two stack values can occupy the same space because they are never in use at the same time — it just adds up every variable/temporary in the function. I don't know of a more precise tool for analyzing stack frames.