@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
Другие вопросы по тегам