22

そこにもっと速い種類はTMultiReadExclusiveWriteSynchronizerありますか?おそらくFastCode?

Windows Vista 以降、Microsoft はSlim Reader/Writer lockを追加しました。Delphiよりもはるかに優れたパフォーマンスを発揮しますTMultiReadExclusiveWriteSynchronizer。残念ながら、これは Windows Vista 以降にしか存在せず、実際にまだほとんどのユーザーが持っていません。

おそらく、内部で使用されている概念はSlim Reader/Writer lock、ネイティブの Delphi コードでやり直すことができますが、それを行った人はいますか?

TMultiReadExclusiveWriteSynchronizer(競合がない場合でも、単一のスレッドで)ロックを取得および解放すると、 100%のオーバーヘッドが発生する(操作時間が2倍になる)状況があります。ロックせずに実行できますが、クラスはスレッドセーフではなくなります。

より速いものはありますTMultiReadExclusiveWriteSynchronizerか?

: i を使用するとTCriticalSection、2% のパフォーマンス ヒットしか発生しません (ただし、取得が成功した場合、つまりシングル スレッドで競合がない場合、クリティカル セクションは高速であることが知られています)。CS の欠点は、「複数のリーダー」機能を失うことです。

測定

内部とTMultiReadExclusiveWriteSynchronizer内部でかなりの時間を費やしています。BeginReadEndRead

ここに画像の説明を入力

次に、Windows 独自のSlimReaderWriter Lockを使用するようにコードを移植し(再帰的なロックの取得をサポートしていないため、一部のコードを書き換えます)、結果をプロファイリングしました。

  • TMultiReadExclusiveWriteSynchronizer: 反復ごと
    に 10,698 ns 1,000,000 回反復するには 10,697,772,613 ns

  • SRWLock: 反復ごとに 8,802 ns
    1,000,000 回反復するには 8,801,678,339 ns

  • Omni Reader-Writer lock: 反復ごとに 8,941 ns
    1,000,000 回反復するには 8,940,552,487 ns

SRWLocks (別名 Omni の回転ロック) を使用すると 17% の改善。

現在、まだ Windows XP を使用している企業全体が存在するため、Windows Vista SRWLocksを使用するようにコードを永続的に切り替えることはできません。

スリムロックは、InterlockedCompareExchange機能を慎重に使用するだけです。しかし、私がうまく使用できるよりも慎重です。関係する 140 の機械語命令を盗むには程遠いです。

ボーナスリーディング

4

5 に答える 5

3

結局、私は妥協案を使用しました。Omniリーダー/ライター ロックは、「スリムな」原則 (回転ビット操作) を使用します。Window のものと同様に、ロックのエスカレーションをサポートしていません。私はそれをテストしましたが、クラッシュやデッドロックをロックアップしていないようです。

最後に、フォールバック状況を使用しました。「読み取り/書き込み」の概念をサポートする最も一般的な汎用インターフェイス:

IReaderWriterLock = interface
   ['{6C4150D0-7B13-446D-9D8E-866B66723320}']
   procedure BeginRead;
   procedure EndRead;
   procedure BeginWrite;
   procedure EndWrite;
end;

そして、実行時にどの実装を使用するかを決定します。Windows Vista またはそれ以降を使用している場合は、Windows 独自の を使用します。それ以外の場合は、バージョンSlimReaderWriterにフォールバックします。Omni

TReaderWriterLock = class(TObject)
public
   class function Create: IReaderWriterLock;
end;

class function TReaderWriterLock.Create: IReaderWriterLock;
begin
   if Win32MajorVersion >= 6 then //SRWLocks were introduced with Vista/Server 2008 (Windows version 6.0)
   begin
      //Use the Windows built-in Slim ReaderWriter lock
      Result := TSlimReaderWriterLock.Create;
   end
   else
   begin
      //XP and earlier fallback to Omni equivalent
      Result := TOmniReaderWriterLock.Create;
   end;
end;

: すべてのコードはパブリック ドメインにリリースされます。帰属は必要ありません。

于 2013-10-28T19:03:40.840 に答える
2

DelphiTMultiReadExclusiveWriteSynchronizerは非常に洗練されています。再帰的に取得でき、からReadに更新できますWrite

これにはコストがかかります。この場合、スレッドごとに共有状態のバケットを管理することを意味します。Windows のスレッド ローカル メカニクス ( 経由でアクセス可能threadvar) は単純すぎるため (複数の MREWS インスタンスに対応できない)、かなり非効率的な方法で行われます - RTL または JCL ソースを参照してください - 実装は非常に似ており、パフォーマンスが低下しています更新デッドロックのリスク。

最初に、MREWS 機能が本当に必要かどうかを確認します。ワークロードに対するロック オーバーヘッドの比例サイズによると、TCriticalSection.

本当に必要な場合は、Delphi の実装を使用して、隠されたロック解除の可能性に注意してBeginWriteください。ドキュメントと戻り値の意味を参照してください。

