【发布时间】:2017-01-27 03:47:15
【问题描述】:
假设我有一个像这样的现有数组
x = rand(4)
然后我想将x 重置为零。我可以避免做x = zeros(4)。我担心内存分配。
【问题讨论】:
假设我有一个像这样的现有数组
x = rand(4)
然后我想将x 重置为零。我可以避免做x = zeros(4)。我担心内存分配。
【问题讨论】:
你可以的
fill!(x, 0.0)
这会用零覆盖内容。
不过,这应该与执行 for 循环一样有效:
for i in 1:length(x)
x[i] = 0.0
end
确实,如果你使用@edit fill!(x, 0.0),你会发现它基本上是这样做的(除了它使用@inbounds 来提高效率)。
【讨论】:
最好的办法是:
x .= zero(y)
或
fill!(x, zero(y))
y 是您想要的数字类型。这种方式之所以好的原因是它适用于所有情况。如果x 是任何类型,只要y 匹配该类型,就可以使用它(实际上,您可以使用y = x[1])。
当我指的是任何类型时,我的意思是这也适用于像 SIUnits 这样的奇数类型。如果你使用这个命令,一个包可以支持 SIUnits 而无需导入包,因为这会将正确的单位应用于x 值(只要它的所有操作在单位中都是正确的),而fill!(x, 0.0) 将由于单位错误而导致的错误(您也可以使用fill!(x, zeros(y))。否则,您必须检查单位和各种东西。
【讨论】:
fill!(x, 0.0) 进行了自动转换,并且显然可以正常工作。无论如何,fill!(x, zero(eltype(x))) 会更加连贯,因为结果必须与x 的元素类型具有相同的类型,因为它被放回同一个数组中。
fill!(x, 0) 依赖于能够将 0 转换为正确的类型。在某些情况下,这是无法做到的。例如,您不能将 0(无量纲)转换为 0meters。另一个是RGB 值的向量,其中将标量转换为 RGB 值是不明确的。
x .= 0。请注意,所有这三个方法实际上都在后台调用 fill!。
在我看来,人们似乎提出了很好的替代方法,但没有人解决“什么是最有效的方法(特别是在内存方面)”的实际问题。
原来如此。给定:
f1(x) = x = zeros(size(x));
f2(x) = x[:] = zero(x[1]);
f3(x) = fill!(x, zero(x[1]));
f4(x) = x = zero(x);
我们有:
julia> x=rand(1000,1000); @time (for i in 1_000_000_000; x=f1(x); end;)
0.000715 seconds (3 allocations: 7.629 MB)
julia> x=rand(1000,1000); @time (for i in 1_000_000_000; f2(x); end;)
0.000691 seconds (2 allocations: 32 bytes)
julia> x=rand(1000,1000); @time (for i in 1_000_000_000; f3(x); end;)
0.000702 seconds (2 allocations: 32 bytes)
julia> x=rand(1000,1000); @time (for i in 1_000_000_000; x=f4(x); end;)
0.000704 seconds (2 allocations: 7.629 MB)
f1 或 f4 是 mutating 函数;它们不是,它们返回对内存中新对象的引用;即他们在那里是为了完整地展示 OP 可能试图避免的 bad 替代方案。
我只是觉得关于“效率”的问题值得一些数字来补充我同事提出的建议:)
【讨论】:
f1 和 f4 不要做你想让他们做的事。
x[:] 和 ! 都是就地语法(嗯,! 是约定),因此它们不会有任何临时性。因此,它们是相同的也就不足为奇了。 x = zero(x) 和 x = zeros(length(x)) 是返回向量的向量函数,因此它们分配并不奇怪。我不知道你还想在这里展示什么。
x[:] 和fill! 并不奇怪,它们没有任何临时变量,因此它们是相同的,嗯,这是真的,但也不能保证。我们可以随心所欲地谈论理论,但基准可以消除所有疑问,不是吗? :)
f1 和 f4 都不会修改函数的参数。他们都创建了一个全新的数组,并且碰巧将其命名为与函数参数相同的名称。但这只是改变了名称 x 在函数的本地范围内指向的内容;它对调用函数的数组没有任何影响。