I have code of this format:
fn f(n: usize) -> Result<Vec<usize>, String> {
...
if some_runtime_condition {
return Err("failure".to_string()); // LINE A
}
let mut v = Vec::with_capacity(n);
while v.len() < n { // THE EXPENSIVE LOOP
... // complex logic building v
}
Ok(v)
}
and I found a 30-40% performance degradation when I changed line A to
return Err(format!("failure on {}", n)); // LINE A
Debugging (on release mode), I found that practically 0 time was spent formatting the error string, and instead the loop was 30-40% slower. As I understand, the only consequential difference between the two versions of line A (as far as the loop is concerned) is that I've allocated other data on the heap in the current scope with the format string.
I was able to solve the performance issue and keep the format string by changing my code to:
#[inline(never)]
fn the_loop(n) -> Vec<usize> {
let mut v = Vec::with_capacity(n);
while v.len() < n { // THE EXPENSIVE LOOP
... // complex logic building v
}
Ok(v)
}
fn f(n: usize) -> Result<Vec<usize>, String> {
...
if some_runtime_condition {
return Err(format!("failure on {}", n)); // LINE A
}
Ok(the_loop(n))
}
I hadn't expected that one heap allocation could hurt performance of different code, so why does this happen?
And is there any better way to tell Rust's compiler how to handle this (other than #[inline(never)])?