Description
@WaffleLapkin is preparing a PR to permit measuring memory usage of dyn upcasting to help gather data for concerns raised in #65991. The lang team is generally in favor of gathering data but we don't want to wait infinitely long. Opening this issue to track this work.
Instructions on how to measure trait upcasting effects on your codebase
You need a recent nightly compiler — 2023-06-14 or newer.
If you are using cargo
, run cargo clean && RUSTFLAGS="-Zprint-vtable-sizes" cargo check
. Note that cargo clean
is required — without it caching will silence the output. If you are using rustc
directly pass -Zprint-vtable-sizes
to it.
The output will contain lines like these:
print-vtable-sizes { "crate_name": "num_traits", "trait_name": "RefNum", "entries": "23", "entries_ignoring_upcasting": "13", "entries_for_upcasting": "10", "upcasting_cost_percent": "76.92307692307693" }
print-vtable-sizes { "crate_name": "num_traits", "trait_name": "NumAssignOps", "entries": "12", "entries_ignoring_upcasting": "8", "entries_for_upcasting": "4", "upcasting_cost_percent": "50" }
print-vtable-sizes { "crate_name": "num_traits", "trait_name": "NumOps", "entries": "12", "entries_ignoring_upcasting": "8", "entries_for_upcasting": "4", "upcasting_cost_percent": "50" }
print-vtable-sizes { "crate_name": "num_traits", "trait_name": "cast::ToPrimitive", "entries": "17", "entries_ignoring_upcasting": "17", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
print-vtable-sizes { "crate_name": "num_traits", "trait_name": "ops::inv::Inv", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
print-vtable-sizes { "crate_name": "num_traits", "trait_name": "ops::mul_add::MulAdd", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
print-vtable-sizes { "crate_name": "num_traits", "trait_name": "ops::mul_add::MulAddAssign", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
print-vtable-sizes { "crate_name": "num_traits", "trait_name": "pow::Pow", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
print-vtable-sizes
followed by a json describing vtable sizes. Json will contain the following fields:
crate_name
— name of the crate which defines the traitstrait_name
— path to the traitentries
— number of entries in a vtable with the current algorithm (i.e. with upcasting)entries_ignoring_upcasting
— number of entries in a vtable, as-if we did not have trait upcastingentries_for_upcasting
— number of entries in a vtable needed solely for upcasting (i.e.entries - entries_ignoring_upcasting
).upcasting_cost_percent
— cost of having upcasting in % relative to the number of entries without upcasting (i.e.entries_for_upcasting / entries_ignoring_upcasting * 100%
).
Lines are sorted by upcasting_cost_percent
, so that the biggest % change is first.
You can collect and analyze these stats however your want.
Some important notes:
- This includes private traits
- This includes traits that are not meant to be used as
dyn
and/or vtables of which are never actually instantiated - For traits with generics/associated types this can produce slightly misleading stats (only making the change bigger than it actually is)
- This does not include traits which are not object safe