アプリケーションの実行時に .NET 3.5 JIT が機能しない 質問する

アプリケーションの実行時に .NET 3.5 JIT が機能しない 質問する

次のコードは、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();
        }
    }
}

ベストアンサー1

これは 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 に増やすとバグは消えますが、展開するには呼び出しが多すぎます。

回避策の 1 つは次のとおりです。

  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 ジッターではまだ存在しています。これほど長い時間が経過した後、古いバージョンでこのバグが修正される可能性は低いようです。

おすすめ記事