添加两个矩阵的Julia内存分配

Lin*_*don 8 in-place blas lapack julia

我很好奇为什么Julias实现矩阵添加似乎可以复制.下面是一个例子:

foo1=rand(1000,1000)
foo2=rand(1000,1000)
foo3=rand(1000,1000)

julia> @time foo1=foo2+foo3;
  0.001719 seconds (9 allocations: 7.630 MB)
julia> sizeof(foo1)/10^6
8.0
Run Code Online (Sandbox Code Playgroud)

分配的内存量与这些维度矩阵所需的内存大致相同.

看起来为了处理foo2 + foo3,内存被分配来存储结果,然后foo1通过引用分配给它.

这是否意味着对于大多数线性代数运算,我们需要直接调用BLAS和LAPACK函数来执行操作?

spe*_*on2 13

要了解这里发生了什么,让我们考虑foo1 = foo2 + foo3实际做了什么.

  • 首先评估foo2 + foo3.为此,它将分配一个新的临时数组来保存输出
  • 然后它将名称绑定foo1到这个新的临时数组,撤消您为预分配输出数组而付出的所有努力.

简而言之,您会看到内存使用情况与结果数组的内存使用情况有关,因为例程确实为该大小的数组分配了新内存.

以下是一些替代方案:

  • 写一个循环
  • 使用 broadcast!
  • 我们可以尝试做copy!(foo1, foo2+foo3),然后你预先分配的数组将被填充,但它仍将分配临时(见下文)
  • 原始版本发布在这里

这是这4个案例的一些代码

julia> function with_loop!(foo1, foo2, foo3)
           for i in eachindex(foo2)
              foo1[i] = foo2[i] + foo3[i]
           end
       end

julia> function with_broadcast!(foo1, foo2, foo3)
           broadcast!(+, foo1, foo2, foo3)
       end

julia> function with_copy!(foo1, foo2, foo3)
           copy!(foo1, foo2+foo3)
       end

julia> function original(foo1, foo2, foo3)
           foo1 = foo2 + foo3
       end
Run Code Online (Sandbox Code Playgroud)

现在让我们来看看这些功能吧

julia> for f in [:with_broadcast!, :with_loop!, :with_copy!, :original]
           @eval $f(foo1, foo2, foo3) # compile
           println("timing $f")
           @eval @time $f(foo1, foo2, foo3)
       end
timing with_broadcast!
  0.001787 seconds (5 allocations: 192 bytes)
timing with_loop!
  0.001783 seconds (4 allocations: 160 bytes)
timing with_copy!
  0.003604 seconds (9 allocations: 7.630 MB)
timing original
  0.002702 seconds (9 allocations: 7.630 MB, 97.91% gc time)
Run Code Online (Sandbox Code Playgroud)

你可以看到with_loop!broadcast!做同样的事情,两者都比其他人更快,更有效率.with_copy!并且original速度较慢并且使用更多内存.

一般来说,要做就地操作,我建议先写一个循环