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.