7

I was recently reading this article by Dave Detlefs in which he presents a few cases where the CLR performs array bounds check elimination. I decided to test this myself, so I did the following:

  • 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

So here's the dissassembly for a[i] = i; in Test_SimpleAscend:

                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 

The cmp/jb/call is bounds checking, actually forcing the call to be executed throws an IndexOutOfRangeException.

Same thing for all array accesses, including the redundant access in Test_SimpleRedundant. So is there something wrong with my testing methodology, or the CLR doesn't actually eliminate bounds checking? I hope I'm wrong and if so I'd like to know how I can really get array bounds checking elimination.

leventov
  • 14,760
  • 11
  • 69
  • 98
Asik
  • 21,506
  • 6
  • 72
  • 131
  • 11
    When you say "started debugging", I assume that you launched the application *with the debugger attached* from the Visual Studio environment. In that case, you need to make sure that JIT compilation is enabled because it is not by default. The JIT compiler is the one that performs this optimization, not the C# compiler. – Cody Gray - on strike Feb 16 '12 at 02:36
  • What do you expect to happen if the bounds checking is disabled? This is generally a feature people expect to have when using a managed language like C#. – M.Babcock Feb 16 '12 at 02:42
  • 1
    The easiest way to ensure the optimization is actually enabled to start the application without debugging and then attach debugger to it. – svick Feb 16 '12 at 02:42
  • 2
    @M.Babcock: He doesn't expect bounds checking to be *disabled*; he expects that the JIT will be able to detect that the bounds will never be exceeded and thus can optimize away the checks. – Gabe Feb 16 '12 at 03:09
  • @M.Babcock It's not about disabling it but optimizing some checks out in cases where it we can statically prove that the index is inside the array bounds. In such cases the bounds check is redundant and only serves to slow down execution. – Asik Feb 16 '12 at 03:11
  • That makes sense. It just sounds strange to expect a commonly depended on feature to *not* be there. If I didn't expect bounds checking I'd use a language that doesn't do it (like C++). – M.Babcock Feb 16 '12 at 03:19

1 Answers1

14

Thanks to a comment by Cody Gray, I've managed to answer my own question:

By default, JIT Optimizations are disabled when debugging. To fix this, one can go to Debug -> Options and Settings -> Debugging -> General, and uncheck both "Enable Just My Code" and "Suppress JIT Optimization on module load".

Also see https://learn.microsoft.com/en-us/visualstudio/debugger/jit-optimization-and-debugging

With optimization enabled, the bounds check are removed as advertised.

I'll leave this here for documentation purposes.

Asik
  • 21,506
  • 6
  • 72
  • 131