Search code examples
c#arraysfor-loopcompiler-optimizationbounds-check-elimination

Array boundaries check optimization in a for-loop


        var ar = new int[500000000];

        var sw = new Stopwatch();
        sw.Start();

        var length = ar.Length;
        for (var i = 0; i < length; i++)
        {
            if (ar[i] == 0);
        }

        sw.Stop();

sw.ElapsedMilliseconds: ~2930ms

        var ar = new int[500000000];

        var sw = new Stopwatch();
        sw.Start();

        for (var i = 0; i < ar.Length; i++)
        {
            if (ar[i] == 0);
        }

        sw.Stop();

sw.ElapsedMilliseconds: ~3520ms

Win8x64, VS12, .NET4.5, Release build, "Optimize code" on.

As far as I know the second approach should be faster because of an array boundaries check optimization. Am I missing something?


Solution

  • I'm also using Win8 x64, .NET 4.5, Release build, outside of the debugger (this is an important one); I get:

    0: 813ms vs 421ms
    1: 439ms vs 420ms
    2: 440ms vs 420ms
    3: 431ms vs 429ms
    4: 433ms vs 427ms
    5: 424ms vs 437ms
    6: 427ms vs 434ms
    7: 430ms vs 432ms
    8: 432ms vs 435ms
    9: 430ms vs 430ms
    10: 427ms vs 418ms
    11: 422ms vs 421ms
    12: 434ms vs 420ms
    13: 439ms vs 425ms
    14: 426ms vs 429ms
    15: 426ms vs 426ms
    16: 417ms vs 432ms
    17: 442ms vs 425ms
    18: 420ms vs 429ms
    19: 420ms vs 422ms
    

    The first pays a JIT / "fusion" cost, but overall it is about the same (some in each column look faster, but overall not much to speak about).

    using System;
    using System.Diagnostics;
    static class Program
    {
        static void Main()
        {
            var ar = new int[500000000];
    
            for (int j = 0; j < 20; j++)
            {
                var sw = Stopwatch.StartNew();
                var length = ar.Length;
                for (var i = 0; i < length; i++)
                {
                    if (ar[i] == 0) ;
                }
    
                sw.Stop();
                long hoisted = sw.ElapsedMilliseconds;
    
                sw = Stopwatch.StartNew();
                for (var i = 0; i < ar.Length; i++)
                {
                    if (ar[i] == 0) ;
                }
                sw.Stop();
                long direct = sw.ElapsedMilliseconds;
    
                Console.WriteLine("{0}: {1}ms vs {2}ms", j, hoisted, direct);
            }
        }
    }