像这样的问题通常的答案是:“取决于情况”。例如,您要创建什么大小的数组?数组的元素类型是什么?
但如果您只是想得到一种启发式方法,为什么不运行一个简单的速度测试呢?例如,以下代码片段:
function f1(N::Int)
x = Array(Int, N)
for n = 1:N
x[n] = n
end
return(x)
end
function f2(N::Int)
x = Array(Int, 0)
for n = 1:N
push!(x, n)
end
return(x)
end
f1(2)
f2(2)
N = 5000000000
@time f1(N)
@time f2(N)
建议使用
push!
比预分配慢约6倍。如果您使用
append!
添加较大的块,则乘数几乎肯定会更少。
在解释这些数字时,要抵制“什么?6倍慢!”的本能反应。这个数字需要放在整个程序/函数/子例程中构建数组的重要性的背景下。例如,如果数组构建仅占您例程运行时间的1%(对于大多数典型例程,数组构建将远远少于1%),则如果例程运行100秒,则花费1秒钟构建数组。将其乘以6得到6秒。99秒+6秒=105秒。因此,使用
push!
而不是预分配将使您整个程序的运行时间增加5%。除非您从事高频交易,否则您可能不会关心。
对于我自己来说,我的通常规则是:如果可以轻松地预先分配空间,则进行预分配。但是,如果使用
push!
可以使程序编写更加容易,减少引入错误的可能性以及避免预先确定适当数组大小时出现的混乱,则毫不犹豫地使用
push!
。
最后注意:如果您想查看
push!
的具体工作原理,您需要深入了解C例程,因为
julia source只是一个
ccall
的包装。
更新: 在评论中,OP质疑了push!
和类似于array(end + 1)= n
的MATLAB操作之间的区别。我最近没有编写MATLAB代码,但是我在我的计算机上保留了一份副本,因为所有旧论文的代码都是用MATLAB编写的。 我当前的版本是R2014a。 我的理解是,在这个版本的MATLAB中,向数组末尾添加元素将重新分配整个数组。相比之下,Julia中的push!
,据我所知,像.NET中的列表一样工作。向向量分配的内存会随着向量大小的增长而动态地增加块。这大大减少了需要执行的重新分配量,尽管我理解仍然需要一些重新分配(我很乐意在这一点上进行更正)。因此,push!
应该比在Matlab中添加到数组要快得多。因此,我们可以运行以下MATLAB代码:
N = 10000000;
tic
x = ones(N, 1);
for n = 1:N
x(n) = n;
end
toc
N = 10000000;
tic
x = [];
for n = 1:N
x(end+1) = n;
end
toc
我得到:
Elapsed time is 0.407288 seconds.
Elapsed time is 1.802845 seconds.
所以,大约是5倍的减速。鉴于在计时方法中应用了极端的非严格性,人们可能会认为这与Julia情况相同。但是等一下,如果我们使用 N = 10000000
在Julia中重新运行这个练习,计时为0.01和0.07秒。这些数字数量级上的巨大差异使我对声称实际发生了什么以及是否可以将MATLAB中的5倍减速与Julia中的6倍减速进行比较感到非常紧张。基本上,我现在已经超出了我的能力范围。也许了解MATLAB在幕后实际做了什么的人可以提供更多洞见。关于Julia,我不是很擅长C编程,所以我怀疑通过查看源代码(与MATLAB不同,Julia的源代码是公开的)可以获得更多见解。
append!()
/push!()
的基准测试。 - aberdysh