Почему A[1:n,1:n] = view(B, 1:n, 1:n)
или его варианты медленнее, чем набор циклов while?Давайте посмотрим, что делает A[1:n,1:n] = view(B, 1:n, 1:n)
.
view
возвращает итератор, который содержит указатель на родительский элемент B
и информацию о том, как вычислять индексы, которые следует скопировать.A[1:n,1:n] = ...
анализируется на вызов _setindex!(...)
.После этого, и несколько вызовов по цепочке вызовов, основная работа выполняется следующим образом:
.\abstractarray.jl:883;
# In general, we simply re-index the parent indices by the provided ones
function getindex(V::SlowSubArray{T,N}, I::Vararg{Int,N}) where {T,N}
@_inline_meta
@boundscheck checkbounds(V, I...)
@inbounds r = V.parent[reindex(V, V.indexes, I)...]
r
end
#.\multidimensional.jl:212;
@inline function next(iter::CartesianRange{I}, state) where I<:CartesianIndex
state, I(inc(state.I, iter.start.I, iter.stop.I))
end
@inline inc(::Tuple{}, ::Tuple{}, ::Tuple{}) = ()
@inline inc(state::Tuple{Int}, start::Tuple{Int}, stop::Tuple{Int}) = (state[1]+1,)
@inline function inc(state, start, stop)
if state[1] < stop[1]
return (state[1]+1,tail(state)...)
end
newtail = inc(tail(state), tail(start), tail(stop))
(start[1], newtail...)
end
getindex
принимает представление V
и индекс I
.Мы получаем представление из B
и индекс I
из A
.На каждом этапе reindex
вычисляет из представления V
и индексов индекса I
, чтобы получить элемент в B
.Он называется r
и мы его возвращаем.Наконец, r
записывается в A
.
. После каждой копии inc
увеличивает индекс I
до следующего элемента в A
и проверяет, выполнено ли оно.Обратите внимание, что код взят из v0.63, но в master
он более или менее одинаков.
В принципе код можно сократить до набора циклов while, но он более общий.Он работает для произвольных представлений B
и произвольных срезов вида a:b:c
и для произвольного числа размеров матрицы.Большое N
в нашем случае 2
.
Поскольку функции более сложные, компилятор также не оптимизирует их.Т.е. есть рекомендация, что компилятор должен их встроить, но он этого не делает.Это показывает, что показанные функции нетривиальны.
Для набора циклов компилятор сокращает самый внутренний цикл до трех сложений (каждый для указателя на A
и B
и один для индекса цикла)и одна инструкция копирования.
tl; dr Внутренняя цепочка вызовов A[1:n,1:n] = view(B, 1:n, 1:n)
в сочетании с многократной диспетчеризацией нетривиальна и обрабатывает общий случай.Это вызывает накладные расходы.Набор циклов while уже оптимизирован для специального случая.
Обратите внимание, что производительность зависит от компилятора.Если взглянуть на одномерный случай A[1:n] = view(B, 1:n)
, это быстрее, чем цикл while, потому что он векторизует код.Но для более высоких измерений N >2
разница возрастает.