checkbounds is intuitive, but how does it compare and work with the other 2? Pulled from the following example in JuAFEM.jl:
@propagate_inbounds function assemble!(g::AbstractVector{T}, edof::AbstractVector{Int}, ge::AbstractVector{T}) where {T}
@boundscheck checkbounds(g, edof)
@inbounds for i in 1:length(edof)
g[edof[i]] += ge[i]
end
end
Note: This comment is full of mistakes, that I didn’t even bother correcting it. Please read on for the correct understanding.
So I would like to paraphrase the docs and specialize on the above example, correct me if I am wrong.
It seems that @boundscheck, which should ideally surround only bound checking lines, communicates with the function calling assemble! only if assemble! was inlined and the function call was in an @inbounds block, telling it (or the compiler really) to skip the lines in the @boundscheck block.
@propagate_inbounds looks for @boundscheck blocks in one deeper layer of function calls, skipping any lines in the @boundscheck blocks.
So it is useless to do the following:
Use @propagate_inbounds if no @inbounds block exists in the function. This one is pretty obvious.
Use @boundscheck block in a function when the function is not inlined and directly called in an @inbounds block in the caller function, or is indirectly called in an @inbounds block in a function (any number of function calls down the stack) decorated with @propagate_inbounds.
No. @inbounds only works with one level of @inbounds, @propagate_inbounds extends it.
This is clearly documented as
To override the “one layer of inlining” rule, a function may be marked with @propagate_inbounds to propagate an inbounds context (or out of bounds context) through one additional layer of inlining.
I see so only one additional layer, my bad. I will edit my post.
Could you elaborate? How can @propagate_inbounds extend @inbounds and yet still work without it?
One more question, the docs seem to imply that the lines in an @bounds_check block are only skipped if the function having that block is inlined? Does this mean that an @inline decorator is missing from the above example for the @bounds_check to be of any use?
@propagate_inbounds propagate @inbounds into the function. It has nothing to do with @inbounds in the function. @inbounds is also independent of anything of the function it is in.
yes.
no. please read the docstring
help?> Base.@propagate_inbounds
@propagate_inbounds
Tells the compiler to inline a function while retaining the caller's inbounds context.
Oh, ok so @propagate_inbounds inlines the function and extends the caller function’s @inbounds context, it has nothing to do with the functions called by assemble! for example. So a simple @inline extends the @inbounds context only once. For additional extensions, @propagate_inbounds is necessary. So if f1 calls f2 in an @inbounds block, and f2 calls f3 without @inbounds, then inlining f2 will extend the @inbounds of f1, but using @propagate_inbounds is necessary before f3 for the @inbounds context of f1 to be passed to f3 through f2. A side effect of this is that f3 will be inlined in f2 which itself is inlined in f1. Alternatively, f2 could have been decorated with @propagate_inbounds instead of @inline, as the inlining happens anyways. Is this about right?
I think this part is worth adding to the docs link.
help?> Base.@propagate_inbounds
@propagate_inbounds
Tells the compiler to inline a function while retaining the caller's inbounds context.