[英]Is there a performance difference between returning inside if block, or writing if/else block?
我正在查看我的項目以重構一些代碼,並意識到我已經寫了如下代碼:
if(errorCode > 0)
{
DisplayError(errorCode);
return;
}
// Continue to do stuff otherwise.
您可能已經猜到了,該函數的返回類型為void。 當我開始研究時,我在思考將其放入if / else塊之間是否有真正的區別:
if(errorCode > 0)
{
DisplayError(errorCode);
}
else
{
// Do other stuff
}
else塊將一直持續到函數結束,因此控制流程本質上是相同的。 這里是否存在性能差異,或者應該使用約定,還是這兩個確實完全相同?
兩種情況下生成的代碼完全相同。
(在第一個示例中,您沒有在代碼中加上括號,但是我只是假設這是一個錯字,實際上您是在使用return
和else
來詢問之間的區別。)
如果您查看為這兩種方法生成的代碼:
public static void Test1(int errorCode) {
if (errorCode > 0) {
Console.WriteLine(errorCode);
return;
}
Console.WriteLine("ok");
}
public static void Test2(int errorCode) {
if (errorCode > 0) {
Console.WriteLine(errorCode);
} else {
Console.WriteLine("ok");
}
}
它看起來像這樣:
if (errorCode > 0) {
011A00DA in al,dx
011A00DB push eax
011A00DC mov dword ptr [ebp-4],ecx
011A00DF cmp dword ptr ds:[10F3178h],0
011A00E6 je 011A00ED
011A00E8 call 7470C310
011A00ED cmp dword ptr [ebp-4],0
011A00F1 jle 011A0100
Console.WriteLine(errorCode);
011A00F3 mov ecx,dword ptr [ebp-4]
011A00F6 call 73C5A920
return;
011A00FB nop
011A00FC mov esp,ebp
011A00FE pop ebp
011A00FF ret
}
Console.WriteLine("ok");
011A0100 mov ecx,dword ptr ds:[3E92190h]
011A0106 call 7359023C
}
011A010B nop
011A010C mov esp,ebp
011A010E pop ebp
011A010F ret
和:
if (errorCode > 0) {
011A0122 in al,dx
011A0123 push eax
011A0124 mov dword ptr [ebp-4],ecx
011A0127 cmp dword ptr ds:[10F3178h],0
011A012E je 011A0135
011A0130 call 7470C310
011A0135 cmp dword ptr [ebp-4],0
011A0139 jle 011A0148
Console.WriteLine(errorCode);
011A013B mov ecx,dword ptr [ebp-4]
011A013E call 73C5A920
011A0143 nop
011A0144 mov esp,ebp
011A0146 pop ebp
011A0147 ret
} else {
Console.WriteLine("ok");
011A0148 mov ecx,dword ptr ds:[3E92190h]
011A014E call 7359023C
}
}
011A0153 nop
011A0154 mov esp,ebp
011A0156 pop ebp
011A0157 ret
直到最后一條指令為止,生成的代碼完全相同。
無關緊要的是,您也可以使用出色的LinqPad進行IL拆卸,但是要評論太大。
有趣的是,在調試模式下僅分解到IL為止,Guffa的Test2 IL中有幾個額外的nop ,它們按照Guffa的答案在x86 / x64 JIT中進行編譯:
調試IL:
Test1:
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldc.i4.0
IL_0003: cgt
IL_0005: ldc.i4.0
IL_0006: ceq
IL_0008: stloc.0 // CS$4$0000
IL_0009: ldloc.0 // CS$4$0000
IL_000A: brtrue.s IL_0016
IL_000C: nop
IL_000D: ldarg.0
IL_000E: call System.Console.WriteLine
IL_0013: nop
IL_0014: br.s IL_0021
IL_0016: ldstr "ok"
IL_001B: call System.Console.WriteLine
IL_0020: nop
IL_0021: ret
Test2:
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldc.i4.0
IL_0003: cgt
IL_0005: ldc.i4.0
IL_0006: ceq
IL_0008: stloc.0 // CS$4$0000
IL_0009: ldloc.0 // CS$4$0000
IL_000A: brtrue.s IL_0017
IL_000C: nop
IL_000D: ldarg.0
IL_000E: call System.Console.WriteLine
IL_0013: nop
IL_0014: nop <-- Extra
IL_0015: br.s IL_0024
IL_0017: nop <-- Extra
IL_0018: ldstr "ok"
IL_001D: call System.Console.WriteLine
IL_0022: nop
IL_0023: nop <-- Extra
IL_0024: ret
IMO非常有趣,因為從處理器來看,如果處理器nop's
實際上被執行,則是浪費的,但是如果填充它們以允許跳轉到達后續對齊邊界,則這是有益的。 這就引出了一個問題,即為什么IL完全對nops
持保留意見,因為填充優化應該只涉及JIT編譯器。 答案似乎無關,大括號,可能是出於調試原因
對於發布模式,Guffa是正確的-在發布模式下編譯IL會丟棄IL中的所有nop-IL nops完全存在於其中以允許調試中斷/對括號進行逐步調試。 抖動重新引入了x86點,以實現均勻指令對齊,並且兩種方法是相同的。
發行版IL(Test1和Test2):
IL_0000: ldarg.0
IL_0001: ldc.i4.0
IL_0002: ble.s IL_000B
IL_0004: ldarg.0
IL_0005: call System.Console.WriteLine
IL_000A: ret
IL_000B: ldstr "ok"
IL_0010: call System.Console.WriteLine
IL_0015: ret
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.