multithreadingdelphicritical-sectionreaderwriterlockslimreaderwriterlock

Faster TMultiReadExclusiveWriteSynchronizer?


Is there a faster kind of TMultiReadExclusiveWriteSynchronizer out there? FastCode perhaps?

Starting with Windows Vista, Microsoft added a Slim Reader/Writer lock. It performs much better than Delphi's TMultiReadExclusiveWriteSynchronizer. Unfortunately it only exists in Windows Vista and later, something which few customers actually have yet.

Presumably the concepts in use inside a Slim Reader/Writer lock could be redone in native Delphi code - but has anyone done it?

i have a situation where acquiring and releasing locks on a TMultiReadExclusiveWriteSynchronizer (even when there's no contention - a single thread), causes 100% overhead (the operation time doubles). i can run without locking, but then my class is no longer thread-safe.

Is there a faster TMultiReadExclusiveWriteSynchronizer?

Note: If i use a TCriticalSection i only suffer a 2% performance hit (although critical sections are known to be fast when the acquire succeeds, i.e. while it's single threaded and there's no contention). The downside of a CS is that i lose the "multiple readers" capability.

The Measurements

Using TMultiReadExclusiveWriteSynchronizer a sizable amount of time is spent inside BeginRead and EndRead:

enter image description here

i then ported the code to use Window's own SlimReaderWriter Lock (which some code rewrite, as it doesn't support recursive lock taking), and profiled the resutls:

A 17% improvement when using SRWLocks (aka Omni's spinning lock).

Now, i cannot switch the code permanantly over to use Windows Vista SRWLocks, as there are some entire enterprises of customers that are still on Windows XP.

The Slim locks are just careful use of InterlockedCompareExchange functions; but more careful than i can successfully use. I'm this far away from just stealing the 140 machine instructions involved, and have it done.

Bonus Reading


Solution

  • TOmniMREW from OmniThreadLibrary claims to be faster and more lightweight:

    OTL is an excellent threading lib, BTW.

    Sample Code

    TOmniReaderWriterLock = class(TInterfacedObject, IReaderWriterLock)
    private
       omrewReference: Integer;
    public
       { IReaderWriterLock }
       procedure BeginRead;
       procedure EndRead;
       procedure BeginWrite;
       procedure EndWrite;
    end;
    
    { TOmniReaderWriterLock }
    
    procedure TOmniReaderWriterLock.BeginRead;
    var
      currentReference: Integer;
    begin
        //Wait on writer to reset write flag so Reference.Bit0 must be 0 than increase Reference
        repeat
            currentReference := Integer(omrewReference) and not 1;
        until currentReference = Integer(InterlockedCompareExchange(Pointer(omrewReference), Pointer(Integer(currentReference) + 2), Pointer(currentReference)));
    end;
    
    procedure TOmniReaderWriterLock.EndRead;
    begin
        //Decrease omrewReference
        InterlockedExchangeAdd(@omrewReference, -2);
    end;
    
    procedure TOmniReaderWriterLock.BeginWrite;
    var
        currentReference: integer;
    begin
        //Wait on writer to reset write flag so omrewReference.Bit0 must be 0 then set omrewReference.Bit0
        repeat
            currentReference := omrewReference and (not 1);
        until currentReference = Integer(InterlockedCompareExchange(Pointer(omrewReference), Pointer(currentReference+1), Pointer(currentReference)));
    
        //Now wait on all readers
        repeat
        until omrewReference = 1;
    end;
    
    procedure TOmniReaderWriterLock.EndWrite;
    begin
        omrewReference := 0;
    end;