我需要为一个巨大矩阵的所有元素添加一个标量。矩阵将尽可能大。在示例中,我将使用 2 GiB 的大小,但在我的实际计算中,它会大得多。
A = rand(2^14, 2^14)
如果我执行
A += 1.0
Julia 分配了额外的 2 GiB 内存。操作大约需要1s。我可以使用
for
环形:for jj = 1:size(A, 2), ii = 1:size(A, 1)
A[ii, jj] = A[ii, jj] + 1.0
end
这不会分配任何内存,但需要一分钟。这两种方法对我来说都不可行,因为第一种违反了内存限制,而第二种显然效率低下。对于逐元素乘法,有
scal!
,它使用 BLAS。有没有什么方法可以像使用 scal!
的乘法一样有效地执行加法? ?
最佳答案
@DSM 的回答很好。但是,这里还有很多事情我想另外说明。你的 for 循环很慢的原因是因为 A
是一个非常量全局变量,您的代码直接改变了该全局变量。由于A
是非常量的,代码要提防A
的可能性在循环执行期间的任何时候变成具有不同类型的不同值。代码必须查找 A
的类型和位置在循环的每次迭代中,动态调度表达式 A[ii, jj] = A[ii, jj] + 1.0
中的方法调用– 打给 getindex
, +
和 setindex!
, 所有这些都依赖于 A
的静态未知类型.只需在函数中完成这项工作,您就可以立即获得更好的性能:
julia> A = rand(2^10, 2^10);
julia> @time for jj = 1:size(A, 2), ii = 1:size(A, 1)
A[ii, jj] += 1
end
elapsed time: 0.288340785 seconds (84048040 bytes allocated, 15.59% gc time)
julia> function inc!(A)
for jj = 1:size(A, 2), ii = 1:size(A, 1)
A[ii, jj] += 1
end
end
inc! (generic function with 1 method)
julia> @time inc!(A)
elapsed time: 0.006076414 seconds (171336 bytes allocated)
julia> @time inc!(A)
elapsed time: 0.000888457 seconds (80 bytes allocated)
避免像这样的非常量全局变量是 Performance Tips 中的第一个建议。手册的部分。您可能还想仔细阅读本章的其余部分。
我们可以进一步提高
inc!
的性能使用 @inbounds
的函数注释以指示此代码不需要边界检查,并使用线性索引而不是二维索引:julia> function inc!(A)
@inbounds for i = 1:length(A)
A[i] += 1
end
end
inc! (generic function with 1 method)
julia> @time inc!(A)
elapsed time: 0.000637934 seconds (80 bytes allocated)
大部分加速来自
@inbounds
注释而不是线性索引,尽管这确实会提高一点速度。 @inbounds
然而,注释应该谨慎使用,并且只有在一个人确定索引不会超出范围并且性能是最重要的情况下才应该使用。如您所见,虽然存在额外的性能改进,但并不是压倒性的。大部分好处来自不直接改变全局变量。
关于math - 在 Julia 中有效地向矩阵添加标量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26656830/