41

以下は簡単なテストフィクスチャです。デバッグ ビルドでは成功し、リリース ビルドでは失敗します (VS2010、.NET4 ソリューション、x64):

[TestFixture]
public sealed class Test
{
    [Test]
    public void TestChecker()
    {
        var checker = new Checker();
        Assert.That(checker.IsDateTime(DateTime.Now), Is.True);
    }
}

public class Checker
{
    public bool IsDateTime(object o)
    {
        return o is DateTime;
    }
}

コードの最適化が大混乱を引き起こしているようです。リリース ビルドで無効にすると、同様に機能します。それは私にとってかなり不可解でした。以下では、ILDASM を使用してビルドの 2 つのバージョンを逆アセンブルしました。

IL をデバッグします。

.method public hidebysig instance bool IsDateTime(object o) cil managed
{
  // Code size       15 (0xf)
  .maxstack  2
  .locals init (bool V_0)
  IL_0000:  nop
  IL_0001:  ldarg.1
  IL_0002:  isinst     [mscorlib]System.DateTime
  IL_0007:  ldnull
  IL_0008:  cgt.un
  IL_000a:  stloc.0
  IL_000b:  br.s       IL_000d
  IL_000d:  ldloc.0
  IL_000e:  ret
} // end of method Validator::IsValid

リリース IL:

.method public hidebysig instance bool IsDateTime(object o) cil managed
{
  // Code size       10 (0xa)
  .maxstack  8
  IL_0000:  ldarg.1
  IL_0001:  isinst     [mscorlib]System.DateTime
  IL_0006:  ldnull
  IL_0007:  cgt.un
  IL_0009:  ret
} // end of method Validator::IsValid

ストアとロードが最適化されているようです。以前のバージョンの .NET フレームワークをターゲットにすることで問題は解決しましたが、それはまぐれかもしれません。私はこの動作がやや不安であることがわかりました.コンパイラが、異なる観察可能な動作を生成する最適化を行うことが安全であると考える理由を誰か説明できますか?

前もって感謝します。

4

4 に答える 4

21

このバグは、Jacob Stanley によるこの SO の質問で既に発生しています。Jacob はすでにこのバグを報告しており、Microsoft はこれが実際に CLR JIT のバグであることを確認しています。マイクロソフトは次のように述べています。

このバグは、ランタイムの将来のバージョンで修正される予定です。残念ながら、それがサービス パックに含まれるのか、それとも次のメジャー リリースに含まれるのかを判断するには時期尚早です。

問題を報告していただき、重ねてお礼申し上げます。

に次の属性を追加することで、バグを回避できるはずですTestChecker()

[MethodImpl(MethodImplOptions.NoInlining)]
于 2011-04-04T20:26:33.287 に答える
16

C# コンパイラとは関係ありません。IL は同一です。.NET 4.0 ジッター オプティマイザーにバグが見つかりました。Visual Studio で再現できます。[ツール] + [オプション]、[デバッグ]、[全般] で、[モジュールの読み込み時に JIT 最適化を抑制する] オプションのチェックを外し、リリース ビルドを実行して失敗を再現します。

バグを特定するのに十分なほど詳しく調べていません。非常に奇妙に見えます。メソッドをインライン化し、ボクシング変換のコードを完全に省略しています。マシン コードは、バージョン 2 ジッターによって生成されたコードとは大幅に異なります。

きれいな回避策はそれほど簡単ではありません。インライン化を抑制することで実行できます。このような:

    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
    public bool IsDateTime(object o) {
        return o is DateTime;
    }

connect.microsoft.com でバグを報告できます。ご希望でない場合はお知らせください。対応させていただきます。


気にしないでください、それはすでに行われています。VS2010 SP1 に含まれるメンテナンス リリースでは修正されませんでした。


このバグは修正されており、再現できません。clrjit.dll の現在のバージョンは、2011 年 5 月 17 日付けの 4.0.30319.237 です。どのアップデートで修復されたのか正確にはわかりません。私は 2011 年 8 月 5 日にセキュリティ更新プログラムを取得しました。これは clrjit.dll をリビジョン 235 に更新し、4 月 12 日の日付が最も早いものでした。

