你的問題是循環不變式分析之一 - 編譯器是否可以檢測某個表達式,該表達式不依賴於循環的狀態來進行評估,並且沒有si影響?
有充分的理由希望編譯器能夠同時滿足這兩個命題 - 編譯器可以足夠聰明地知道在枚舉上調用ToString()
不會改變;並且在枚舉上調用ToString()
沒有任何明顯的副作用。也許調用該函數在某種程度上比堆棧上存儲一個額外的變量更快 -
爲什麼編譯器將積極決定不弔不變有可能是原因。
問題歸結到它是否確實。
我使用VS2012目標的.Net 4.6編譯下面的程序,並啓用優化編譯。看來,編譯器沒有選擇扯起不變圈外:
public static void Main()
{
for(int i = 0; i < 10; i++)
{
Console.Out.WriteLine(i);
Console.Out.WriteLine(Test.Option1.ToString());
}
}
public enum Test
{
Option1,
Option2,
Option3
}
下面是我獲得的使用ILSpy 2.3.1程序的原始IL。請注意呼叫ToString()
,正好在循環中。
.method public hidebysig static
void Main() cil managed
{
.custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x2050
// Code size 46 (0x2e)
.maxstack 2
.entrypoint
.locals init (
[0] int32 i
)
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: br.s IL_0028
// loop start (head: IL_0028)
IL_0004: call class [mscorlib]System.IO.TextWriter [mscorlib]System.Console::get_Out()
IL_0009: ldloc.0
IL_000a: callvirt instance void [mscorlib]System.IO.TextWriter::WriteLine(int32)
IL_000f: call class [mscorlib]System.IO.TextWriter [mscorlib]System.Console::get_Out()
IL_0014: ldc.i4.0
IL_0015: box TestProject.Program/Test
---> IL_001a: callvirt instance string [mscorlib]System.Object::ToString()
IL_001f: callvirt instance void [mscorlib]System.IO.TextWriter::WriteLine(string)
IL_0024: ldloc.0
IL_0025: ldc.i4.1
IL_0026: add
IL_0027: stloc.0
IL_0028: ldloc.0
IL_0029: ldc.i4.s 10
IL_002b: blt.s IL_0004
// end loop
IL_002d: ret
} // end of method Program::Main
我也很好奇,看運行時JITer是否會提升不變量,但它似乎也不是。我改變了代碼以下,使裝配清潔:
public static void Main()
{
TextWriter cons = Console.Out;
for(int i = 0; i < 10; i++)
{
cons.WriteLine(i);
cons.WriteLine(Test.Option1.ToString());
}
}
,然後用於VS的調試器來組裝,小心以確保VS允許優化JITer。它仍然沒有提示ToString()的呼叫:
TextWriter cons = Console.Out;
00000000 push rdi
00000001 push rsi
00000002 sub rsp,28h
00000006 call 0000000050D76460
0000000b mov rsi,rax
for(int i = 0; i < 10; i++)
0000000e xor edi,edi
{
cons.WriteLine(i);
00000010 mov rcx,rsi
00000013 mov edx,edi
00000015 mov rax,qword ptr [rsi]
00000018 mov rax,qword ptr [rax+60h]
0000001c call qword ptr [rax+28h]
cons.WriteLine(Test.Option1.ToString());
0000001f mov rcx,7FE90116770h
00000029 call 000000005F6302D0
0000002e mov rcx,rsi
00000031 xor ecx,ecx
00000033 mov dword ptr [rax+8],ecx
00000036 mov rcx,rax
00000039 mov rax,qword ptr [rax]
0000003c mov rax,qword ptr [rax+40h]
00000040 call qword ptr [rax] <---- call System.Enum.ToString()
00000042 mov rdx,rax
00000045 mov rcx,rsi
00000048 mov rax,qword ptr [rsi]
0000004b mov rax,qword ptr [rax+68h]
0000004f call qword ptr [rax+20h]
for(int i = 0; i < 10; i++)
00000052 inc edi
00000054 cmp edi,0Ah
00000057 jl 0000000000000010
00000059 add rsp,28h
}
}
它如何優化它?的原因,它會要求每個項目! – Backs
@Backs'Test.OptionOne.ToString()'是一個永遠不會改變的固定字符串。它可以通過生成一個單一的常量字符串並對其進行優化,而不是在每個循環中生成一個新的字符串。 –
@Backs - 這並不真正有幫助,畢竟這是海報在這裏提出質疑的確切命題。你所做的一切都是在沒有提供任何證據或理性的情況下斷言一個職位。 – antiduh