web-dev-qa-db-ja.com

ロック文はどれくらい高価ですか?

私はマルチスレッドと並列処理を試してきましたが、処理の速度の基本的なカウントと統計分析を行うためにカウンターが必要でした。クラスの同時使用に関する問題を回避するために、クラス内のプライベート変数でロックステートメントを使用しました。

private object mutex = new object();

public void Count(int amount)
{
 lock(mutex)
 {
  done += amount;
 }
}

しかし、私は疑問に思っていました...変数のロックはどれくらい高価ですか?パフォーマンスへの悪影響は何ですか?

98
Kees C. Bakker

記事 はコストになります。短い答えは50nsです。

72
Jake Pearson

技術的な答えは、これを定量化することは不可能であり、CPUメモリのライトバックバッファの状態と、プリフェッチャーが収集したデータを破棄して再読み取りする量に大きく依存するということです。どちらも非常に非決定的です。主要な失望を回避するために、150 CPUサイクルをエンベロープの裏側近似として使用します。

実用的な答えは、waaaayロックをスキップできると思うときにコードのデバッグに費やす時間よりも安いということです。

ハード番号を取得するには、測定する必要があります。 Visual Studioには、洗練された 同時実行アナライザー が拡張機能として用意されています。

48
Hans Passant

参考文献:

一般的な同期プリミティブに興味があり、個別のシナリオとスレッド数に応じてモニター、C#ロックステートメントの動作、プロパティ、およびコストを掘り下げている記事をいくつか紹介したいと思います。 CPUの浪費とスループット期間に特に関心があるのは、複数のシナリオでどれだけの作業をプッシュできるかを理解することです。

https://www.codeproject.com/Articles/1236238/Unified-Concurrency-I-Introductionhttps://www.codeproject.com/Articles/1237518/Unified-Concurrency- II-benchmarking-methodologieshttps://www.codeproject.com/Articles/1242156/Unified-Concurrency-III-cross-benchmarking

元の回答:

まあ!

THE ANSWERは本質的に間違っているため、正解はここにフラグが立てられているようです!回答の著者に、リンクされた記事を最後まで読むようお願いします。 記事

2003年の記事の著者 記事 はデュアルコアマシンでのみ測定し、最初の測定ケースでは、彼はシングルスレッドのみでロックを測定しました結果はロックアクセスごとに約50nsでした。

並行環境でのロックについては何も述べていません。そのため、この記事を読み続ける必要があり、後半では、著者は2つと3つのスレッドでロックシナリオを測定しました。これは、今日のプロセッサの同時実行レベルに近づきます。

そこで著者は、デュアルコアの2つのスレッドではロックに120nsかかり、3つのスレッドでは180nsになると述べています。したがって、ロックに同時にアクセスするスレッドの数に明らかに依存しているようです。

したがって、単純であり、ロックが役に立たないシングルスレッドでない限り、50 nsではありません。

考慮すべき別の問題は、平均時間として測定されることです!

反復時間を測定すると、1ミリ秒から20ミリ秒までの偶数時間になります。これは、大部分が高速だったからです。しかし、プロセッサー時間を待機しているスレッドはほとんどなく、ミリ秒の長い遅延が発生します。

これは、高スループット、低遅延を必要とするあらゆる種類のアプリケーションにとって悪いニュースです。

そして、考慮すべき最後の問題は、ロック内の操作が遅くなる可能性があるということです。コードブロックがロック内で実行される時間が長くなるほど、競合が高くなり、空の上昇が遅れます。

2003年からすでに10年以上が経過していることを考慮してください。完全に同時に実行するように特別に設計された数世代のプロセッサであり、ロックがパフォーマンスを大幅に低下させます。

25
ipavlu

これはパフォーマンスに関する質問には答えませんが、.NET Frameworkには Interlocked.Add 別のオブジェクトを手動でロックせずにamountメンバーにdoneを追加できるメソッド。

20
Adam Maras

lock(Monitor.Enter/Exit)は非常に安価で、WaithandleやMutexなどの代替品よりも安価です。

しかし、もしそれが(少し)遅いなら、あなたはむしろ間違った結果の速いプログラムを持っているでしょうか?

10
Henk Holterman

ロックのない代替と比較して、タイトループでのロックのコストは莫大です。何回もループしても、ロックよりも効率的です。そのため、ロックフリーキューが非常に効率的です。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LockPerformanceConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            var stopwatch = new Stopwatch();
            const int LoopCount = (int) (100 * 1e6);
            int counter = 0;

            for (int repetition = 0; repetition < 5; repetition++)
            {
                stopwatch.Reset();
                stopwatch.Start();
                for (int i = 0; i < LoopCount; i++)
                    lock (stopwatch)
                        counter = i;
                stopwatch.Stop();
                Console.WriteLine("With lock: {0}", stopwatch.ElapsedMilliseconds);

                stopwatch.Reset();
                stopwatch.Start();
                for (int i = 0; i < LoopCount; i++)
                    counter = i;
                stopwatch.Stop();
                Console.WriteLine("Without lock: {0}", stopwatch.ElapsedMilliseconds);
            }

            Console.ReadKey();
        }
    }
}

出力:

With lock: 2013
Without lock: 211
With lock: 2002
Without lock: 210
With lock: 1989
Without lock: 210
With lock: 1987
Without lock: 207
With lock: 1988
Without lock: 208
6
Johan Nilsson

「コスト」を定義する方法はいくつかあります。ロックの取得と解放には実際のオーバーヘッドがあります。 Jakeが書いているように、この操作が何百万回も実行されない限り、それはごくわずかです。

より重要なのは、これが実行の流れに与える影響です。このコードは、一度に1つのスレッドのみが入力できます。この操作を定期的に実行するスレッドが5つある場合、そのうち4つはロックが解放されるのを待ち、ロックが解放された後にそのコードを入力する最初のスレッドになる予定です。そのため、あなたのアルゴリズムはかなり苦しむことになります。その程度はアルゴリズムと操作が呼び出される頻度に依存します。競合状態を発生させずに回避することはできませんが、ロックされたコードの呼び出し回数を最小限に抑えることで改善できます。

4
KeithS