We all know `iter` is faster than `loop`, but why?

Oddly enough this code is much faster on the ARM as 32 bit code. Ten million elements:

test tests::bench_use_for   ... bench:  14,893,815 ns/iter (+/- 81,267)
test tests::bench_use_iter  ... bench:  12,013,362 ns/iter (+/- 71,831)
test tests::bench_use_while ... bench:  13,283,917 ns/iter (+/- 77,613)

But not so for a thousand elements:

test tests::bench_use_for   ... bench:       1,408 ns/iter (+/- 4)
test tests::bench_use_iter  ... bench:         942 ns/iter (+/- 2)
test tests::bench_use_while ... bench:       1,251 ns/iter (+/- 0)

But iter is not being so effective here.

The impact is usually second-order effects on things like optimizations. If the compiler doesn't know that a read is in-bounds, and thus control flow might stop, it's much harder for it to do optimizations like unrolling and vectorization. (LLVM, especially, is generally unwilling to optimize loops with more than one exit condition. IIRC GCC is a bit better about that.) And there's the general "more code is bad" (all else being equal) things like instruction cache size limits (which are hard to benchmark, because a microbenchmark will often fit in that cache).

Of course, whether this matters depends greatly on just how much code there is inside the loop. If you're json parsing a file in the loop, any difference will be completely invisible.

:100:

The usual link to read more about this:

2 Likes

I will note that these 3 all produce the same assembly under -C opt-level 3 on godbolt.org using 1.47 (stable):

(Note: I am a "new forum user" so the forum won't let me post 3 links. Copy and paste will have to do.)

  • iter: https://godbolt.org/z/59Ehrz
const VEC_SIZE: usize = 10000000;

fn use_iter(vec: &mut Vec<usize>) {
    vec.iter_mut().enumerate().for_each(|(i, v)| *v = i);
}

pub fn main() {
    let mut vec = vec![0; VEC_SIZE];

    use_iter(&mut vec);
}
  • for: https://godbolt.org/z/e4zWfj
const VEC_SIZE: usize = 10000000;

fn use_for(vec: &mut Vec<usize>) {
    for i in 0..vec.len() {
        vec[i] = i;
    }
}

pub fn main() {
    let mut vec = vec![0; VEC_SIZE];

    use_for(&mut vec);
}
  • while: https://godbolt.org/z/o5v1Mf
const VEC_SIZE: usize = 10000000;

fn use_while(vec: &mut Vec<usize>) {
    let mut i = 0;
    let len = vec.len();
    while i < len {
        vec[i] = i;
        i += 1;
    }
}

pub fn main() {
    let mut vec = vec![0; VEC_SIZE];

    use_while(&mut vec);
}
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.