Function side effects and optimizations

From the recent post in the other thread:

 while i < s.len() {

That's idiomatic Rust; the compiler can very easily see that optimization opportunity. It should be done this way.

Can someone explain in some detail why that works in Rust, e.g. it makes no sense to declare a variable let l = s.len() and use that in the loop?

It is quite obvious, that it works when the method is inlined. But what is with a non inlined function and side effects -- e.g. when self is mutable, or when the parameter list is not empty? I assume that Rust's has an advantage for optimizations, as it typically forbids use of global state variables? For other languages, things are not that obvious, I can remember a case some years ago (Nim), where expressions like x = tiny_func() + 2 * tiny_func() resulted actually in two function calls. That time I even tried Nim's efect system, to indicate that the called function does not change global state, but that did not avoid the multiple function calls.

We have discussed this in this thread

It turns out that this optimization is done by LLVM, and Rust just give LLVM the hint that whether a function has side effects.

2 Likes

Note that, for slice lengths in particular, that length call isn't really a call, exactly.

As an implementation detail (you can't rely on it for soundness or correctness, just perf), a &[T] is passed to a function as a NonNull<T> for the address and a usize for the length. So once it's in LLVM, there's actually already a single variable for the slice length (that parameter) and thus you don't need to make a variable for it yourself. (Of course, if you'd like a variable for it you can do that and it doesn't make anything worse, but you don't need it.)

For non-slice things like Vec::len, though, that's not the case so the tradeoffs can be a bit different.

1 Like