julia 的视图函数的幕后发生了什么?a[3, :] = view(a, 1, :) vs a[3, :] = a[1, :]

Bel*_*len 7 julia

我认为视图函数的工作方式类似于 C++ 中的引用,基本上两个变量都指向同一块内存。

为什么这样做:

julia> a = [1 2 3; 4 5 6; 7 8 9]
3×3 Array{Int64,2}:
 1  2  3
 4  5  6
 7  8  9


julia> b = view(a, 1, :)
3-element view(::Array{Int64,2}, 1, :) with eltype Int64:
 1
 2
 3

julia> b[1] = 100
100

julia> a
3×3 Array{Int64,2}:
 100  2  3
   4  5  6
   7  8  9

julia> a[1, 3] = 200
200

julia> b
3-element view(::Array{Int64,2}, 1, :) with eltype Int64:
 100
   2
 200
Run Code Online (Sandbox Code Playgroud)

基本上你改变一个,另一个也改变,反之亦然。但这并没有相同的效果:

julia> a = [1 2 3; 4 5 6; 7 8 9]
3×3 Array{Int64,2}:
 1  2  3
 4  5  6
 7  8  9

julia> a[3, :] = view(a, 1, :)
3-element view(::Array{Int64,2}, 1, :) with eltype Int64:
 1
 2
 3

julia> a
3×3 Array{Int64,2}:
 1  2  3
 4  5  6
 1  2  3

julia> a[1, 1] = 100
100

julia> a
3×3 Array{Int64,2}:
 100  2  3
   4  5  6
   1  2  3

julia> a[3, 1] = 200
200

julia> a
3×3 Array{Int64,2}:
 100  2  3
   4  5  6
 200  2  3
Run Code Online (Sandbox Code Playgroud)

我的问题:这相当于只是做:a[3, :] = a[1, :]?是否有任何性能优势?第二种情况的幕后发生了什么?

任何反馈表示赞赏!

Bog*_*ski 4

您可以通过运行以下代码来检查这种情况下是否发生复制:

\n
julia> a = rand(3, 10^6);\n\njulia> b = view(a, 1, :);\n\njulia> @time a[3, :] = b; # I have already compiled the code earlier\n  0.005599 seconds (3 allocations: 7.629 MiB)\n\njulia> bc = copy(b);\n\njulia> @time a[3, :] = bc; # I have already compiled the code earlier\n  0.002189 seconds (1 allocation: 16 bytes)\n
Run Code Online (Sandbox Code Playgroud)\n

原因是 Julia 在函数中_unsafe_setindex!(即最终由您执行的操作调用)执行x\xe2\x80\xb2 = unalias(A, x). 此操作意味着在编译时检查是否保证源数组和目标数组不共享内存。在这种情况下,编译器无法证明这一点,因此会发生复制。

\n

为了使您想要的操作高效,您可以做的就是使用循环(正如您所知,实际上别名不会通过您定义操作的方式发生;我正在使用视图,但当然您可以只读取和写入直接来自a而不创建b):

\n
function fastcopy!(a)\n    @assert size(a, 1) > 2\n    b = view(a, 1, :)\n    @inbounds @simd for i in eachindex(b)\n        a[3, i] = b[i]\n    end\nend\n
Run Code Online (Sandbox Code Playgroud)\n

时间安排如下:

\n
julia> @time fastcopy!(a)\n  0.001895 seconds\n
Run Code Online (Sandbox Code Playgroud)\n