@propagate_inbounds 在 Julia 中

@propagate_inbounds in Julia

考虑以下情况:

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

查看 docs and some 我确定在执行“@inbounds b[i, j]”(b 是类型 B 的数组)时会消除边界检查。然而,事实并非如此。我错过了什么?

仅使用您的定义这对我有用:

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 仅在 type-stable 函数中有效。在全局范围(如 REPL)或类型不稳定的函数(如引用 non-constant 全局或 abstractly-typed 字段或数组的函数)中测试它不会删除这些边界检查。

请注意,您对 B 的定义可能会导致类型不稳定,因为 Julia 无法知道它存储的是什么函数。它们在您发布的最小示例中并不明显,但这可能是您更复杂的原始问题的根本原因。更好的定义是:

struct B{F} <: AbstractArray{Int, 2}
    A::A
    Func::F
end