索引与赋值与view、reshape配合时的性能差异

遇到了几个有意思的现象

  • 使用 view 时, .= 赋值快于 =
  • 不用 view 时, .= 赋值速度与 = 几乎一样
  • 使用 view 时,UnitRange{Int64} 索引快于 StepRange{Int64, Int64} 索引
  • 使用 view 时,配合 reshape 比不配合时慢
using BenchmarkTools

D1,D2,D3,S = 64, 512, 128, 1;
x = reshape(collect(1:D1*D2*D3), D1, D2, D3);

# case one
n = length(1:S:D2)
s = (D1, n, D3)
y1 = Array{Int}(undef, s);
y2 = Array{Int}(undef, s);
y3 = Array{Int}(undef, s);

y4 = Array{Int}(undef, s);
y5 = Array{Int}(undef, s);
y6 = Array{Int}(undef, s);

begin
    print( 1,": ");@btime y1[:,:,:] .= view(x, 1:1:D1, 1:S:D2, 1:1:D3); # 1: 66.939 ms (11 allocations: 640 bytes)
    print( 2,": ");@btime y1[:,:,:]  = view(x, 1:1:D1, 1:S:D2, 1:1:D3); # 2: 74.860 ms (6 allocations: 272 bytes)
    print( 3,": ");@btime y2[:,:,:] .= view(x, 1:D1,   1:S:D2, 1:D3);   # 3: 29.010 ms (11 allocations: 592 bytes)
    print( 4,": ");@btime y2[:,:,:]  = view(x, 1:D1,   1:S:D2, 1:D3);   # 4: 34.652 ms (6 allocations: 256 bytes)
    print( 5,": ");@btime y3[:,:,:] .= view(x, :,      1:S:D2, :);      # 5: 28.824 ms (18 allocations: 784 bytes)
    print( 6,": ");@btime y3[:,:,:]  = view(x, :,      1:S:D2, :);      # 6: 34.123 ms (13 allocations: 480 bytes)
    
    print( 7,": ");@btime y4[:,:,:] .= x[1:1:D1, 1:S:D2, 1:1:D3];       # 7:  17.476 ms (12 allocations: 32.00 MiB)
    print( 8,": ");@btime y4[:,:,:]  = x[1:1:D1, 1:S:D2, 1:1:D3];       # 8:  17.701 ms (7 allocations: 32.00 MiB)
    print( 9,": ");@btime y5[:,:,:] .= x[1:D1,   1:S:D2, 1:D3];         # 9:  17.681 ms (12 allocations: 32.00 MiB)
    print(10, ":");@btime y5[:,:,:]  = x[1:D1,   1:S:D2, 1:D3];         # 10: 17.322 ms (7 allocations: 32.00 MiB)
    print(11, ":");@btime y6[:,:,:] .= x[:,      1:S:D2, :];            # 11: 17.534 ms (18 allocations: 32.00 MiB)
    print(12, ":");@btime y6[:,:,:]  = x[:,      1:S:D2, :];            # 12: 17.855 ms (13 allocations: 32.00 MiB)
    return nothing
end

# case two
n = length(1:S:D2)
s = (D1 * n, D3)
y1 = Array{Int}(undef, s);
y2 = Array{Int}(undef, s);
y3 = Array{Int}(undef, s);

y4 = Array{Int}(undef, s);
y5 = Array{Int}(undef, s);
y6 = Array{Int}(undef, s);

begin
    print(13,":");@btime y1[:,:] .= reshape(view(x, 1:1:D1, 1:S:D2, 1:1:D3), s); # 13: 71.735 ms (7 allocations: 448 bytes)
    print(14,":");@btime y1[:,:]  = reshape(view(x, 1:1:D1, 1:S:D2, 1:1:D3), s); # 14: 78.502 ms (7 allocations: 448 bytes)
    print(15,":");@btime y2[:,:] .= reshape(view(x, 1:D1,   1:S:D2, 1:D3)  , s); # 15: 32.483 ms (7 allocations: 416 bytes)
    print(16,":");@btime y2[:,:]  = reshape(view(x, 1:D1,   1:S:D2, 1:D3)  , s); # 16: 40.632 ms (7 allocations: 416 bytes)
    print(17,":");@btime y3[:,:] .= reshape(view(x, :,      1:S:D2, :)     , s); # 17: 32.066 ms (14 allocations: 624 bytes)
    print(18,":");@btime y3[:,:]  = reshape(view(x, :,      1:S:D2, :)     , s); # 18: 38.293 ms (14 allocations: 624 bytes)
    
    print(19,":");@btime y4[:,:] .= reshape(x[1:1:D1, 1:S:D2, 1:1:D3], s);       # 19: 17.811 ms (9 allocations: 32.00 MiB)
    print(20,":");@btime y4[:,:]  = reshape(x[1:1:D1, 1:S:D2, 1:1:D3], s);       # 20: 17.620 ms (9 allocations: 32.00 MiB)
    print(21,":");@btime y5[:,:] .= reshape(x[1:D1,   1:S:D2, 1:D3]  , s);       # 21: 17.704 ms (9 allocations: 32.00 MiB)
    print(22,":");@btime y5[:,:]  = reshape(x[1:D1,   1:S:D2, 1:D3]  , s);       # 22: 17.608 ms (9 allocations: 32.00 MiB)
    print(23,":");@btime y6[:,:] .= reshape(x[:,      1:S:D2, :]     , s);       # 23: 17.619 ms (15 allocations: 32.00 MiB)
    print(24,":");@btime y6[:,:]  = reshape(x[:,      1:S:D2, :]     , s);       # 24: 17.645 ms (15 allocations: 32.00 MiB)
    return nothing
end

【A】从(1,2)、(3,4)、(5,6)、(13,14),(15,16)、(17,18)这些输出对中,可以看出:

  • 使用 view 时, .= 赋值快于 =

【B】从(7,8)、(9,10)、(11,12)、(19,20)、(21,22)、(23,24)这些输出对中,可以看出

  • 不用 view 时, .= 赋值速度与 = 几乎一样

【C】从(1,3,5)、(2,4,6)、(13,15,17)、(14,16,18)中,可以看出

  • 使用 view 时,Colon() 索引快于 UnitRange{Int64} 索引快于 StepRange{Int64, Int64} 索引

【D】从 7~12 与 19~24 可以看出,不用view时,各种索引方式的速度无显著差别;
【E】从 1~6 与 13~18 逐一对比,看出view加了reshape后,速度变慢

为何会有这么多区别??? :thinking: