广播分配慢,Julia语言

3

我有这样一个简单的例子:

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

我需要对这个进行数千次时间步骤的操作。这会产生

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

有没有更快的方法来做这件事?


2
你应该使用 BenchmarkTools 进行更好的基准测试,并在参数中进行拼接。 - phipsgabler
@phipsgabler 你所说的 splicing 是什么意思? - clearseplex
2
与什么相比慢? - carstenbauer
@clearseplex 我本应该说“插值”。请参见这里 - phipsgabler
另一个性能测量工具:https://github.com/KristofferC/TimerOutputs.jl - phyatt
1个回答

8
首先,我会假定您的意思是:
function assign1()
    e = zeros(100, 90000)
    e2 = ones(100) * 0.16
    e[:, 100:end] .= e2[:]
    return e  # <- important!
end

否则你将无法返回e的前99列(!):

julia> size(assign())
(100, 89901)

其次,不要这样做:

e[:, 100:end] .= e2[:]

e2[:] 会复制e2并将其赋值给变量,但为什么?直接将e2赋值即可:

e[:, 100:end] .= e2

好的,但让我们尝试几个不同的版本。请注意,无需将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

进行基准测试的时间到了

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)

版本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)

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接