检查边界
绕过边境检查
宏'@boundscheck(。..)'标记执行边界检查的代码块。 当这样的块被插入到块'@inbounds(。..),编译器可以删除它们。 只有当
@boundscheck`块嵌入到调用函数中时,编译器才会删除它。 例如,您可以编写’sum’方法,如下所示。
function sum(A::AbstractArray)
r = zero(eltype(A))
for i in eachindex(A)
@inbounds r += A[i]
end
return r
end
在这里,自定义类似数组的类型’myArray’看起来像这样。
@inline getindex(A::MyArray, i::Real) = (@boundscheck checkbounds(A, i); A.data[to_index(i)])
然后,将`getindex’插入’sum’后,将省略对`checkbounds(A,i)的调用。 如果函数包含多个嵌入级别,则只排除位于以下不超过一个嵌入级别的
@boundscheck’块。 此规则可防止进一步位于堆栈上的代码对程序行为进行意外更改。
注意!
使用'@inbounds’很容易意外地揭示不安全的操作。 你可能会想写上面的例子如下。
function sum(A::AbstractArray)
r = zero(eltype(A))
for i in 1:length(A)
@inbounds r += A[i]
end
return r
end
它假定索引基于1,因此,打开危险的内存访问时使用 'OffsetArrays'。
julia> using OffsetArrays
julia> sum(OffsetArray([1, 2, 3], -10))
9164911648 # Несогласованные результаты или аварийное завершение
虽然错误的初始来源是'1:length(A),但使用
@inbounds`会加剧从边界错误到危险的内存访问的后果,这些内存访问不容易检测和调试。 通常很难或不可能证明使用'@inbounds’的方法是安全的,因此有必要平衡提高性能的好处与崩溃和隐藏的不当行为的风险,特别是在公共Api中。
在边界内传播
在某些情况下,由于与代码组织相关的原因,您可能需要`@inbounds`和`@boundscheck`声明之间的多个级别。 例如,默认方法`getindex’有一个链’getindex(A::AbstractArray,I::Real)调用`getindex(IndexStyle(A),A,i)
,调用`_getindex(::IndexLinear,A,i)`。
要重新定义一个级别嵌入的规则,可以使用宏标记函数 '基地。@propagate_inbounds'通过一个额外的嵌入级别传播边界内的上下文(或边界外的上下文)。
边界检查调用的层次结构
总体层次结构如下:
-
功能'checkbounds(A,I。..)',它调用 功能'checkbounds(Bool,A,I。..)',它调用 函数`checkbounds_indices(Bool,axes(A),I)',递归调用 ***每个维度的’checkindex’函数。
这里`A’是一个数组’I’包含请求的索引。 'axes(A)`返回`A’的允许索引的元组。
函数'checkbounds(A,I。..如果索引无效,则'返回错误,而函数'checkbounds(Bool,A,I。..)`在这种情况下返回’false'。 'Checkbounds_indices’函数丢弃除`axes`元组之外的任何有关数组的信息,并执行干净的索引到索引比较'`这允许相对较少数量的编译方法来服务于各种数组类型。 索引被设置为元组,通常根据"1-1"方案进行比较,而单独的测量则通过调用另一个重要函数’checkindex’来处理。
checkbounds_indices(Bool, (IA1, IA...), (I1, I...)) = checkindex(Bool, IA1, I1) &
checkbounds_indices(Bool, IA, I)
因此,'checkindex’函数检查一个维度。 对于所有这些函数,包括不可导出的’checkbounds_indices',在输入'?""性格。
如果您需要为特定数组类型设置边界检查,则应专门化'checkbounds(Bool,A,I。..)`. 但是,在大多数情况下,只要为数组类型提供有用的"轴"值,就可以使用`checkbounds_indices`函数。
如果您有新的索引类型,请首先考虑`checkindex’特化,它处理特定数组维度的单个索引。 如果您有自定义多维索引类型(如`CartesianIndex'),则可能需要考虑专门化`checkbounds_indices'。
请注意,此层次结构旨在减少方法中出现歧义的可能性。 我们试图使’checkbounds’函数成为数组类型的特化的地方,并试图避免索引类型的特化。 相反,'checkindex’函数设计为仅专门化索引类型(特别是最后一个参数)。