【问题标题】:When does the compiler optimize my code编译器何时优化我的代码
【发布时间】:2011-10-27 22:27:31
【问题描述】:

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,
           [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

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

【问题讨论】:

  • 如果mul 确实被优化为shl——我不知道它是否这样做——当IL 被嵌入特定平台时,它几乎肯定会完成代码。

标签: c# visual-studio compiler-optimization


【解决方案1】:

这是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 中记录了抖动执行的优化类型。

【讨论】:

    【解决方案2】:

    “优化”标志在 C# 到 IL 编译阶段的作用并不大。它确实有所作为,但不是为了这种事情。

    我希望 JIT 编译器可以处理这种优化。

    【讨论】:

    • 是否有说明 CSC 确实优化了哪些内容的文档或其他内容?
    • @zenwalker:我不知道 - 这是编译器团队应该能够在版本之间作为实现细节进行更改的事情。你可能会发现一些关于它的博客文章......
    • 谢谢你的答案。我认为这是一个基本的优化例程,用移位运算符代替乘法。我不认为 JIT 编译器会替换它,因为 multiply 语句比 shift 语句长 6 倍。
    猜你喜欢
    • 2016-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多