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

时间:2015-03-26 00:01:37

标签: c# arrays performance indexoutofboundsexception jit

我已经读过数组边界检查会减慢程序中的热点,因为每次访问数组时,都必须执行一条附加指令以确保它在数组的范围内。

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

然而,这与将前面的长度保存在单独的int变量中的想法相冲突,作为一种循环提升到一个不变量的形式,许多人建议也保存性能。

假设代码将在发布中运行而不是在Visual Studio中运行(因此没有调试器),这是最佳的吗?将上限提升到单独的变量或维护JIT提示的array.Length?在这种情况下,是否仍在执行数组边界检查?

2 个答案:

答案 0 :(得分:3)

蒂姆是正确的 - 这是过早的优化,你很可能有更好的东西可以优化(使用分析器!)

但是,要回答您的问题,JIT编译器在很多情况下都会忽略边界检查。

http://blogs.msdn.com/b/clrcodegeneration/archive/2009/08/13/array-bounds-check-elimination-in-the-clr.aspx

  

简单案例好消息是我们确实消除了边界检查   我们认为最常见的数组访问形式是:访问   在循环的所有索引的循环内发生。所以,那里   在此程序中没有对数组访问进行动态范围检查:

static void Test_SimpleAscend(int[] a) {

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

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

}

在撰写本文时,将a.Length提升为单独的变量会阻止x86 JIT编译器执行边界检查

  

这里没有任何借口:JIT编译器没有理由不这样做   应该知道n是ia中新分配的数组的长度。   事实上,这些代码的作者可能会认为他正在做JIT   编译一个恩惠,因为可能看起来与局部变量n进行比较   比ia.Length便宜(虽然这不是真的   在英特尔机器上)。但在我们的系统中,至少在今天,这种类型   转换对x86 JIT起反作用,因为它可以防止   边界检查被淘汰。我们可能会扩展我们的   编译器在未来跟踪这种价值等值。   但是现在,你应该遵循这条实用建议:·   建议2:如果可能,使用“a.Length”绑定索引的循环   变量用于索引“a”。

答案 1 :(得分:1)

首先,这是一个绝对经典的预成熟优化案例。几乎可以肯定的是,编译器会对其进行优化,除非您实际上是可衡量的性能减速,否则永远不会开始关注这样的低级优化。

其次,如果你想真正知道,因为你实际上看到减速,写两个,编译,并运行基准测试。更进一步,看看编译的IL,看看它是否有所作为。但是,如果您遇到减速,那绝对应该这样做。即使它以微不足道的速度更快(我怀疑),你在可维护性方面进行了大量交易。在长期回报中,这不是一个很好的权衡。

相关问题