さて、あなたが物事を計っている方法は私にはかなり厄介に見えます。ループ全体の時間を計測するほうがはるかに賢明です。
var stopwatch = Stopwatch.StartNew();
for (int i = 1; i < 100000000; i++)
{
    Fibo(100);
}
stopwatch.Stop();
Console.WriteLine("Elapsed time: {0}", stopwatch.Elapsed);
そうすれば、小さなタイミング、浮動小数点演算、蓄積されたエラーに翻弄されることはありません。
その変更を行った後、「非キャッチ」バージョンが「キャッチ」バージョンよりもまだ遅いかどうかを確認します。
編集:さて、私はそれを自分で試してみました-そして私は同じ結果を見ています 非常に奇妙な。try / catchがいくつかの悪いインライン化を無効にしているかどうか疑問に思いましたが、[MethodImpl(MethodImplOptions.NoInlining)]代わりに使用しても役に立たなかった...
基本的には、cordbgで最適化されたJITされたコードを確認する必要があると思います...
編集:情報のいくつかのビット:
- n++;行のみにtry / catchを配置しても、パフォーマンスは向上しますが、ブロック全体に配置するほどではありません
- (ArgumentException私のテストで)特定の例外をキャッチした場合、それはまだ高速です
- あなたがキャッチブロックで例外を出力するならば、それはまだ速いです
- キャッチブロックで例外を再スローすると、再び遅くなります
- catchブロックの代わりにfinallyブロックを使用すると、再び遅くなります
- finallyブロックと catchブロックを使用すると、高速です
奇妙な...
編集:さて、私たちは分解しています...
これはC#2コンパイラと.NET 2(32ビット)CLRを使用しており、mdbgを使用して逆アセンブルします(マシンにcordbgがないため)。デバッガーでも、同じパフォーマンス効果が表示されます。高速バージョンではtry、catch{}ハンドラーのみで、変数宣言とreturnステートメントの間のすべてを囲むブロックを使用します。明らかに遅いバージョンは、try / catchがないことを除いて同じです。呼び出しコード(メインなど)はどちらの場合も同じで、アセンブリ表現も同じです(インライン化の問題ではありません)。
高速バージョンの逆アセンブルされたコード:
 [0000] push        ebp
 [0001] mov         ebp,esp
 [0003] push        edi
 [0004] push        esi
 [0005] push        ebx
 [0006] sub         esp,1Ch
 [0009] xor         eax,eax
 [000b] mov         dword ptr [ebp-20h],eax
 [000e] mov         dword ptr [ebp-1Ch],eax
 [0011] mov         dword ptr [ebp-18h],eax
 [0014] mov         dword ptr [ebp-14h],eax
 [0017] xor         eax,eax
 [0019] mov         dword ptr [ebp-18h],eax
*[001c] mov         esi,1
 [0021] xor         edi,edi
 [0023] mov         dword ptr [ebp-28h],1
 [002a] mov         dword ptr [ebp-24h],0
 [0031] inc         ecx
 [0032] mov         ebx,2
 [0037] cmp         ecx,2
 [003a] jle         00000024
 [003c] mov         eax,esi
 [003e] mov         edx,edi
 [0040] mov         esi,dword ptr [ebp-28h]
 [0043] mov         edi,dword ptr [ebp-24h]
 [0046] add         eax,dword ptr [ebp-28h]
 [0049] adc         edx,dword ptr [ebp-24h]
 [004c] mov         dword ptr [ebp-28h],eax
 [004f] mov         dword ptr [ebp-24h],edx
 [0052] inc         ebx
 [0053] cmp         ebx,ecx
 [0055] jl          FFFFFFE7
 [0057] jmp         00000007
 [0059] call        64571ACB
 [005e] mov         eax,dword ptr [ebp-28h]
 [0061] mov         edx,dword ptr [ebp-24h]
 [0064] lea         esp,[ebp-0Ch]
 [0067] pop         ebx
 [0068] pop         esi
 [0069] pop         edi
 [006a] pop         ebp
 [006b] ret
遅いバージョンの逆アセンブルされたコード:
 [0000] push        ebp
 [0001] mov         ebp,esp
 [0003] push        esi
 [0004] sub         esp,18h
*[0007] mov         dword ptr [ebp-14h],1
 [000e] mov         dword ptr [ebp-10h],0
 [0015] mov         dword ptr [ebp-1Ch],1
 [001c] mov         dword ptr [ebp-18h],0
 [0023] inc         ecx
 [0024] mov         esi,2
 [0029] cmp         ecx,2
 [002c] jle         00000031
 [002e] mov         eax,dword ptr [ebp-14h]
 [0031] mov         edx,dword ptr [ebp-10h]
 [0034] mov         dword ptr [ebp-0Ch],eax
 [0037] mov         dword ptr [ebp-8],edx
 [003a] mov         eax,dword ptr [ebp-1Ch]
 [003d] mov         edx,dword ptr [ebp-18h]
 [0040] mov         dword ptr [ebp-14h],eax
 [0043] mov         dword ptr [ebp-10h],edx
 [0046] mov         eax,dword ptr [ebp-0Ch]
 [0049] mov         edx,dword ptr [ebp-8]
 [004c] add         eax,dword ptr [ebp-1Ch]
 [004f] adc         edx,dword ptr [ebp-18h]
 [0052] mov         dword ptr [ebp-1Ch],eax
 [0055] mov         dword ptr [ebp-18h],edx
 [0058] inc         esi
 [0059] cmp         esi,ecx
 [005b] jl          FFFFFFD3
 [005d] mov         eax,dword ptr [ebp-1Ch]
 [0060] mov         edx,dword ptr [ebp-18h]
 [0063] lea         esp,[ebp-4]
 [0066] pop         esi
 [0067] pop         ebp
 [0068] ret
いずれの場合*も、デバッガーが単純な「ステップイン」で入力した場所を示しています。
編集:さて、私はコードを調べました、そして各バージョンがどのように機能するかを見ることができると思います...そして遅いバージョンはより少ないレジスタとより多くのスタック空間を使用するので遅いと思います。値が小さい場合はn高速になる可能性がありますが、ループが時間の大半を占める場合は遅くなります。
おそらく、try / catchブロックは、より多くのレジスターを強制的に保存および復元するため、JITはそれらもループに使用します...これにより、全体的なパフォーマンスが向上します。JIT が「通常の」コードで多くのレジスタを使用しないことが合理的な決定であるかどうかは明らかではありません。
編集:ちょうど私のx64マシンでこれを試してみました。x64 CLRは、このコードのx86 CLRよりもはるかに高速(約3〜4倍高速)であり、x64では、try / catchブロックは顕著な違いを生じません。