Given
let store = [0, 1, 2, ...]
let M = store.count
Which looping construct is more optimal (say, in terms of energy use )?
for i in 0..<M {
use (store [i])
}
(0..<M).forEach {
use (store [$0])
}
for v in store {
use (v)
}
Given
let store = [0, 1, 2, ...]
let M = store.count
Which looping construct is more optimal (say, in terms of energy use )?
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
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.
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.