CLR中的数组边界检查消除?

7

我最近阅读了Dave Detlefs的这篇文章,其中他提出了一些CLR执行数组边界检查消除的情况。我决定自己测试一下,所以我做了以下几点:

  • Opened Visual Studio 2010 Ultimate SP1
  • Created a new C# project of type Console Application (targeting .NET 4 Client Profile by default)
  • Added the following code (all sub-methods are taken directly from the article):

    class Program {
        static void Main(string[] args) {
            int[] array = new int[30];
            Test_SimpleAscend(array);
            Test_SimpleRedundant(array, 3);
    
            foreach (int i in array) {
                Console.WriteLine(i);
            }
        }
    
        static void Test_SimpleAscend(int[] a) {
            for (int i = 0; i < a.Length; i++)
                a[i] = i;
        }
    
        static void Test_SimpleRedundant(int[] a, int i) {
            int k = a[i];
            k = k + a[i];
        }
    }
    
  • Switched to Release mode; verified that "Optimize Code" is checked in the Build options

  • Added a breakpoint to each array access, started debugging (F5) and opened the Dissassembly window
在Test_SimpleAscend中,a[i] = i;的反汇编如下:
                a[i] = i;
00000024  mov         eax,dword ptr [ebp-4] 
00000027  mov         edx,dword ptr [ebp-8] 
0000002a  cmp         eax,dword ptr [edx+4] 
0000002d  jb          00000034 
0000002f  call        64FD6E08 
00000034  mov         ecx,dword ptr [ebp-4] 
00000037  mov         dword ptr [edx+eax*4+8],ecx 

“cmp/jb/call”是边界检查,实际上强制执行调用会抛出IndexOutOfRangeException异常。
所有数组访问都是相同的,包括Test_SimpleRedundant中的冗余访问。那么我的测试方法有什么问题,或者CLR实际上没有消除边界检查?我希望我是错的,如果是这样,我想知道如何真正消除数组边界检查。

11
您说“开始调试”时,我理解为您是在Visual Studio环境中使用调试器启动了应用程序。在这种情况下,您需要确保启用JIT编译,因为默认情况下它是关闭的。JIT编译器执行此优化,而不是C#编译器。 - Cody Gray
如果禁用边界检查,您希望发生什么?这通常是人们在使用像C#这样的托管语言时期望拥有的功能。 - M.Babcock
1
确保优化已启用的最简单方法是在不调试的情况下启动应用程序,然后将调试器附加到它上面。 - svick
2
@M.Babcock:他并不希望禁用边界检查;他期望JIT能够检测到边界永远不会被超越,从而可以优化掉这些检查。 - Gabe
@M.Babcock 这不是要禁用它,而是在一些情况下优化一些检查,如果我们可以静态地证明索引在数组边界内,则这样做。在这种情况下,边界检查是多余的,只会减慢执行速度。 - Asik
这很有道理。只是奇怪的是,我们期望一个常用的功能在那里,却没有它。如果我不需要边界检查,我会使用不进行此项操作的语言(比如C++)。 - M.Babcock
1个回答

14

感谢 Cody Gray 的评论,我成功回答了自己的问题:

默认情况下,在调试时 JIT 优化是被禁用的。要解决这个问题,可以进入“调试”->“选项和设置”->“调试”->“常规”,然后取消选中“启用我的代码”和“在模块加载时抑制 JIT 优化”。

另请参见https://learn.microsoft.com/zh-cn/visualstudio/debugger/jit-optimization-and-debugging

启用优化后,边界检查就像广告中所说的那样被移除了。

出于文档目的,我会将其留在此处。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接