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

时间:2016-02-17 19:19:51

标签: julia lapack blas in-place

我很好奇为什么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

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

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

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

3 个答案:

答案 0 :(得分:13)

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

  • 首先评估foo2 + foo3。为此,它将分配一个 new 临时数组来保存输出
  • 然后它会将名称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

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

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)

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

一般来说,要进行实地操作,我建议先写一个循环

答案 1 :(得分:2)

首先,阅读@ spencerlyon2的回答。另一种方法是使用Dahua Lin的包Devectorize.jl。它定义了@devec宏,它自动将向量(数组)表达式转换为循环代码。

在此示例中,我们将如下定义with_devec!(foo1,foo2,foo3)

julia> using Devectorize      # install with Pkg.add("Devectorize")

julia> with_devec!(foo1,foo2,foo3) = @devec foo1[:]=foo2+foo3

运行基准测试可以实现4个分配结果。

答案 2 :(得分:0)

您可以使用LinearAlgebra软件包中的axpy!函数。

using LinearAlgebra
julia> @time BLAS.axpy!(1., foo2, foo3)
  0.002187 seconds (4 allocations: 160 bytes)