1. ホーム
  2. c#

[解決済み】.NET 3.5のJITがアプリケーションの実行時に機能しない。

2022-03-24 01:54:24

質問

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

解決方法は?

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));
    }
  }

UPDATE: 2012年8月に再確認したところ、このバグはバージョン4.0.30319のジッターで修正されました。 しかし、v2.0.50727のジッターではまだ存在しています。 これだけ時間が経つと、古いバージョンで修正される可能性は低そうです。