Подтвердить что ты не робот

Удаляет ли компилятор С#, если он инкапсулирует debug.writeline

У меня есть такой код:

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

Разве компилятор оптимизирует это, если я делаю сборку релиза? Или оценка остается и, следовательно, требует некоторого времени обработки?

4b9b3361

Ответ 1

Да, это происходит, по крайней мере, для вызова Debug. Я не вижу здесь, если компилятор JIT также удалил оценку if, но, я думаю, это происходит, поскольку уравнение не имеет никаких побочных эффектов.

Однако вам лучше сохранить его безопасным путем вызова Debug.WriteLineIf, который не зависит от компилятора JIT для удаления оценки.

Для полноты доказательства для компилятора удалить Debug.WriteLine.


Код в сборке Release:

.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

Как вы видите, в режиме Release нет вызова Debug.WriteLine, где работает режим Debug.

Ответ 2

От Страница MSDN в классе Debug:

Если вы используете методы класса Debug для печати информации отладки и проверки своей логики с помощью утверждений, вы можете сделать свой код более надежным, не влияя на производительность и размер кода вашего продукта доставки.

...

Атрибут ConditionalAttribute применяется к методам Debug. Компиляторы, которые поддерживают ConditionalAttribute, игнорируют вызовы этих методов, если только "DEBUG" не является условным символом компиляции.

Как вы можете видеть, компилятор опускает любые вызовы членам Debug для не-отладочных сборников. Тем не менее, это не остановит программу от проверки вашего оператора if. Если вы хотите, чтобы компилятор также проигнорировал оператор if, вы можете использовать директиву препроцессора, чтобы заключить весь блок следующим образом:

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

Ответ 3

Компилятор С# требуется спецификацией языка для удаления вызова Debug и оценки его аргументов.

Если .NET JIT был сложным JIT, он определил бы, что вызов метода строки не является побочным эффектом и может быть удален..NET JIT не очень сложный, поэтому на самом деле есть шанс, что он по-прежнему вызывает этот метод. Давайте узнаем.

Скомпилируйте программу в режиме деблокирования, декомпилируйте ее и запустите ее как x64 на 4.6.2 без отладки отладчика.

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

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

Компилятор С# оставил вызов неравенства строки неповрежденным:

введите описание изображения здесь

введите описание изображения здесь

Я не уверен, разрешает ли спецификация оптимизировать это, потому что это может быть побочный эффект. Не уверен, что компилятору разрешено предположить об этом.

Наш фантастический JIT также не удалил вызов:

введите описание изображения здесь

(1) является GetState() и (2) является string.!=.


Используйте Debug.WriteLineIf, потому что:

17.4.2.1 Условные методы Метод, украшенный атрибутом Conditional, является условным методом. Атрибут Conditional указывает условие, проверяя условный символ компиляции. Вызовы условного метода включаются или исключаются в зависимости от того, определен ли этот символ в точке вызова. Если символ определен, вызов включается; в противном случае вызов (включая оценку получателя и параметры вызова) опускается.