22

The result of the following Code differs If it is started with the debugger in the background or without. The difference is only there, if optimization is switched on.

This is the result:

-> with optimazation: 1000 2008 3016 1001 2009 3007 ...

-> without optimization ( as expected ) 1000 1008 1016 1001 1009 1017 ...

Code:

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace OptimizerTest
{   
    public class Test
    {
        int dummy;

        public void TestFunction(int stepWidth)
        // stepWidth must be a parameter
        {
            for (int step = 0; step < stepWidth; step++)
            {
                dummy = step + 1000;
                // addition with constant ( same value as later !)
                for (int x = 0; x < 20; x += stepWidth)
                {
                    int index = x + 1000 + step;
                    // constant must be same as above and ?!?! 
                    // int index = x + step + 1000; works !!!!!
                    Console.Write("\n\r" + index);
                }
            }
        }

        [MethodImpl(MethodImplOptions.NoOptimization)]
        public void TestFunctionNoOptimization(int stepWidth)
        {
            for (int step = 0; step < stepWidth; step++)
            {
                dummy = step + 1000;
                for (int x = 0; x < 20; x += stepWidth)
                {
                    int index = x + 1000 + step;                        
                    Console.Write("\n\r" + index);
                }
            }
        }
    }

    class Program
    {
        /// <summary>
        /// Result differs from Start with F5 to Ctrl-F5
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            Test test = new Test();
            Console.Write("\n\r---------\n\roptimized result\n\r-------------" );
            test.TestFunction(8);
            Console.Write("\n\r---------\n\rnot optimized result\n\r-------------");
            test.TestFunctionNoOptimization(8);
            Console.Write("\n\r---------\n\rpress any key");
            Console.ReadKey();
        }
    }
}

The behavior of the error depends on the number of iterations of the inner loop (x<5 everything works fine). Very interesting is that the does not occure when I use

   int index = x + step + 1000; 

instead of

   int index = x + 1000 + step; 

I am working with Visual Studio 2010 SP1 and tried it with .NET Framework from 2.0 to 4.0.3. I've always seen the same result.

Does anybody know about this bug or can reproduce?

Zach Lipton
  • 1,850
  • 1
  • 14
  • 28
Jan
  • 223
  • 1
  • 4
  • I'm getting the same results as your "without optimization"-example with VS 2012 for **both** functions -- so, cannot reproduce. – Vlad Dec 20 '13 at 10:42
  • Try using ILSpy to decompile your generated dll and see what you get. (Or put your dll somewhere we can look at it, I'm curious :D) – Rawling Dec 20 '13 at 10:48
  • ILSpy gives me back, what I put in. Seems to be OK. – Jan Dec 20 '13 at 10:54
  • 5
    @UweKeim I [beg to differ](http://stackoverflow.com/questions/20398705/why-cant-the-compiler-tell-the-better-conversion-target-in-this-overload-resolu) :D – Rawling Dec 20 '13 at 10:54
  • ditto for cannot reproduce in 2012. Also ditto for putting the exe somewhere that it can be tested on other machines and/or the IL examined. – Chris Dec 20 '13 at 11:23
  • 2
    @UweKeim: Feel free to point out what mistake Jan has made if you want. ;-) – Chris Dec 20 '13 at 11:24
  • We tested the code on different PC's in our company. we compiled it in differnt Studios 2010 (but same configuration), but we had allways the same wrong result :-(. My workaround (exchange step & 1000) works in my main application (where 1000 is a function parameter) fine... but I would like to know what's wrong. It only occures when programm runs with "Start without debugging"! – Jan Dec 20 '13 at 11:32
  • 1
    I get the same result as you with VS 2010 SP1 on x64 builds but it works fine with x86 builds. – StevieB Dec 20 '13 at 11:58
  • 1
    I doubleschecked my project settings and I saw that the platform target settings was x64. With x86 its fine, but wit x64 it fails. tx for test – Jan Dec 20 '13 at 12:27
  • This bug ***does*** reproduce (on 64-bit hardware) with VS2013 targeting .NET 4.5.1 if configuration is "Release" and target is "x64". It also reproduces with "Release" and target "Any CPU" if the "Prefer 32-bit" is deselected (not checked). It will not reproduce with compilation for 32-bit. – Jeppe Stig Nielsen Dec 20 '13 at 12:56

1 Answers1

20

Yes, this is definitely a jitter optimizer bug. The reason other SO users have trouble reproducing it is because only the x64 jitter appears to have this bug. You must set the project's Platform target to AnyCPU, untick the "Prefer 32-bit" option on VS2012 and up.

I haven't looked at the underlying reason closely enough but it appears to fumble at trying to eliminate the common step + 1000 sub-expression. Sub-expression elimination is one of the standard jitter optimizations. But it incorrectly incorporates the expression code inside the loop instead of keeping it out of the loop as written. You'll for example see the bug disappear when you write:

  dummy = step + 999;

This bug is still present in the latest .NET 4.5.1 version (clrjit.dll, v4.0.30319.34003 on my machine), also present in the v2 jitter (mscorjit.dll, v2.0.50727.7905 on my machine).

The code is a bit too synthetic to recommend a solid workaround, you already found one anyway so you can keep motoring on your project. In general, I'd recommend you eliminate the sub-expression yourself with:

  int index = x + dummy;  

It should be reported to Microsoft, you can so so by posting a bug report at connect.microsoft.com. If you don't want to take the time then let me know and I'll take care of it.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • This should indeed be reported. However, I doubt they'd fix it, given this is apparently the first time it shows up, and given they are working on a new jit anyway. Has anyone tried this with ryuJit? (http://blogs.msdn.com/b/dotnet/archive/2013/11/18/ryujit-net-jit-compiler-ctp1-faq.aspx) – Kris Vandermotten Dec 20 '13 at 12:43
  • tx. Please inform microsoft. Did I understand you right, that the bug is also in VS2012 x64? – Jan Dec 20 '13 at 12:45
  • 7
    Submitted as [feedback item 812093](https://connect.microsoft.com/VisualStudio/feedback/details/812093/x64-jitter-sub-expression-elimination-optimizer-bug). – Hans Passant Dec 20 '13 at 12:59
  • @Hans Is it possible to give us some pseudocode describing the incorrect jitter output? I'm struggling to work it out myself. – Rawling Dec 20 '13 at 13:19
  • Just move the dummy variable assignment inside the loop and use index = x + dummy for a logical repro. – Hans Passant Dec 20 '13 at 13:34
  • Yeah, this doesn't occur in RyuJIT. How horrible is it if we just focus on getting RyuJIT ready and don't fix the issue? (Seriously, fixing stuff in JIT64 is unpleasant :-) – Kevin Frei Feb 10 '14 at 22:42
  • RyuJIT was forked from the x86 jitter codebase. So sure, probably doesn't have this problem. Not that inspiring btw, the x64 jitter has a better optimizer in general. – Hans Passant Feb 10 '14 at 23:05
  • @Hans, I completely agree with your assessment of RyuJIT CTP1 against JIT64 regarding optimizations. CTP2 is coming soon, and should help change your mind. When I say "getting RyuJIT ready" I mean getting RyuJIT to where it's better than JIT64 for both compile speed (done) and optimization capability (making great progress). – Kevin Frei Feb 11 '14 at 17:36