I had a unit test that was running too slowly to be able to run it in debug mode (it completed instantly in release), so I started using perf to try and see what the bottlenecks were... and I was surprised.
Rust uses functions for a bunch of core operations that would be directly implemented by the compiler in C/C++. This results in some absurdities, like this code causing 4 billion function calls:
for i in 0..u32::MAX { ... }
Which if you benchmark in a debug build you will find:
callq core::iter::range::<impl core::iter::traits::iterator::Iterator for core::ops::range::Range<A>>::next
Leading to the following workaround:
let mut i = 0;
loop {
if i >= u32::MAX {
break;
}
// ...
i += 1;
}
There are a large number of other functions like this, like: core::ptr::mut_ptr::<impl *mut T>::add
, core::ptr::write
, core::ptr::drop_in_place
, core::cell::UnsafeCell<T>::get
.
AFAIK none of these should ever compile to more than a single instruction, and they are primitive enough that I don't think any reasonable user should really expect to step inside them.
Moreover, while I can write my own Cell
with a get
that is always inlined, I can't write an UnsafeCell
replacement because it is special cased inside the compiler. Even if I could, most users are using a ton of third party libraries where the std types are ubiquitous.
I can appreciate debug builds are meant to be easier to debug, but these seem to substantially slow down debug builds for no debuggability benefit. Thoughts?