AnyMath 文档

边界检查

像许多现代编程语言一样,Julia在访问数组时使用边界检查来确保程序安全。 在紧密的内部循环或其他性能关键情况下,您可能希望跳过这些边界检查以提高运行时性能。 例如,为了发出矢量化(SIMD)指令,循环体不能包含分支,因此不能包含边界检查。 因此,朱莉娅包括一个 @inbounds(。..) 宏来告诉编译器在给定的块内跳过这样的边界检查。 用户定义的数组类型可以使用 @boundscheck(。..) 宏来实现上下文相关的代码选择。

Eliding边界检查

@boundscheck(。..) 宏标记执行边界检查的代码块。 当这样的块内联到 @inbounds(。..) 块,编译器可能会移除这些块。 编译器删除 @boundscheck 块_only如果它是inlined_到调用函数。 例如,您可以编写方法 总和 作为:

function sum(A::AbstractArray)
    r = zero(eltype(A))
    for i in eachindex(A)
        @inbounds r += A[i]
    end
    return r
end

具有自定义的类数组类型 我的阵列 有:

@inline getindex(A::MyArray, i::Real) = (@boundscheck checkbounds(A, i); A.data[to_index(i)])

然后当 getindex,getindex 被内联到 总和,呼吁 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的索引,因此在使用时会暴露不安全的内存访问 [医外线]:

julia> using OffsetArrays

julia> sum(OffsetArray([1, 2, 3], -10))
9164911648 # inconsistent results or segfault

而这里的错误的原始来源是 1:长度(A),使用 @inbounds 增加了从边界错误到不太容易捕获和调试的不安全内存访问的后果。 通常很难或不可能证明一种使用 @inbounds 是安全的,因此必须权衡性能改进的好处与分段攻击和无声的不当行为的风险,特别是在面向公众的Api中。

传播内圈

在某些情况下,由于代码组织的原因,您需要在 @inbounds@boundscheck 声明。 例如,默认 getindex,getindex 方法有链 getindex(A::AbstractArray,I::Real) 电话 getindex(IndexStyle(A),A,i) 电话 _getindex(::IndexLinear,A,i).

要复盖"一层内联"规则,函数可以用 基地。@传播_inbounds通过一个额外的内联层传播内部上下文(或越界上下文)。

边界检查调用层次结构

整体层次结构为:

* checkbounds(A,I。..) 哪些调用 ** checkbounds(Bool,A,I。..) 哪些调用 *** checkbounds_indices(Bool,axes(A),I) 递归调用 **** 检查索引 对于每个维度

这里 A 是阵列,并且 包含"请求的"索引。 轴(A) 返回"允许"索引的元组 A.

checkbounds(A,I。..) 如果索引无效,则引发错误,而 checkbounds(Bool,A,I。..) 申报表 错误 在那种情况下。 支票/支票 丢弃除数组之外的任何有关数组的信息 轴心 元组,并执行一个纯粹的索引与索引比较:这允许相对较少的编译方法来服务于大量的数组类型。 索引被指定为元组,并且通常以1-1的方式与通过调用另一个重要函数处理的单个维度进行比较, 检查索引:典型,

checkbounds_indices(Bool, (IA1, IA...), (I1, I...)) = checkindex(Bool, IA1, I1) &
                                                      checkbounds_indices(Bool, IA, I)

所以 检查索引 检查单个维度。 所有这些功能,包括未报告的 支票/支票 有文档可用 ? .

如果您必须自定义特定数组类型的边界检查,则应该专门化 checkbounds(Bool,A,I。..). 但是,在大多数情况下,您应该能够依赖 支票/支票 只要你提供有用的 轴心 为您的数组类型。

如果您有新颖的索引类型,请首先考虑专门化 检查索引,它处理数组特定维度的单个索引。 如果您有自定义多维索引类型(类似于 [医]CartesianIndex),那么你可能不得不考虑。 支票/支票.

注意此层次结构旨在减少方法含糊不清的可能性。 我们努力使 支票/支票 专门化数组类型的地方,并尽量避免对索引类型进行专门化;相反, 检查索引 仅用于索引类型(特别是最后一个参数)。

发射边界检查

茱莉亚可以用 --检查边界={yes|no|auto} 发出边界检查总是,从不,或尊重 @inbounds 声明。