SRW関数またはインライン アセンブリを使用してVista のようなものを実装することは可能ですがInterlocked、ほとんどの場合、努力する価値はありません。

于 2012-10-04T20:41:18.273 に答える
0

JCLにはMREWSがありますが、これは別の実装であり、機能する可能性があります。必要なWindowsのバージョンがわからない。

http://wiki.delphi-jedi.org/wiki/JCL_Help:TJclMultiReadExclusiveWrite

http://wiki.delphi-jedi.org/index.php?title=JEDI_Code_Library

于 2012-06-26T15:59:53.597 に答える
0

これを試して?通常の変数として使用できます。

type myclass=class
              Lock:TOBRWLock;
              function ReadIt:Integer;
              procedure WriteIt(A:Integer);
             end;  
function ReadIt:Integer;
begin;
 Lock.LockRead;
 Result:=GetVal;
 Lock.UnLockRead;
end;

改善の余地はたくさんあります。ここから、必要に応じて、read over write を優先するか、単に異なる動作をするさまざまな種類を構築できます。

const ldFree    = 0;
      ldReading = 1;
      ldWriting = 2;
type TOBRWLock          = record
                 [Volatile]WritersWaiting,
                           ReadersWaiting,
                           ReadersReading,
                           Disposition    : Integer;
                           procedure LockRead;
                           procedure LockWrite;
                           procedure UnlockRead;
                           procedure UnlockWrite;
                           procedure UnReadWrite;
                           procedure UnWriteRead;
                          end;

procedure TOBRWLock.LockRead;
var SpinCnt : NativeUInt;
    I       : Integer;
begin
 SpinCnt:=0;
 TInterlocked.Increment(ReadersWaiting);
 repeat
  if (Disposition=ldReading)
     then begin
           I:=TInterlocked.Increment(ReadersReading);
           if (Disposition<>ldReading) or (I=1)(*Only 1 read reference or Disposition changed, suspicious, rather retry*)
              then begin
                    TInterlocked.Decrement(ReadersReading);
                    continue;
                   end
              else begin(*Success*)
                    TInterlocked.Decrement(ReadersWaiting);
                    break;
                   end;
          end;
  if (WritersWaiting<>0)or(Disposition<>ldFree)
     then begin
           SpinBackoff(SpinCnt);
           continue;
          end;
  if TInterlocked.CompareExchange(Disposition,ldReading,ldFree)=ldFree
     then begin
           TInterlocked.Increment(ReadersReading);
           TInterlocked.Decrement(ReadersWaiting);
           break;
          end;
  SpinBackoff(SpinCnt);
 until False;
end;

procedure TOBRWLock.LockWrite;
var SpinCnt : NativeUInt;
begin
 SpinCnt:=0;
 TInterlocked.Increment(WritersWaiting);
 repeat
  if (Disposition<>ldFree)
     then begin
           SpinBackoff(SpinCnt);
           continue;
          end;
  if TInterlocked.CompareExchange(Disposition,ldWriting,ldFree)=ldFree
     then begin
           TInterlocked.Decrement(WritersWaiting);
           break;
          end
     else SpinBackoff(SpinCnt);
 until False;
end;

procedure TOBRWLock.UnlockRead;
begin
 {$IFDEF DEBUG}
 if Disposition<>ldReading
    then raise Exception.Create('UnlockRead a lock that is not Reading');
 {$ENDIF}
 TInterlocked.Decrement(ReadersReading);
 if ReadersReading=0
    then begin;
          if TInterlocked.CompareExchange(Disposition,ldFree,ldReading)<>ldReading
             then raise Exception.Create('Impossible 310');
         end;
end;

procedure TOBRWLock.UnlockWrite;
begin
 {$IFDEF DEBUG}
 if Disposition<>ldWriting
    then raise Exception.Create('UnlockWrite a lock that is not Writing');
 {$ENDIF}
 if TInterlocked.CompareExchange(Disposition,ldFree,ldWriting)<>ldWriting
    then raise Exception.Create('Impossible 321');
end;

procedure TOBRWLock.UnReadWrite;
var SpinCnt : NativeUInt;
begin
 {$IFDEF DEBUG}
 if Disposition<>ldReading
    then raise Exception.Create('UnReadWrite a lock that is not Reading');
 {$ENDIF}
 TInterlocked.Increment(WritersWaiting);
 SpinCnt:=0;
 repeat
  if ReadersReading=1(*Only me reading*)
     then begin;
           if TInterlocked.CompareExchange(Disposition,ldWriting,ldReading)<>ldReading(*Must always succeed*)
              then raise Exception.Create('Impossible 337');
           TInterlocked.Decrement(ReadersReading);
           TInterlocked.Decrement(WritersWaiting);
           break;
          end;
  SpinBackoff(SpinCnt);
 until False;
end;
于 2014-01-20T11:28:48.547 に答える