私はこのようなコードを持っています:
if (state != "Ok")
{
Debug.WriteLine($"Error occured: {state}, {moreInfo}");
}
リリースビルドを作成すると、コンパイラはこれを最適化しますか?または、評価が残っているため、処理時間がかかりますか?
はい、少なくとも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
デバッグビルドのコード:
.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.WriteLine
への呼び出しがありません。デバッグモードとは異なります。
From デバッグクラスのMSDNのページ:
Debug
クラスのメソッドを使用してデバッグ情報を出力し、アサーションを使用してロジックを確認すると、出荷される製品のパフォーマンスとコードサイズに影響を与えることなく、コードをより堅牢にすることができます。...
ConditionalAttribute
属性は、Debug
のメソッドに適用されます。ConditionalAttribute
をサポートするコンパイラは、 "DEBUG"が条件付きコンパイルシンボルとして定義されていない限り、これらのメソッドの呼び出しを無視します。
ご覧のように、コンパイラーは非デバッグビルドでのDebug
メンバーへの呼び出しを省略します。ただし、プログラムがifステートメントをチェックするのを妨げることはありません。コンパイラーがifステートメントも無視するようにしたい場合は、 プリプロセッサーディレクティブ を使用して、次のようにブロック全体を囲むことができます。
#if DEBUG
if (state != "Ok")
{
Debug.WriteLine($"Error occured: {state}, {moreInfo}");
}
#endif
言語仕様では、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#コンパイラは文字列不等式呼び出しをそのまま残しました:
仕様がこれを最適化することを許可するかどうかはわかりません。これは副作用のメソッドになる可能性があるためです。コンパイラがそれについて何を想定できるかわからない。
私たちの素晴らしいJITも呼び出しを削除しませんでした:
(1)はGetState()
で、(2)は_string.!=
_です。
_Debug.WriteLineIf
_を使用する理由:
17.4.2.1条件付きメソッドConditional属性で修飾されたメソッドは、条件付きメソッドです。条件付き属性は、条件付きコンパイルシンボルをテストして条件を示します。条件付きメソッドの呼び出しは、このシンボルが呼び出しの時点で定義されているかどうかに応じて、含まれるか省略されます。シンボルが定義されている場合、呼び出しが含まれます。それ以外の場合、呼び出し(呼び出しのレシーバーとパラメーターの評価を含む)は省略されます。