广播分配慢朱莉娅

cle*_*lex 3 performance julia

我有这样的事情(简单的例子):

using BenchmarkTools
function assign()
    e = zeros(100, 90000)
    e2 = ones(100) * 0.16
    e[:, 100:end] .= e2[:]
end
@benchmark assign()
Run Code Online (Sandbox Code Playgroud)

并且需要数千个时间步长。这给

BenchmarkTools.Trial: 
  memory estimate:  68.67 MiB
  allocs estimate:  6
  --------------
  minimum time:     16.080 ms (0.00% GC)
  median time:      27.811 ms (0.00% GC)
  mean time:        31.822 ms (12.31% GC)
  maximum time:     43.439 ms (27.66% GC)
  --------------
  samples:          158
  evals/sample:     1
Run Code Online (Sandbox Code Playgroud)

有没有更快的方法来做到这一点?

DNF*_*DNF 8

首先我会假设你的意思是

function assign1()
    e = zeros(100, 90000)
    e2 = ones(100) * 0.16
    e[:, 100:end] .= e2[:]
    return e  # <- important!
end
Run Code Online (Sandbox Code Playgroud)

否则您将不会返回e(!)的前 99 列:

julia> size(assign())
(100, 89901)
Run Code Online (Sandbox Code Playgroud)

其次,不要这样做:

e[:, 100:end] .= e2[:]
Run Code Online (Sandbox Code Playgroud)

e2[:]复制e2并分配它,但为什么呢?e2直接赋值即可:

e[:, 100:end] .= e2
Run Code Online (Sandbox Code Playgroud)

好的,但让我们尝试几个不同的版本。请注意,不需要制作e2向量,只需分配一个标量:

function assign2()
    e = zeros(100, 90000)
    e[:, 100:end] .= 0.16  # Just broadcast a scalar!
    return e
end

function assign3()
    e = fill(0.16, 100, 90000)  # use fill instead of writing all those zeros that you will throw away
    e[:, 1:99] .= 0
    return e
end

function assign4()
    # only write exactly the values you need!
    e = Matrix{Float64}(undef, 100, 90000)
    e[:, 1:99] .= 0
    e[:, 100:end] .= 0.16
    return e
end
Run Code Online (Sandbox Code Playgroud)

基准测试时间

julia> @btime assign1();
  14.550 ms (5 allocations: 68.67 MiB)

julia> @btime assign2();
  14.481 ms (2 allocations: 68.66 MiB)

julia> @btime assign3();
  9.636 ms (2 allocations: 68.66 MiB)

julia> @btime assign4();
  10.062 ms (2 allocations: 68.66 MiB)
Run Code Online (Sandbox Code Playgroud)

版本 1 和 2 同样快,但您会注意到有 2 个分配而不是 5 个,但是,当然,大分配占主导地位。

版本 3 和 4 更快,但不是很明显,但您会看到它避免了一些重复工作,例如将值写入矩阵两次。版本 3 是最快的,但不是很多,但是如果分配更加平衡,这种情况会发生变化,在这种情况下,版本 4 更快:

function assign3_()
    e = fill(0.16, 100, 90000)
    e[:, 1:44999] .= 0
    return e
end

function assign4_()
    e = Matrix{Float64}(undef, 100, 90000)
    e[:, 1:44999] .= 0
    e[:, 45000:end] .= 0.16
    return e
end

julia> @btime assign3_();
  11.576 ms (2 allocations: 68.66 MiB)

julia> @btime assign4_();
  8.658 ms (2 allocations: 68.66 MiB)
Run Code Online (Sandbox Code Playgroud)

教训是避免做不必要的工作。