Ramblings of an aging IT geek
← Ramblings of an aging IT geek
rust

dyn or not: i finally benchmarked it

A small criterion benchmark comparing static dispatch via generics against dynamic dispatch through trait objects in Rust, and what the numbers actually say.

Source code on a screen

The advice is always "prefer generics, the vtable lookup costs you". I'd repeated it myself without ever measuring it, which is a bad habit, so I sat down with criterion and a small hot loop to see how big the gap really is.

The workload was deliberately trivial: a trait with one method doing a tiny bit of arithmetic, called a million times. The generic version monomorphises and inlines; the &dyn Trait version goes through a vtable each call.

fn run_static<T: Op>(op: &T, xs: &[u64]) -> u64 {
    xs.iter().map(|&x| op.apply(x)).sum()
}

fn run_dyn(op: &dyn Op, xs: &[u64]) -> u64 {
    xs.iter().map(|&x| op.apply(x)).sum()
}

On my machine the generic path came out a touch over twice as fast for this pathological case. That sounds damning until you notice the absolute numbers are nanoseconds, and the gap collapses the moment the method does anything real, because the indirect call stops being the bottleneck.

So the rule survives, but with a caveat I now actually believe: reach for generics in genuine hot paths, and stop feeling guilty about dyn everywhere else. It buys you smaller binaries and saner compile times, and the cost is noise unless you've measured otherwise.