8

Windows Via C/C++のコードを使用して、Windows 7 でスリム リーダー/ライター ロックのパフォーマンスをテストしました。

その結果、排他ロックアウトのパフォーマンスが共有のものであることに驚きました。これがコードと結果です。

unsigned int __stdcall slim_reader_writer_exclusive(void *arg)
{
    //SRWLOCK srwLock;
    //InitializeSRWLock(&srwLock);

    for (int i = 0; i < 1000000; ++i) {
        AcquireSRWLockExclusive(&srwLock);
        g_value = 0;
        ReleaseSRWLockExclusive(&srwLock);
    }
    _endthreadex(0);
    return 0;
}

unsigned int __stdcall slim_reader_writer_shared(void *arg)
{

    int b;
    for (int i = 0; i < 1000000; ++i) {
        AcquireSRWLockShared(&srwLock);
        //b = g_value;
        g_value = 0;
        ReleaseSRWLockShared(&srwLock);
    }
    _endthreadex(0);
    return 0;
}

g_valueグローバルな int volatile 変数です。

ここに画像の説明を入力

なぜこのようなことが起こるのか、親切に説明していただけますか?

4

3 に答える 3

22

これは、小さな汎用ロック (サイズが 1 つのポインターしかない SRWLocks など) ではよくある結果です。

重要なポイント:保護されたコード セクションが非常に小さく、ロック自体のオーバーヘッドが支配的である可能性がある場合は、共有ロックよりも排他ロックを使用することをお勧めします。

また、g_Value の競合に関する Raymond Chen の主張も同様です。どちらの場合も g_Value が書き込まれる代わりに読み取られた場合、共有ロックの利点に気付くかもしれません。

詳細:

SRW ロックは、下位ビットの値に応じて、さまざまな状態を取ることができる単一のポインター サイズのアトミック変数を使用して実装されます。これらのビットの使用方法の説明は、このコメントの範囲外です。状態遷移の数は非常に多いため、テストで遭遇する可能性のあるいくつかの状態についてのみ言及します。

初期ロック状態: (0, ControlBits:0) -- SRW ロックは、すべてのビットが 0 に設定されて開始されます。

共有状態: (ShareCount: n, ControlBits: 1) -- 競合する排他的取得がなく、ロックが共有されている場合、共有カウントはロック変数に直接格納されます。

排他的状態: (ShareCount: 0, ControlBits: 1) -- 競合する共有取得または排他的取得がない場合、ロックには下位ビットが設定され、他には何もありません。

競合状態の例: (WaitPtr:ptr, ControlBits: 3) -- 競合がある場合、ロックを待機しているスレッドは、待機中のスレッドのスタックに割り当てられたデータを使用してキューを形成します。lock 変数は、共有カウントではなく、キューの末尾へのポインターを格納します。

このスキームでは、初期状態がわからないときに排他ロックを取得しようとするのは、ロック ワードへの 1 回の書き込みであり、下位ビットを設定して古い値を取得します (x86 では LOCK BTS 命令を使用して実行できます)。 )。成功した場合 (1 スレッドの場合は常にそうであるように)、それ以上の操作なしでロックされた領域に進むことができます。

共有ロックを取得しようとするのは、より複雑な操作です。最初にロック変数の初期値を読み取って古い共有数を特定し、読み取った共有数をインクリメントしてから、条件付きで LOCK CMPXCHG を使用して更新された値を書き戻す必要があります。命令。これは、シリアルに依存する命令のチェーンが著しく長いため、速度が低下します。また、CMPXCGH は、LOCK BTS のような無条件のアトミック命令よりも多くのプロセッサで少し遅くなります。

理論的には、最初にロックが初期状態にあると想定し、最初に LOCK CMPXCHG を実行することで、ロックの最初の共有取得を高速化することができます。これにより、ロックの最初の共有取得が高速化されますが (シングル スレッドの場合はすべて)、ロックが既に共有されており、2 番目の共有取得が発生する場合はかなり遅くなります。

ロックが解放されているときに、同様の発散操作のセットが発生するため、共有状態を管理するための追加コストも ReleaseSRWLockShared 側で支払われます。

于 2012-11-04T04:58:19.760 に答える
6

Windows でのロックの最適化に専念している Windows カーネル開発者は、経験則としてのパフォーマンスに関して次のように語っています。

  1. 最高のパフォーマンスを得るには、ほとんどの場合にクリティカル セクションを使用します
  2. 少なくとも 4:1 対書き込みの読み取りを行う場合は、SRW ロックを検討してください。
  3. コードが飢餓を絶対に許可できない場合にのみミューテックスを使用してください

明らかに、ロックに関して考慮すべき他の側面があります。

  1. SRWロックをクリーンアップする必要はありませんが、CSをクリーンアップする必要があります
  2. CS ロックは再帰的に取得できますが、SRW ロックは再帰的に取得できません。
  3. ミューテックスは UM と KM を同期できます。KM にいる場合、CS と SRW のロックは実際にはオプションではありません

ええ、読み取り >>>> 書き込みでない限り、CS を優先してください。

于 2016-05-21T00:02:08.247 に答える