Optimal looping construct

Given

let store = [0, 1, 2, ...]
let M = store.count

Which looping construct is more optimal (say, in terms of energy use :slight_smile:)?

for i in 0..<M {
    use (store [i])
}
(0..<M).forEach {
    use (store [$0])
}
for v in store {
    use (v)
}

I would expect the optimizer to be able to transform all of them to essentially identical assembly

1 Like

This definitely should be true for arrays. But you should prefer for…in on the array. It’s the clearest and least error-prone.

forEach is implemented in terms of for…in so it should not be possible for it to be faster in any circumstances (except via overloading… but it’s not a customization point even that can’t happen generically).

It is possible for an iterator of a type to be slower than subscript access. For example, the iterator must take a copy. In practice noticeable differences are very unlikely, and iterators can also take advantage of knowledge of the collection to get a performance edge that looping over indexes and subscriptions can’t, so sometimes they can be faster.

5 Likes

Confirming what Ben says, for simple use cases like this, the optimizer can generate equivalent code for all of these loops (and lots of other ways to spell it as well). Some examples: Compiler Explorer

If you're not used to reading x86 assembly, the key bit is that each loop ends up looking something like:

.LBB1_2:
        mov     rdi, qword ptr [r15 + 8*rbx + 32] // load element from array
        add     rbx, 1                            // increment array index
        call    r14                               // call function on element
        cmp     r12, rbx                          // compare index with end of array
        jne     .LBB1_2                           // continue if elements remain

Interestingly the outliner figures out that the forEach and reduce implementations are equivalent and changes one to simply call the other.

6 Likes