Удаляет ли компилятор С#, если он инкапсулирует debug.writeline
У меня есть такой код:
if (state != "Ok")
{
Debug.WriteLine($"Error occured: {state}, {moreInfo}");
}
Разве компилятор оптимизирует это, если я делаю сборку релиза? Или оценка остается и, следовательно, требует некоторого времени обработки?
Ответы
Ответ 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 указывает условие, проверяя условный символ компиляции. Вызовы условного метода включаются или исключаются в зависимости от того, определен ли этот символ в точке вызова. Если символ определен, вызов включается; в противном случае вызов (включая оценку получателя и параметры вызова) опускается.