try-catch の影響をテストするためのコードをいくつか書きましたが、驚くべき結果が得られました。
static void Main(string[] args)
{
Thread.CurrentThread.Priority = ThreadPriority.Highest;
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime;
long start = 0, stop = 0, elapsed = 0;
double avg = 0.0;
long temp = Fibo(1);
for (int i = 1; i < 100000000; i++)
{
start = Stopwatch.GetTimestamp();
temp = Fibo(100);
stop = Stopwatch.GetTimestamp();
elapsed = stop - start;
avg = avg + ((double)elapsed - avg) / i;
}
Console.WriteLine("Elapsed: " + avg);
Console.ReadKey();
}
static long Fibo(int n)
{
long n1 = 0, n2 = 1, fibo = 0;
n++;
for (int i = 1; i < n; i++)
{
n1 = n2;
n2 = fibo;
fibo = n1 + n2;
}
return fibo;
}
私のコンピューターでは、常に 0.96 前後の値が出力されます。
次のように、Fibo() 内の for ループを try-catch ブロックでラップします。
static long Fibo(int n)
{
long n1 = 0, n2 = 1, fibo = 0;
n++;
try
{
for (int i = 1; i < n; i++)
{
n1 = n2;
n2 = fibo;
fibo = n1 + n2;
}
}
catch {}
return fibo;
}
今では一貫して 0.69 を出力します... -- 実際にはより速く実行されます! しかし、なぜでしょうか?
注: これをリリース構成を使用してコンパイルし、EXE ファイルを直接実行しました (Visual Studio の外部)。
編集:ジョン・スキートの優れた分析この特定のケースでは、try-catch が何らかの形で x86 CLR に CPU レジスタをより有利な方法で使用させていることがわかります (その理由はまだわかっていないと思います)。Jon の調査結果によると、x64 CLR にはこの違いはなく、x86 CLR よりも高速でした。また、int
Fibo メソッド内でlong
types の代わりに型を使用してテストしたところ、x86 CLR は x64 CLR と同等の速度でした。
更新:この問題は Roslyn によって修正されたようです。同じマシン、同じ CLR バージョン - VS 2013 でコンパイルすると問題は上記のままですが、VS 2015 でコンパイルすると問題は解消されます。
ベストアンサー1
の1つロザリンスタック使用の最適化の理解を専門とするエンジニアがこれを調べ、C#コンパイラがローカル変数ストアを生成する方法と、ジットコンパイラは対応する x86 コードでレジスタ スケジューリングを実行します。その結果、ローカルのロードとストアで最適ではないコードが生成されます。
私たち全員にとって不明な何らかの理由で、ブロックが try 保護領域内にあることを JITter が認識すると、問題のあるコード生成パスは回避されます。
これはかなり奇妙です。JITter チームに連絡して、バグを登録してもらい、修正してもらうことができるかどうかを確認します。
また、私たちは、ローカルを「一時的」にできるタイミング (つまり、アクティブ化の期間中スタック上の特定の場所を割り当てるのではなく、スタックにプッシュおよびポップするだけ) を決定するための Roslyn の C# および VB コンパイラのアルゴリズムの改善に取り組んでいます。ローカルを「無効」にできるタイミングについてより適切なヒントを JITter に提供すれば、レジスタ割り当てなどの処理がさらに効率化されると考えています。
この件を報告していただきありがとうございます。また、奇妙な行動をお詫び申し上げます。