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

fly*_*bug 38 .net c# compiler-optimization

我有一段这样的代码:

if (state != "Ok")
{
     Debug.WriteLine($"Error occured: {state}, {moreInfo}");
}
Run Code Online (Sandbox Code Playgroud)

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

Pat*_*man 48

是的,它确实如此,至少对于Debug电话而言.我在这里看不到JIT编译器是否也删除了它的评估if,但我想它确实如此,因为该等式没有任何副作用.

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

为了完整性,编译器要删除的证明Debug.WriteLine.


发布版本中的代码:

.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
Run Code Online (Sandbox Code Playgroud)

Debug构建中的代码:

.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
Run Code Online (Sandbox Code Playgroud)

如您所见,Release模式没有调用Debug.WriteLine,Debug模式的调用方式.

  • JIT确实是一个黑盒子,它没有提供任何明确的/文件证明保证它将删除if语句,但你当然可以*凭经验*验证它是否存在.在调试器下运行已编译的代码并查看反汇编.确保在调试器下运行时启用JIT优化; 我不记得它是否在VS中默认关闭. (8认同)
  • Release代码仍然调用`System.String :: op_Inequality`.我会说它仍然会评估`if`语句,但它被视为有一个空体. (6认同)
  • 不是我所知道的.这是一个黑盒子. (4认同)
  • 那么JIT删除的证据在哪里,你_demands_? (2认同)

Abi*_*n47 15

Debug类的MSDN页面:

如果您使用Debug类中的方法来打印调试信息并使用断言检查逻辑,则可以使代码更加健壮,而不会影响运输产品的性能和代码大小.

...

ConditionalAttribute属性应用于方法Debug.ConditionalAttribute除非"DEBUG"被定义为条件编译符号,否则支持忽略对这些方法的调用的编译器.

如您所见,编译器将忽略Debug对非调试版本的成员的任何调用.但是,它不会阻止程序检查您的if语句.如果您希望编译器也忽略if语句,您可以使用预处理器指令将整个块包含在内,如下所示:

#if DEBUG
if (state != "Ok")
{
    Debug.WriteLine($"Error occured: {state}, {moreInfo}");
}
#endif
Run Code Online (Sandbox Code Playgroud)


usr*_*usr 5

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";
    }
Run Code Online (Sandbox Code Playgroud)

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

在此处输入图片说明

在此处输入图片说明

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

我们出色的 JIT 也没有删除调用:

在此处输入图片说明

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


使用Debug.WriteLineIf原因:

17.4.2.1 条件方法 用 Conditional 属性修饰的方法是条件方法。Conditional 属性通过测试条件编译符号来指示条件。包含或省略对条件方法的调用取决于此符号是否在调用点定义。如果定义了符号,则包括调用;否则,调用(包括接收者的评估和调用的参数)将被省略。