首页 文章

.NET JIT潜在错误?

提问于
浏览
390

在Visual Studio中运行发行版并在Visual Studio外部运行发行版时,以下代码提供了不同的输出 . 我正在使用Visual Studio 2008并以.NET 3.5为目标 . 我也尝试过.NET 3.5 SP1 .

当在Visual Studio外部运行时,JIT应该启动 . 或者(a)C#中有一些微妙的东西我缺少或者(b)JIT实际上是错误的 . 我怀疑JIT可能出错,但我已经没有其他可能性......

在Visual Studio中运行时的输出:

0 0,
    0 1,
    1 0,
    1 1,

在Visual Studio外部运行发布时的输出:

0 2,
    0 2,
    1 2,
    1 2,

是什么原因?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Test
{
    struct IntVec
    {
        public int x;
        public int y;
    }

    interface IDoSomething
    {
        void Do(IntVec o);
    }

    class DoSomething : IDoSomething
    {
        public void Do(IntVec o)
        {
            Console.WriteLine(o.x.ToString() + " " + o.y.ToString()+",");
        }
    }

    class Program
    {
        static void Test(IDoSomething oDoesSomething)
        {
            IntVec oVec = new IntVec();
            for (oVec.x = 0; oVec.x < 2; oVec.x++)
            {
                for (oVec.y = 0; oVec.y < 2; oVec.y++)
                {
                    oDoesSomething.Do(oVec);
                }
            }
        }

        static void Main(string[] args)
        {
            Test(new DoSomething());
            Console.ReadLine();
        }
    }
}

3 回答

  • 23

    这是一个JIT优化器错误 . 它正在展开内部循环,但没有正确更新oVec.y值:

    for (oVec.x = 0; oVec.x < 2; oVec.x++) {
    0000000a  xor         esi,esi                         ; oVec.x = 0
            for (oVec.y = 0; oVec.y < 2; oVec.y++) {
    0000000c  mov         edi,2                           ; oVec.y = 2, WRONG!
              oDoesSomething.Do(oVec);
    00000011  push        edi  
    00000012  push        esi  
    00000013  mov         ecx,ebx 
    00000015  call        dword ptr ds:[00170210h]        ; first unrolled call
    0000001b  push        edi                             ; WRONG! does not increment oVec.y
    0000001c  push        esi  
    0000001d  mov         ecx,ebx 
    0000001f  call        dword ptr ds:[00170210h]        ; second unrolled call
          for (oVec.x = 0; oVec.x < 2; oVec.x++) {
    00000025  inc         esi  
    00000026  cmp         esi,2 
    00000029  jl          0000000C
    

    当你让oVec.y增加到4时,这个bug就会消失,那就是要打开的次数太多了 .

    一个解决方法是:

    for (int x = 0; x < 2; x++) {
        for (int y = 0; y < 2; y++) {
          oDoesSomething.Do(new IntVec(x, y));
        }
      }
    

    更新:2012年8月重新检查,此错误已在版本4.0.30319抖动中修复 . 但仍然存在于v2.0.50727抖动中 . 在这么久之后,他们似乎不太可能在旧版本中解决这个问题 .

  • 206

    我相信这是一个真正的JIT编译错误 . 我会向微软报告,看看他们说了什么 . 有趣的是,我发现x64 JIT没有同样的问题 .

    这是我对x86 JIT的阅读 .

    // save context
    00000000  push        ebp  
    00000001  mov         ebp,esp 
    00000003  push        edi  
    00000004  push        esi  
    00000005  push        ebx  
    
    // put oDoesSomething pointer in ebx
    00000006  mov         ebx,ecx 
    
    // zero out edi, this will store oVec.y
    00000008  xor         edi,edi 
    
    // zero out esi, this will store oVec.x
    0000000a  xor         esi,esi 
    
    // NOTE: the inner loop is unrolled here.
    // set oVec.y to 2
    0000000c  mov         edi,2 
    
    // call oDoesSomething.Do(oVec) -- y is always 2!?!
    00000011  push        edi  
    00000012  push        esi  
    00000013  mov         ecx,ebx 
    00000015  call        dword ptr ds:[002F0010h] 
    
    // call oDoesSomething.Do(oVec) -- y is always 2?!?!
    0000001b  push        edi  
    0000001c  push        esi  
    0000001d  mov         ecx,ebx 
    0000001f  call        dword ptr ds:[002F0010h] 
    
    // increment oVec.x
    00000025  inc         esi  
    
    // loop back to 0000000C if oVec.x < 2
    00000026  cmp         esi,2 
    00000029  jl          0000000C 
    
    // restore context and return
    0000002b  pop         ebx  
    0000002c  pop         esi  
    0000002d  pop         edi  
    0000002e  pop         ebp  
    0000002f  ret
    

    这看起来像是对我不利的优化......

  • 81

    我将您的代码复制到新的控制台应用程序中 .

    • Debug Build

    • 使用调试器和无调试器更正输出

    • 切换到发布版本

    • 再次,正确输出两次

    • 创建了一个新的x86配置(I 'm on running X64 Windows 2008 and was using '任何CPU')

    • Debug Build

    • F5和CTRL F5输出正确

    • 发布版本

    • 附加调试器的正确输出

    • 没有调试器 - Got the incorrect output

    所以它是x86 JIT错误地生成代码 . 删除了我关于循环重新排序的原始文本等 . 这里的一些其他答案已经确认JIT在x86上不正确地展开循环 .

    要解决此问题,您可以将IntVec的声明更改为类,并且它适用于所有类型 .

    认为这需要继续MS Connect ....

    -1到微软!

相关问题