วิธีที่คุณใช้เวลาทำสิ่งต่าง ๆ ดูน่ารังเกียจสำหรับฉัน มันจะมีเหตุผลมากขึ้นที่จะแค่วนรอบทั้งหมด:
var stopwatch = Stopwatch.StartNew();
for (int i = 1; i < 100000000; i++)
{
Fibo(100);
}
stopwatch.Stop();
Console.WriteLine("Elapsed time: {0}", stopwatch.Elapsed);
ด้วยวิธีนี้คุณไม่ได้อยู่ในความเมตตาของการกำหนดเวลาเล็ก ๆ เลขทศนิยมและข้อผิดพลาดสะสม
เมื่อทำการเปลี่ยนแปลงแล้วให้ดูว่ารุ่น "ที่ไม่จับ" ยังคงช้ากว่ารุ่น "ที่จับได้"
แก้ไข: โอเคฉันได้ลองด้วยตัวเองแล้ว - และฉันก็เห็นผลลัพธ์เดียวกัน แปลกมาก. ฉันสงสัยว่าการลอง / จับปิดการใช้งานอินไลน์ที่ไม่ดีบางอย่าง แต่การใช้งาน[MethodImpl(MethodImplOptions.NoInlining)]
แทนไม่ได้ช่วย ...
โดยทั่วไปคุณจะต้องดูรหัส JITted ที่ดีที่สุดภายใต้ Cordbg ฉันสงสัยว่า ...
แก้ไข: ข้อมูลอีกไม่กี่บิต:
- การลอง / จับรอบ ๆ
n++;
เส้นก็ยังช่วยเพิ่มประสิทธิภาพ แต่ไม่มากเท่ากับการวางไว้รอบบล็อกทั้งหมด
- หากคุณพบข้อยกเว้นเฉพาะ (
ArgumentException
ในการทดสอบของฉัน) ก็ยังเร็ว
- หากคุณพิมพ์ข้อยกเว้นใน catch catch มันยังคงรวดเร็ว
- หากคุณสร้างข้อยกเว้นขึ้นใหม่ในบล็อก catch มันจะช้าอีกครั้ง
- หากคุณใช้บล็อกในที่สุดแทนบล็อกจับมันจะช้าอีกครั้ง
- หากคุณใช้บล็อกในที่สุดเช่นเดียวกับบล็อก catch มันเร็ว
แปลก...
แก้ไข: โอเคเรามีชิ้นส่วน ...
นี่ใช้คอมไพเลอร์ C # 2 และ CLR .NET 2 (32 บิต) แยกส่วนด้วย mdbg (เนื่องจากฉันไม่มี cordbg ในเครื่องของฉัน) ฉันยังคงเห็นเอฟเฟกต์ประสิทธิภาพเดียวกันแม้จะอยู่ภายใต้ดีบักเกอร์ รุ่นที่รวดเร็วใช้try
บล็อกรอบ ๆ ทุกสิ่งระหว่างการประกาศตัวแปรและคำสั่ง return โดยมีเพียงcatch{}
ตัวจัดการ เห็นได้ชัดว่ารุ่นช้านั้นเหมือนกันยกเว้นโดยไม่มีการลอง / จับ รหัสการโทร (เช่น Main) นั้นเหมือนกันในทั้งสองกรณีและมีการแสดงแอสเซมบลีที่เหมือนกัน
ถอดรหัสสำหรับรุ่นที่รวดเร็ว:
[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
ในแต่ละกรณีการ*
แสดงที่ debugger ป้อนใน "ขั้นตอน" ง่าย ๆ
แก้ไข: โอเคตอนนี้ฉันดูรหัสแล้วและฉันคิดว่าฉันสามารถดูว่าแต่ละรุ่นทำงานอย่างไรและฉันเชื่อว่ารุ่นที่ช้ากว่านั้นช้ากว่าเพราะใช้การลงทะเบียนน้อยลงและมีพื้นที่สแต็คมากขึ้น สำหรับค่าขนาดเล็กn
ที่อาจเร็วกว่า - แต่เมื่อลูปใช้เวลาเป็นกลุ่มมันจะช้ากว่า
เป็นไปได้ว่าบล็อก try / catch บังคับให้รีจิสเตอร์ถูกบันทึกและเรียกคืนมากขึ้นดังนั้น JIT จึงใช้สิ่งเหล่านั้นสำหรับลูปเช่นกัน ... ซึ่งเกิดขึ้นเพื่อปรับปรุงประสิทธิภาพโดยรวม ยังไม่ชัดเจนว่าเป็นการตัดสินใจที่สมเหตุสมผลสำหรับ JIT ที่จะไม่ใช้การลงทะเบียนมากในรหัส "ปกติ"
แก้ไข: เพิ่งลองสิ่งนี้ในเครื่อง x64 ของฉัน x64 CLR นั้นเร็วกว่ามาก (ประมาณ 3-4 เท่า) กว่า x86 CLR ในรหัสนี้และภายใต้ x64 บล็อค try / catch ไม่ได้สร้างความแตกต่างที่เห็นได้ชัดเจน