边界检查
像许多现代编程语言一样,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),那么你可能不得不考虑。 支票/支票.
注意此层次结构旨在减少方法含糊不清的可能性。 我们努力使 支票/支票 专门化数组类型的地方,并尽量避免对索引类型进行专门化;相反, 检查索引 仅用于索引类型(特别是最后一个参数)。