如何避免 Julia 中的内存分配?

Ale*_*lex 3 julia

考虑以下对四个复杂矩阵进行操作的简单 Julia 代码:

n = 400

z = eye(Complex{Float64},n)
id = eye(Complex{Float64},n)
fc = map(x -> rand(Complex{Float64}), id)
cr = map(x -> rand(Complex{Float64}), id)

s = 0.1 + 0.1im

@time for j = 1:n
    for i = 1:n
        z[i,j] = id[i,j] - fc[i,j]^s * cr[i,j]
    end
end
Run Code Online (Sandbox Code Playgroud)

尽管所有变量都是预先分配的,但时间显示了几百万次内存分配:

0.072718 seconds (1.12 M allocations: 34.204 MB, 7.22% gc time)
Run Code Online (Sandbox Code Playgroud)

如何避免所有这些分配(和 GC)?

Abo*_*mar 5

高性能 Julia 代码的首要提示之一是避免使用全局变量。仅此一项就可以减少分配7次数。如果必须使用全局变量,提高其性能的一种方法是使用const. 使用const可以防止类型更改,但可以通过警告更改值。

在不使用函数的情况下考虑这个修改后的代码:

const n = 400

z = Array{Complex{Float64}}(n,n)
const id = eye(Complex{Float64},n)
const fc = map(x -> rand(Complex{Float64}), id)
const cr = map(x -> rand(Complex{Float64}), id)

const s = 0.1 + 0.1im

@time for j = 1:n
    for i = 1:n
            z[i,j] = id[i,j] - fc[i,j]^s * cr[i,j]
    end
end 
Run Code Online (Sandbox Code Playgroud)

时间显示了这个结果:

0.028882 seconds (160.00 k allocations: 4.883 MB)
Run Code Online (Sandbox Code Playgroud)

不仅分配次数变7 times少了,执行速度也变2.2 times快了。

现在让我们将第二个技巧应用于高性能 Julia 代码;把所有的东西都写在函数中。将上述代码写入函数z_mat(n)

function z_mat(n)
    z  = Array{Complex{Float64}}(n,n)
    id = eye(Complex{Float64},n)
    fc = map(x -> rand(Complex{Float64}), id)
    cr = map(x -> rand(Complex{Float64}), id)

    s = 1.0 + 1.0im

    @time for j = 1:n
        for i = 1:n
            z[i,j] = id[i,j] - fc[i,j]^s * cr[i,j]
        end
    end    
end
Run Code Online (Sandbox Code Playgroud)

和跑步

z_mat(40)
  0.000273 seconds
@time z_mat(400)
  0.027273 seconds
  0.032443 seconds (429 allocations: 9.779 MB)
Run Code Online (Sandbox Code Playgroud)

2610 times比整个函数的原始代码分配更少,因为循环单独执行零分配。