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