@propagate_inbounds в Юлия
Рассмотрим следующий случай:
struct A <: AbstractArray{Int, 2}
N::Int
end
struct B <: AbstractArray{Int, 2}
A::A
Func
end
...
@inline Base.getindex(A::A, i::Int, j::Int) = begin
@boundscheck (1 <= i <= A.N && 1 <= j <= A.N) || throw(BoundsError())
i - j
end
Base.@propagate_inbounds Base.getindex(B::B, i::Int, j::Int) = begin
B.Func(B.A[i, j])
end
Глядя на документы и некоторые примеры, я был уверен, что проверки границ будут исключены при выполнении "@inbounds b[i, j]" (b - это массив типа B). Однако это не так. Что мне не хватает?
1 ответ
Это работает для меня, используя только ваши определения:
julia> f1(x, i, j) = return x[i,j]
f2(x, i, j) = @inbounds return x[i,j]
julia> f1(A(5), 123, 123)
ERROR: BoundsError
Stacktrace:
[1] getindex at ./REPL[3]:2 [inlined]
[2] f1(::A, ::Int64, ::Int64) at ./REPL[4]:1
julia> f2(A(5), 123, 123)
0
julia> f1(B(A(5), identity), 123, 123)
ERROR: BoundsError
Stacktrace:
[1] getindex at ./REPL[3]:2 [inlined]
[2] getindex at ./REPL[3]:6 [inlined]
[3] f1(::B, ::Int64, ::Int64) at ./REPL[4]:1
julia> f2(B(A(5), identity), 123, 123)
0
Как я уже упоминал в своем комментарии, @inbounds
работает только тогда, когда он находится в функции стабильного типа. Тестирование его в глобальной области видимости (например, в REPL) или в функции, которая не является стабильной по типу (например, функция, которая ссылается на непостоянное глобальное поле или поле или массив с абстрактной типизацией), не удалит эти проверки границ.
Обратите внимание, что ваше определение B
скорее всего, приведет к нестабильности типов, поскольку Джулия не может знать, какую функцию она хранит. Они не очевидны в минимальном примере, который вы опубликовали, но это может быть основной причиной вашей более сложной исходной проблемы. Лучшее определение будет:
struct B{F} <: AbstractArray{Int, 2}
A::A
Func::F
end