C#编译器是否删除了封装debug.writeline的if

时间:2016-12-14 12:52:17

标签: c# .net compiler-optimization

我有一段这样的代码:

//string
Session["LoggedInOk"] = "loginOk";
string value = Session["LoggedInOk"].ToString();

//bool
Session["LoggedInOk"] = true;
bool value = Session["LoggedInOk"] as bool;

//class
Book book = new Book();
Session["LoggedInOk"] = book;
Book value = Session["LoggedInOk"] as Book;

//datatable
DataTable table = new DataTable();
Session["LoggedInOk"] = table;
DataTable value = Session["LoggedInOk"] as DataTable;

如果我发布版本,编译器是否会对此进行优化?或者评估是否仍然存在,从而花费一些处理时间?

3 个答案:

答案 0 :(得分:48)

是的,至少在Debug电话中确实如此。我无法在这里看到JIT编译器是否也删除了if的评估,但我想这是因为方程式没有任何副作用。

但是,您最好通过调用Debug.WriteLineIf来保证安全,而Debug.WriteLine并不依赖于JIT编译器来删除评估。

为了完整性,编译器可以删除.method public hidebysig static void Main(string[] args) cil managed { .entrypoint // Code size 17 (0x11) .maxstack 8 IL_0000: call string [mscorlib]System.Console::ReadLine() IL_0005: ldstr "Ok" IL_000a: call bool [mscorlib]System.String::op_Inequality(string, string) IL_000f: pop IL_0010: ret } // end of method Program::Main

发布版本中的代码:

.method public hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       42 (0x2a)
  .maxstack  2
  .locals init ([0] string state,
           [1] bool V_1)
  IL_0000:  nop
  IL_0001:  call       string [mscorlib]System.Console::ReadLine()
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  ldstr      "Ok"
  IL_000d:  call       bool [mscorlib]System.String::op_Inequality(string,
                                                                   string)
  IL_0012:  stloc.1
  IL_0013:  ldloc.1
  IL_0014:  brfalse.s  IL_0029
  IL_0016:  nop
  IL_0017:  ldstr      "Error occured: {0}"
  IL_001c:  ldloc.0
  IL_001d:  call       string [mscorlib]System.String::Format(string,
                                                              object)
  IL_0022:  call       void [System]System.Diagnostics.Debug::WriteLine(string)
  IL_0027:  nop
  IL_0028:  nop
  IL_0029:  ret
} // end of method Program::Main

Debug build中的代码:

Debug.WriteLine

如您所见,Release模式没有调用 #pragma pack(1) struct Example { int foo; QVector<int> bar; }; ,调试模式的位置。

答案 1 :(得分:15)

来自preprocessor directive

  

如果您使用[Files] Source: "..\*.ext"; DestDir: "{tmp}\Test\"; \ AfterInstall: DoSomething; Flags: ignoreversion recursesubdirs createallsubdirs; [Code] procedure DoSomething; var Path: string; begin Path := CurrentFilename; { ... } end; 类中的方法来打印调试信息并使用断言检查逻辑,则可以使代码更加健壮,而不会影响运输产品的性能和代码大小。

     

...

     

Debug属性适用于ConditionalAttribute的方法。支持Debug的编译器忽略对这些方法的调用,除非“DEBUG”被定义为条件编译符号。

如您所见,编译器将忽略在非调试版本上对ConditionalAttribute成员的任何调用。但是,它不会阻止程序检查您的if语句。如果您希望编译器也忽略if语句,您可以使用{{3}}将整个块括起来:

Debug

答案 2 :(得分:4)

语言规范要求C#编译器删除Debug调用对其参数的评估。

如果.NET JIT是一个复杂的JIT,它将确定字符串方法调用不是副作用,可以删除。 .NET JIT不是很复杂,所以它实际上仍有可能调用该方法。让我们来看看。

在发布模式下编译程序,对其进行反编译并在4.6.2上以x64运行,而不会调优器抑制优化。

    static void Main()
    {
        var state = GetState();
        if (state != "Ok")
        {
            Debug.WriteLine(state);
        }
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    static string GetState()
    {
        return "x";
    }

C#编译器保持字符串不等式调用完整:

enter image description here

enter image description here

我不确定规范是否允许对此进行优化,因为这可能是一种副作用方法。不确定允许编译器对其进行假设。

我们出色的JIT也没有删除电话:

enter image description here

(1)是GetState(),(2)是string.!=

使用Debug.WriteLineIf因为:

  

17.4.2.1条件方法   使用Conditional属性修饰的方法是条件方法。 Conditional属性通过测试条件编译符号来指示条件。根据是否在调用点定义了此符号,可以包含或省略对条件方法的调用。如果定义了符号,则包括呼叫;否则,将忽略呼叫(包括接收机的评估和呼叫参数)。