C# · 12月 26, 2021

c# – 编译器何时优化我的代码

I’m trying to build a code sample显示编译器与2个数字相乘时的代码优化.然而当我转向IL上的优化代码仍然主要是相同的.任何想法我在这里做错什么?

代码:

int nr;int result;var stopwatch = new Stopwatch();nr = 5;stopwatch.Start(); result = nr * 4;stopwatch.Stop();Console.WriteLine(result);Console.WriteLine(stopwatch.Elapsed.ToString() + “ms ellapsed”);stopwatch.Reset();stopwatch.Start();result = nr << 2;stopwatch.Stop();Console.WriteLine(result);Console.WriteLine(stopwatch.Elapsed.ToString() + “ms ellapsed”);

非优化IL:

.method private hidebysig static void Main(string[] args) cil managed{ .entrypoint // Code size 130 (0x82) .maxstack 2 .locals init ([0] int32 nr,[1] int32 result,[2] class [System]System.Diagnostics.Stopwatch stopwatch,[3] valuetype [mscorlib]System.TimeSpan CS$0$0000,[4] valuetype [mscorlib]System.TimeSpan CS$0$0001) IL_0000: newobj instance void [System]System.Diagnostics.Stopwatch::.ctor() IL_0005: stloc.2 IL_0006: ldc.i4.5 IL_0007: stloc.0 IL_0008: ldloc.2 IL_0009: callvirt instance void [System]System.Diagnostics.Stopwatch::Start() IL_000e: ldloc.0 IL_000f: ldc.i4.4 IL_0010: mul IL_0011: stloc.1 IL_0012: ldloc.2 IL_0013: callvirt instance void [System]System.Diagnostics.Stopwatch::Stop() IL_0018: ldloc.1 IL_0019: call void [mscorlib]System.Console::WriteLine(int32) IL_001e: ldloc.2 IL_001f: callvirt instance valuetype [mscorlib]System.TimeSpan [System]System.Diagnostics.Stopwatch::get_Elapsed() IL_0024: stloc.3 IL_0025: ldloca.s CS$0$0000 IL_0027: constrained. [mscorlib]System.TimeSpan IL_002d: callvirt instance string [mscorlib]System.Object::ToString() IL_0032: ldstr “ms ellapsed” IL_0037: call string [mscorlib]System.String::Concat(string,string) IL_003c: call void [mscorlib]System.Console::WriteLine(string) IL_0041: ldloc.2 IL_0042: callvirt instance void [System]System.Diagnostics.Stopwatch::Reset() IL_0047: ldloc.2 IL_0048: callvirt instance void [System]System.Diagnostics.Stopwatch::Start() IL_004d: ldloc.0 IL_004e: ldc.i4.2 IL_004f: shl IL_0050: stloc.1 IL_0051: ldloc.2 IL_0052: callvirt instance void [System]System.Diagnostics.Stopwatch::Stop() IL_0057: ldloc.1 IL_0058: call void [mscorlib]System.Console::WriteLine(int32) IL_005d: ldloc.2 IL_005e: callvirt instance valuetype [mscorlib]System.TimeSpan [System]System.Diagnostics.Stopwatch::get_Elapsed() IL_0063: stloc.s CS$0$0001 IL_0065: ldloca.s CS$0$0001 IL_0067: constrained. [mscorlib]System.TimeSpan IL_006d: callvirt instance string [mscorlib]System.Object::ToString() IL_0072: ldstr “ms ellapsed” IL_0077: call string [mscorlib]System.String::Concat(string,string) IL_007c: call void [mscorlib]System.Console::WriteLine(string) IL_0081: ret} // end of method Program::Main

优化IL:

.method private hidebysig static void Main(string[] args) cil managed{ .entrypoint // Code size 130 (0x82) .maxstack 2 .locals init ([0] int32 nr,string) IL_007c: call void [mscorlib]System.Console::WriteLine(string) IL_0081: ret} // end of method Program::Main

我以为编译器会将mul语句优化为一个shl语句?
我对IL的了解非常有限(如果不是不存在的话).

解决方法 这是由Release版本中的抖动生成的代码: 0000003e mov ecx,14h

当知道操作数值时,优化器太聪明,无法生成乘法代码.如果更换nr = 5;使用nr = int.Parse(“5”),使得抖动不能知道操作数值,然后生成此乘法的代码:

0000005c lea ebx,[rdi*4+00000000h]

这利用内置在cpu中的地址生成逻辑中的乘数,允许指令与另一个使用ALU的指令重叠.这使得乘法基本上是免费的.这是64位抖动的输出,32位抖动产生:

0000004d shl edi,2

这是你所希望的.我记录了this post中抖动执行的优化方式.