C# 在发布版本中是否消除了数组边界检查?

C# 在发布版本中是否消除了数组边界检查?,c#,arrays,performance,indexoutofboundsexception,jit,C#,Arrays,Performance,Indexoutofboundsexception,Jit,我已经读到数组边界检查可以减缓程序中的热点,因为每次访问数组时,都必须执行一条额外的指令,以确保它在数组的边界内 因此,有时人们建议让for循环的上限为array.Length,因为JIT可以看到它并优化数组边界检查(假设主体每次迭代只访问当前循环变量) 然而,这与在一个单独的int变量中保存之前的长度的想法相冲突,该变量是循环提升到不变量的一种形式,许多人建议这样做也可以节省性能 假设代码将在发行版中运行,而不是从VisualStudio中运行(因此没有调试器),哪一个是最优的?将上界提升到一

我已经读到数组边界检查可以减缓程序中的热点,因为每次访问数组时,都必须执行一条额外的指令,以确保它在数组的边界内

因此,有时人们建议让for循环的上限为
array.Length
,因为JIT可以看到它并优化数组边界检查(假设主体每次迭代只访问当前循环变量)

然而,这与在一个单独的int变量中保存之前的长度的想法相冲突,该变量是循环提升到不变量的一种形式,许多人建议这样做也可以节省性能


假设代码将在发行版中运行,而不是从VisualStudio中运行(因此没有调试器),哪一个是最优的?将上界提升到一个单独的变量中,还是维护JIT提示的
数组。长度
?在这种情况下是否仍在执行数组边界检查?

首先,这是一个绝对经典的预成熟优化案例。这几乎可以肯定是由comp优化的除非您确实可以测量性能下降,否则不要开始考虑像这样的低级别优化

第二,如果你真的想知道,因为你真的看到了减速,那么编写、编译和运行基准测试。更进一步,看看编译后的IL是否会产生影响。但这绝对应该只在你经历减速时才做。即使它的速度很小(我怀疑),为了可维护性,您大量地牺牲了一点性能。在长期回报中,这不是一个好的折衷办法。

Tim是正确的——这是过早的优化,很可能您有更好的东西可以优化(使用探查器!)

然而,为了回答您的问题,JIT编译器在许多情况下将省略边界检查

简单的情况好消息是我们消除了 我们认为最常见的数组访问形式是:访问 它发生在一个循环中,覆盖了循环的所有索引 此程序中的阵列访问没有动态范围检查:

static void Test_SimpleAscend(int[] a) {

    for (int i = 0; i < a.Length; i++)

        a[i] = i;  // We get this.

}
static void Test\u SimpleAscend(int[]a){
for(int i=0;i
在撰写本文时,将.Length提升到单独的变量中会阻止x86 JIT编译器执行边界检查

这里没有借口:没有理由不得到这个,JIT编译器 应该知道n是ia中新分配的数组的长度。 事实上,这类代码的作者可能认为他在做JIT 编译器是一个优势,因为与局部变量n的比较似乎 比与ia.Length相比更便宜(尽管这不是真的 但在我们的系统中,至少在今天,这种 转换对于x86 JIT来说是适得其反的,因为它会阻止 边界检查不会被取消。我们很可能会延长我们的时间 编译器在将来跟踪这种值等价性。 不过,就目前而言,您应该遵循以下实用建议:· 建议2:如果可能,使用“a.Length”绑定一个索引为 变量用于索引到“a”


否,未禁用边界检查。@OldProgrammer--未更正请注意,降序版本(对于(int i=a.Length-1;i>=0;i--))没有得到消除的边界检查,正如博客PostExcellenly所说的那样,但有一个挑剔的地方:IL是不够的,因为JIT编译器有很多神奇的利斯古德点。仅仅看IL不足以说明它更好。但是,它足以说明它是一样的。相同的IL将在JIT中以相同的效率运行所以如果你得到相同的结果,你不会有什么不同。