論理的には、非常に短くて単純なメソッドは C# コンパイラによってインライン化されるため、単純にメソッドにコードを手動で入力する場合と比較してオーバーヘッドが発生しないと常に想定していました...
今日まで-さまざまなメソッドと手動でインライン化されたコードのベンチマークを試みたとき。(私にとって) 最も単純なコードでさえ、手動でインライン化されたものと比較してメソッド呼び出しのオーバーヘッドが発生することがわかりました。実際、インライン化されている
メソッドについて
の手がかりを見つけることができなかったので、簡単なテストを実行しました。
使用したシステム:
- インテル C2D E7200 (デュアルコア) 2.53GHz
- 4GB DDR2
- ウィンドウズ7 64ビット
- .NET 4.0
- Visual Studio 2010 アルティメット
すべてのテストは、デバッグなしでリリース構成 (コードの最適化)を使用して実行されました。
ベンチマークに使用したコードは次のとおりです。
static void Main()
{
const int iterations = 250000000; // 250 million iterations
Thread.Sleep(1000); // sleep for one second
var sw = new Stopwatch();
int s = 0;
sw.Start();
for (int i = 0; i < iterations; i++)
{
// incrementing s by 1 in various ways
}
sw.Stop();
Console.WriteLine("Time: {0}ms", sw.ElapsedMilliseconds);
}
[1]まず、単純なインクリメント コマンドのベンチマークを行いました。
// in Main
for (int i = 0; i < iterations; i++)
{
s = s + 1;
}
5 回実行した結果:
- 867ミリ秒
- 877ミリ秒
- 868ミリ秒
- 865ミリ秒
- 870ms
[2]メソッド呼び出しへの切り替え:
static int Increment(int a)
{
return a + 1;
}
...
// in Main
for (int i = 0; i < iterations; i++)
{
s = Increment(s);
}
5 回実行した結果:
- 2161ミリ秒
- 2159ミリ秒
- 2194ミリ秒
- 2177ミリ秒
- 2163ms
痛い!明らかに、メソッドのオーバーヘッドがあります。
リフレクションを使用して、メソッドMethodBase.GetCurrentMethod().Name
内から出力しようとしました。Increment
それは確かに印刷されていましたIncrement
-つまり、メソッドはインライン化されていません。
[MethodImpl(MethodImplOptions.NoInlining)]
次に、属性をメソッドに追加しようとしましたが、ベンチマーク時間はまったく同じままでした。
Debug モードで Optimize Code を false に設定すると、最初のテストはわずかに遅くなり、2 番目のテストは約 2 倍遅くなります。また、NoInlining属性はパフォーマンスに影響しません。
ここで何か間違ったことをしているのですか?そのような単純なメソッドでさえオーバーヘッドなしで機能させることができませんか? なぜこうなった?
確かにこれは予想される動作ではありません - それともそうですか?
注: Java での同様のテストでは、このようなメソッド呼び出しに対するオーバーヘッドは示されていません。(Eclipse + JDK 1.7 を使用すると、Java もこれでかなり高速になるようです。)