了解编译器优化

时间:2019-01-18 17:28:50

标签: c# .net compiler-construction compiler-optimization

我试图了解编译器对非常简单的代码所做的工作:

if (group.ImageHeight > 1 && group.ImageWidth > 1)
{ //No code exists between the braces
}

Debug配置进行编译后,然后进行反编译,我看到以下内容:

if (group.ImageHeight <= 1 || group.ImageWidth <= 1);

反编译Release配置会导致

if (group.ImageHeight > 1)
{
  int imageWidth = group.ImageWidth;
}

更完整的(原始)代码:

public class Group 
{
  public int ImageHeight { get; set; }
  public int ImageWidth { get; set; }
}

//The following belongs to a different project than `Group`
static void Main(string[] args)
{
  Group group = new Group();
  MyMethod(group);
}
static void MyMethod(Group group)
{
    if (group.ImageHeight > 1 && group.ImageWidth > 1)
    { 
    }
}

这是我到目前为止的猜测和发现:

  • 当我第一次开始时,我希望编译器完全删除整个语句。我认为这不是因为对属性的评估可能会有副作用。
  • 我认为,group类型属于我的解决方案中的另一个项目很重要。我之所以这样说,是因为编译器可能无法“知道”将来评估属性的副作用。例如,我可以在编译后替换包含group定义的DLL。
  • Release配置中,可能的副作用似乎与我的代码相同:对ImageHeight进行了评估,如果满足> 1条件将对ImageWidth进行评估(尽管通过分配而不是比较)

现在,对于我的具体问题:

  • 为什么Release配置使用分配(int imageWidth = group.ImageWidth)而不是我的原始比较?运行作业更快吗?
  • 为什么Debug配置完全改变了副作用的可能性?在此配置中,ImageHeightImageWidth都将始终被评估。

1 个答案:

答案 0 :(得分:3)

For the first specific question. When you look at IL on sharplab.io The simple assignment is 1 compare instruction short. Whose "then" and "else" would point to the same instruction (in this case IL_0012) so compare there is not needed for calling function and two pops are enough. Weird is only loading the Int32 constant 1 which will be discarded immidiately.

if (group.ImageHeight > 1)

IL_0000: ldarg.0
IL_0001: callvirt instance int32 Group::get_ImageHeight()
IL_0006: ldc.i4.1
IL_0007: ble.s IL_0012

int imageWidth = group.ImageWidth;

IL_0009: ldarg.0
IL_000a: callvirt instance int32 Group::get_ImageWidth()
IL_000f: ldc.i4.1
IL_0010: pop
IL_0011: pop

IL_0012: ret

For second specific question. If you look at IL on the same page with Debug mode, you'll see, that the code is identical only with some additional instructions for debuging and the compare itself so you can watch the result of it in debuger.

IL_0000: nop
IL_0001: ldarg.0
IL_0002: callvirt instance int32 Group::get_ImageHeight()
IL_0007: ldc.i4.1
IL_0008: ble.s IL_0015

IL_000a: ldarg.0
IL_000b: callvirt instance int32 Group::get_ImageWidth()
IL_0010: ldc.i4.1
IL_0011: cgt
IL_0013: br.s IL_0016

IL_0015: ldc.i4.0

IL_0016: stloc.0
        // sequence point: hidden
IL_0017: ldloc.0
IL_0018: brfalse.s IL_001c

IL_001a: nop
IL_001b: nop

IL_001c: ret