Interlocked.Incrementのパフォーマンス
-
06-07-2019 - |
質問
Interlocked.Increment(ref x)
は、さまざまなプラットフォームのintおよびlongの x ++
よりも速いですか、それとも遅いですか?
解決
これは、アクションをアトミックに発生させ、メモリバリアとして機能し、プロセッサが命令周辺のメモリアクセスを再順序付けする能力を排除するため、より低速です。
Interlocked.Incrementを使用する必要があります。スレッド間で共有できる状態でアクションをアトミックにする場合は、x ++を完全に置き換えることを意図していません。
他のヒント
私たちの経験では、WindowsでのInterlockedIncrement()などは非常に重要な影響を及ぼします。あるサンプルケースでは、インターロックを削除し、代わりに++ / ---を使用できました。これだけで、実行時間が140秒から110秒に短縮されました。私の分析では、インターロックはメモリの往復を強制します(そうでなければ、他のコアはそれをどのように見ることができますか?) L1キャッシュの読み取り/書き込みは約10クロックサイクルですが、メモリの読み取り/書き込みは100に近いです。
このサンプルケースでは、インクリメント/デクリメント操作の数を約10億と推定しました。 2Ghz CPUでは、これは++ /-では5秒、インターロックでは50秒になります。差を複数のスレッドに分散し、30秒近くになります。
少し考えてみてください。 Increment
呼び出しは、インクリメント演算子の単純なアプリケーションよりも速くなることはありません。もしそうなら、インクリメント演算子のコンパイラの実装は内部で Increment
を呼び出し、同じことを実行します。
しかし、あなたが自分でテストすることでわかるように、彼らは同じことを行いません。
2つのオプションの目的は異なります。通常、インクリメント演算子を使用します。操作をアトミックにする必要があり、その変数の他のすべてのユーザーもインターロック操作を使用していることが確実な場合は、 Increment
を使用します。 (それらがすべて協力していない場合、それは本当に助けになりません。)
遅いです。ただし、スカラー変数でスレッドセーフを実現するために知っている最もパフォーマンスの高い一般的な方法です。
CPUバスロックを実行する必要があるため、レジスタを更新するだけなので、常に遅くなります。ただし、最新のCPUはレジスタに近いパフォーマンスを実現しているため、リアルタイム処理でも無視できます。
私のパフォーマンステスト:
volatile:65,174,400
ロック:62,428,600
連動:113,248,900
TimeSpan span = TimeSpan.FromSeconds(5);
object syncRoot = new object();
long test = long.MinValue;
Do(span, "volatile", () => {
long r = Thread.VolatileRead(ref test);
r++;
Thread.VolatileWrite(ref test, r);
});
Do(span, "lock", () =>
{
lock (syncRoot)
{
test++;
}
});
Do(span, "interlocked", () =>
{
Interlocked.Increment(ref test);
});