什么是重置Julia数组为全零的最快方法?

11
假设我有一个已存在的数组,如下所示:
x = rand(4)

然后我想将x重置为零。我能避免执行x = zeros(4)吗?我担心内存分配。

3个回答

15

用一行代码实现的最佳方式是:

x .= zero(y)
或者
fill!(x, zero(y))

其中y是你想要的数字类型。这种方法之所以好用是因为它适用于各种情况。如果x是任何类型,只要y与该类型匹配(实际上,你可以使用y = x[1]),就可以使用此方法。

当我说任何类型时,我的意思是这也适用于奇怪的数字类型,比如SIUnits。如果你使用这个命令,一个包可以支持SIUnits而无需导入该包,因为它会将正确的单位应用于x值(只要所有操作都在单位上正确),而fill!(x, 0.0)会由于单位错误而出错(你也可以使用fill!(x, zeros(y)))。否则,你需要检查单位和其他各种事情。


1
实际上,fill!(x, 0.0) 可以自动转换并且似乎可以正常工作。无论如何,fill!(x, zero(eltype(x))) 更加连贯,因为结果必须与 x 的元素类型相同,因为它将被放回到同一数组中。 - David P. Sanders
1
fill!(x, 0) 的实现依赖于能够将0转换为正确的类型。在某些情况下,这是不可能的。例如,您无法将0(无量纲)转换为 0米。另一个例子是 RGB 值的向量,其中将标量转换为 RGB 值是不明确的。 - tholy
1
在Julia 0.5中,您还可以执行x .= 0。请注意,这三种方法实际上都在底层调用fill!函数。 - SGJ

3
您可以执行以下操作

fill!(x, 0.0)

这将用零覆盖内容。

这应该与仅使用for循环一样有效:

for i in 1:length(x)
    x[i] = 0.0
end

实际上,如果你执行@edit fill!(x, 0.0),你会发现这基本上就是它所做的(除了它使用@inbounds来提高效率)。


2

我觉得有些人提出了不错的替代方法,但没有人回答实际问题,“什么是最有效的方式(具体来说是指内存方面)”。

所以,这里就是答案。假设:

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)


PS. As a response to some comments below, the comparison of the above methods is purely intended to demonstrate the difference in approaches quantitatively rather than in generic abstract terms, so as to more fully address the question of "what's more efficient, by how much" etc.
It is not intended to imply that f1 or f4 are mutating functions; they are not, they return a reference to a new object in memory; i.e. they are there for completeness to demonstrate the bad alternatives that OP is presumably trying to avoid.

I just felt that a question about "efficiency" deserved some numbers to complement the suggestions offered by my colleagues :)


"f1"和"f4"并不会做你想要它们做的事情。 - mbauman
x[:]!都是语法(好吧,!是约定俗成的),用于原地操作,因此它们不会产生任何临时变量。所以它们相同并不奇怪。x = zero(x)x = zeros(length(x))是返回向量的向量函数,因此它们分配内存也就不足为奇了。我不知道你还想展示什么。 - Chris Rackauckas
@Matt:我知道f1(我现在已经编辑它以保持一致),但为什么是f4?能详细说明一下吗? - Tasos Papastylianou
@ChrisRackauckas 我并不是在暗示这会让人感到惊讶。这只是一个比较。OP担心内存分配问题,我展示了赋值和原地操作的两种情况以显示一些数字。至于x[:]fill!不会让人感到惊讶,因为它们没有任何临时变量,因此相同,这是真的,但也不是保证。我们可以尽情探讨理论,但基准测试能消除所有疑虑,不是吗? :) - Tasos Papastylianou
f1f4都不会修改函数的参数。它们都创建了一个全新的数组,并恰好将其命名为与函数参数相同的名称。但这只是在函数的本地作用域内更改了名称x指向的内容,对于调用该函数的数组没有任何影响。 - mbauman
@MattB. 哦,我明白了;是的,当然,这正是这里的意图。OP从未明确表示“我想要一个原地替代方案”,他/她只是说“我担心分配”,因此我通过比较探索了所有选项,以显示不同方法之间的差异(即变异 vs 新引用)。但无论如何感谢您指出这一点;我并不打算让它看起来像我在暗示那些非内存分配选项,所以澄清总是好的! :) - Tasos Papastylianou

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接