于 2011-04-04T20:20:58.747 に答える
5

フロー制御に関する限り、ストアとロードは基本的にノープですが、おそらく何らかの方法で一部の CPU キャッシュをマッサージします。実際のフローは、引数をスタックにロードし、それがインスタンス (null またはインスタンスを返す) であるかどうかを確認し、null をスタックにプッシュし、比較 (より大きい) して、ブール値がスタックに残されるようにするだけです。

JITter がそれを使って何をするかは、まったく別の話です (使用しているプラ​​ットフォームによって異なります。JITter は、パフォーマンスの名の下にあらゆる種類のクレイジーなことを行います (私たちのチームは最近、末尾呼び出しの最適化が最適化に変更されたため、打撃を受けました)。 GetCallingAssembly() を壊したドメイン境界を越えて) JITter が IsDateTime をインライン化している可能性があります。

また、リリース バージョンがわずかに異なるフレームワークを対象としている可能性もあるため、テスト アセンブリの DateTime はテスト済みアセンブリの DateTime ではありません。

あなたのコードが壊れている理由には答えられないことに気づきました。

于 2011-04-04T20:21:27.807 に答える
3

参考までにmonoで確認しました

  • Mono JIT コンパイラ バージョン 2.6.7 (Debian 2.6.7-3ubuntu1)
  • Mono JIT コンパイラ バージョン 2.8.2 (sehe/d1c74ad Fri Feb 18 21:46:52 CET 2011)

どちらも何の問題もありませんでした。これは、2.8.2 で最適化された IL です。

.method public hidebysig 
       instance default bool IsDateTime (object o)  cil managed 
{
    // Method begins at RVA 0x2130
    // Code size 10 (0xa)
    .maxstack 8
    IL_0000:  ldarg.1 
    IL_0001:  isinst [mscorlib]System.DateTime
    IL_0006:  ldnull 
    IL_0007:  cgt.un 
    IL_0009:  ret 
} // end of method Checker::IsDateTime

最適化なしはまったく同じです

この IL の mono のジット コードの結果は次のとおりです。

00000130 <TestData_Checker_IsDateTime_object>:
     130:       55                      push   %ebp
     131:       8b ec                   mov    %esp,%ebp
     133:       53                      push   %ebx
     134:       56                      push   %esi
     135:       83 ec 10                sub    $0x10,%esp
     138:       e8 00 00 00 00          call   13d <TestData_Checker_IsDateTime_object+0xd>
     13d:       5b                      pop    %ebx
     13e:       81 c3 03 00 00 00       add    $0x3,%ebx
     144:       8b 45 0c                mov    0xc(%ebp),%eax
     147:       89 45 f4                mov    %eax,-0xc(%ebp)
     14a:       8b 75 0c                mov    0xc(%ebp),%esi
     14d:       83 7d 0c 00             cmpl   $0x0,0xc(%ebp)
     151:       74 1a                   je     16d <TestData_Checker_IsDateTime_object+0x3d>
     153:       8b 45 f4                mov    -0xc(%ebp),%eax
     156:       8b 00                   mov    (%eax),%eax
     158:       8b 00                   mov    (%eax),%eax
     15a:       8b 40 08                mov    0x8(%eax),%eax
     15d:       8b 48 08                mov    0x8(%eax),%ecx
     160:       8b 93 10 00 00 00       mov    0x10(%ebx),%edx
     166:       33 c0                   xor    %eax,%eax
     168:       3b ca                   cmp    %edx,%ecx
     16a:       0f 45 f0                cmovne %eax,%esi
     16d:       85 f6                   test   %esi,%esi
     16f:       0f 97 c0                seta   %al
     172:       0f b6 c0                movzbl %al,%eax
     175:       8d 65 f8                lea    -0x8(%ebp),%esp
     178:       5e                      pop    %esi
     179:       5b                      pop    %ebx
     17a:       c9                      leave  
     17b:       c3                      ret    
     17c:       8d 74 26 00             lea    0x0(%esi,%eiz,1),%esi
于 2011-04-04T20:29:47.337 に答える