Nah, cara Anda mengatur waktu hal-hal tampak sangat buruk bagi saya. Akan jauh lebih masuk akal untuk mengatur waktu seluruh loop:
var stopwatch = Stopwatch.StartNew();
for (int i = 1; i < 100000000; i++)
{
Fibo(100);
}
stopwatch.Stop();
Console.WriteLine("Elapsed time: {0}", stopwatch.Elapsed);
Dengan begitu Anda tidak berada di bawah kekuasaan timing kecil, aritmatika floating point dan akumulasi kesalahan.
Setelah melakukan perubahan itu, lihat apakah versi "non-tangkapan" masih lebih lambat dari versi "tangkapan".
EDIT: Oke, saya sudah mencobanya sendiri - dan saya melihat hasil yang sama. Sangat aneh. Saya bertanya-tanya apakah mencoba / menangkap itu menonaktifkan beberapa inlining yang buruk, tetapi menggunakan[MethodImpl(MethodImplOptions.NoInlining)]
bukannya tidak membantu ...
Pada dasarnya Anda harus melihat kode JITted yang dioptimalkan di bawah cordbg, saya kira ...
EDIT: Beberapa bit informasi lagi:
- Menempatkan mencoba / menangkap hanya sekitar
n++;
garis masih meningkatkan kinerja, tetapi tidak sebanyak menempatkannya di seluruh blok
- Jika Anda menangkap pengecualian tertentu (
ArgumentException
dalam pengujian saya) itu masih cepat
- Jika Anda mencetak pengecualian di blok tangkap, itu masih cepat
- Jika Anda mengubah kembali pengecualian di blok tangkap itu lambat lagi
- Jika Anda menggunakan blok akhirnya bukan blok menangkap itu lambat lagi
- Jika Anda menggunakan blok terakhir dan juga blok tangkap, itu cepat
Aneh...
EDIT: Oke, kami telah membongkar ...
Ini menggunakan kompiler C # 2 dan. NET 2 (32-bit) CLR, disassembling dengan mdbg (karena saya tidak punya cordbg di komputer saya). Saya masih melihat efek kinerja yang sama, bahkan di bawah debugger. Versi cepat menggunakan try
blok di sekitar segala sesuatu antara deklarasi variabel dan pernyataan kembali, hanya dengan catch{}
handler. Tentunya versi lambatnya sama kecuali tanpa coba / tangkap. Kode panggilan (yaitu Utama) adalah sama dalam kedua kasus, dan memiliki perwakilan perakitan yang sama (jadi ini bukan masalah inlining).
Kode yang dibongkar untuk versi cepat:
[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
Kode yang dibongkar untuk versi lambat:
[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
Dalam setiap kasus *
ditampilkan di mana debugger dimasukkan dalam "langkah-ke" sederhana.
EDIT: Oke, saya sekarang telah melihat melalui kode dan saya pikir saya bisa melihat bagaimana setiap versi bekerja ... dan saya percaya versi lebih lambat lebih lambat karena menggunakan register lebih sedikit dan lebih banyak ruang tumpukan. Untuk nilai kecil dari n
itu mungkin lebih cepat - tetapi ketika loop mengambil sebagian besar waktu, itu lebih lambat.
Mungkin blok coba / tangkap memaksa lebih banyak register untuk disimpan dan dipulihkan, sehingga JIT juga menggunakannya untuk loop ... yang terjadi untuk meningkatkan kinerja secara keseluruhan. Tidak jelas apakah ini merupakan keputusan yang masuk akal bagi JIT untuk tidak menggunakan sebanyak mungkin register dalam kode "normal".
EDIT: Baru saja mencoba ini di mesin x64 saya. CLR x64 jauh lebih cepat (sekitar 3-4 kali lebih cepat) daripada CLR x86 pada kode ini, dan di bawah x64 blok try / catch tidak membuat perbedaan yang nyata.