Here are two alternative codes (coded in Julia) that do essentially the same thing.
counter = 0
for i = myArray
counter = counter + 1
Do1(i)
Do2(counter)
end
and
for counter = 1:length(myArray)
i = myArray[counter]
Do1(i)
Do2(j)
end
What is the good practice? Which code is faster? Which code consumes less memory? Which code is less error prone? Why?
In julia, you can test this very easily:
function indexing(A)
si = 0
sA = zero(eltype(A))
for i = 1:length(A)
sA += A[i]
si += i
end
si, sA
end
function counter(A)
si = 0
sA = zero(eltype(A))
i = 0
for a in A
sA += a
si += (i += 1)
end
si, sA
end
function enumrt(A)
si = 0
sA = zero(eltype(A))
for (i, a) in enumerate(A)
sA += a
si += i
end
si, sA
end
A = rand(Float32, 10^8)
# Compile all the functions, including those needed to time things
indexing(A)
counter(A)
enumrt(A)
@time 1+1
# Test the timing
@time indexing(A)
@time counter(A)
@time enumrt(A)
Output:
elapsed time: 4.61e-6 seconds (80 bytes allocated)
elapsed time: 0.12948093 seconds (144 bytes allocated)
elapsed time: 0.191082557 seconds (144 bytes allocated)
elapsed time: 0.331076493 seconds (160 bytes allocated)
If you add @inbounds
annotations before every loop, then you get this:
elapsed time: 4.632e-6 seconds (80 bytes allocated)
elapsed time: 0.12512546 seconds (144 bytes allocated)
elapsed time: 0.12340103 seconds (144 bytes allocated)
elapsed time: 0.323285599 seconds (160 bytes allocated)
So the difference between the first two is really just the effectiveness of automatic bounds-checking removal. Finally, if you really want to dive into the details, you can inspect the generated machine code using @code_native indexing(A)
, or use @code_llvm
to inspect the LLVM IR (internal representation).
However, in some cases readability may be more important, and so the enumerate
approach is one I use often (but not in truly performance-critical code).
I don't actually know much about Julia, but the pattern is common enough that most (sane) languages have a built-in function to help you deal with such an indecision. In both Python and Julia, it's called enumerate
:
Return an iterator that yields
(i, x)
wherei
is an index starting at 1, andx
is the ith value from the given iterator. It’s useful when you need not only the valuesx
over which you are iterating, but also the indexi
of the iterations.
For your example, it would look like:
for (j, i) in enumerate(myArray)
Do1(i)
Do2(j)
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With