以下是两个替代代码(在Julia中编码),它们完全相同。
counter = 0
for i = myArray
counter = counter + 1
Do1(i)
Do2(counter)
end
和
for counter = 1:length(myArray)
i = myArray[counter]
Do1(i)
Do2(j)
end
什么是好习惯?哪个代码更快?哪个代码消耗的内存较少?哪个代码不易出错?为什么呢?
答案 0 :(得分:12)
在朱莉娅,你可以很容易地测试这个:
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)
输出:
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)
如果您在每个循环之前添加@inbounds
注释,那么您会得到:
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)
所以前两个之间的区别实际上只是自动边界检查删除的有效性。最后,如果您真的想深入了解详细信息,可以使用@code_native indexing(A)
检查生成的机器代码,或使用@code_llvm
检查LLVM IR(内部表示)。
但是,在某些情况下,可读性可能更重要,因此enumerate
方法是我经常使用的方法(但不是真正的性能关键代码)。
答案 1 :(得分:7)
我实际上并不太了解朱莉娅,但这种模式很常见,大多数(理智)语言都有内置函数来帮助你处理这种犹豫不决。在Python和Julia中,它被称为enumerate
:
返回一个产生
(i, x)
的迭代器,其中i
是一个从1开始的索引,x
是来自给定迭代器的第i个值。当您不仅需要迭代的x
值,而且还需要迭代的索引i
时,它非常有用。
对于您的示例,它看起来像:
for (j, i) in enumerate(myArray)
Do1(i)
Do2(j)
end
答案 2 :(得分:-8)
对于编译语言(例如C),我期望一个体面/现代的编译器能够将两者转换为相同的可执行代码;所以唯一的区别是可读性/可维护性。
对于动态语言(例如Julia),我预计源代码的微小差异会因语言的低效率及其运行时间而相形见绌。所以唯一重要的区别是可读性/可维护性。
为了可读性/可维护性;后者对我来说似乎更清洁。