我无法理解为什么分配是在广播分配期间发生的,其中涉及的所有操作本身都是广播的。
using TimerOutputs
to = TimerOutput()
function f(to::TimerOutput)
k = 100
n = 10000
a = zeros(Int, n)
b = zeros(Int, n)
c = falses(n)
d = falses(n)
@timeit to "a" rand!(a, 1:k)
@timeit to "b" rand!(b, 1:k)
@timeit to "c" c .= a .<= b
@timeit to "d" d .= c .& (b .!= 0)
end
f(to)
show(to; allocations=true, compact=true)
(请注意,
a, b, c, d
的顺序在各个表中实际上是随机的。)
Time Allocations
─────────────── ───────────────
Total measured: 111ms 12.4MiB
Section ncalls time %tot alloc %tot
───────────────────────────────────────────────────
a 1 28.9μs 42.4% 0.00B 0.0%
b 1 27.8μs 40.7% 0.00B 0.0%
d 1 7.71μs 11.3% 4.19KiB 50.0%
c 1 3.83μs 5.6% 4.19KiB 50.0%
───────────────────────────────────────────────────
我希望每一行都是非分配的;它们都使用广播操作写入预先分配的数组。我认为这相当于将结果分配给目的地的 for 循环。但是
c
和 d
分配来执行广播分配。
如果我将
a .<= b
替换为 a <= b
,c
的分配就会消失:
Time Allocations
─────────────── ───────────────
Total measured: 95.0ms 11.7MiB
Section ncalls time %tot alloc %tot
───────────────────────────────────────────────────
a 1 29.0μs 38.4% 0.00B 0.0%
b 1 28.5μs 37.7% 0.00B 0.0%
c 1 9.29μs 12.3% 0.00B 0.0%
d 1 8.71μs 11.5% 4.19KiB 100.0%
───────────────────────────────────────────────────
但是我无法弄清楚如何在不手动编写循环的情况下使
d
的分配消失:
@timeit to "d" for i in eachindex(a) # they all have the same indices; this is ok
d[i] = c[i] && (b != 0)
end
Time Allocations
─────────────── ───────────────
Total measured: 60.0ms 3.19MiB
Section ncalls time %tot alloc %tot
───────────────────────────────────────────────────
b 1 33.7μs 36.1% 0.00B - %
a 1 33.0μs 35.3% 0.00B - %
d 1 25.7μs 27.5% 0.00B - %
c 1 959ns 1.0% 0.00B - %
───────────────────────────────────────────────────
我在导致分配的广播版本中哪里出了问题?
我认为这只是时机的产物。使用 BenchmarkTools 这些都显示零分配:
using BenchmarkTools
let
k = 100
n = 10000
a = zeros(Int, n)
b = zeros(Int, n)
c = falses(n)
d = falses(n)
@btime rand!($a, 1:$k)
@btime rand!($b, 1:$k)
@btime $c .= $a .<= $b
@btime $d .= $c .& ($b .!= $0)
end;