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