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).
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments