Engee 文档

检查边界

像许多现代编程语言一样,Julia在访问数组时使用边界检查来确保程序安全性。 在连续的内部循环或其他性能关键情况下,可以跳过边界检查以提高运行时性能。 例如,要执行矢量化(SIMD)指令,循环体不能包含分支,这意味着它不能包含边界检查。 在这方面,Julia包含宏'@inbounds(。..)`,它指示编译器在给定块中跳过此类边界检查。 用户定义的数组类型可以使用宏'@boundscheck(。..)'用于上下文相关的代码选择。

绕过边境检查

宏'@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’函数设计为仅专门化索引类型(特别是最后一个参数)。

进行边境检查

Julia可以使用命令'--check-bounds={yes|no|auto}`始终执行边界检查,从不执行边界检查,或受'@inbounds’声明的约束。