Jump to content
Sign in to follow this  
dummzeuch

TRtlCriticalSection with custom SpinCount

Recommended Posts

After looking into general spin locks

I now had a closer look at TCriticalSection and the internally used TRtlCriticalSection record with associated API functions.

 

One thing I didn't know about is the SetCriticalSectionSpinCount API function which allows to adjust the SpinCount of a CriticalSection, in theory that is: My tests on my computer with Windows 10 have shown that using SetCriticalSectionSpinCount after initializing a TRtlCriticalSection with InitializeCriticalSection has no effect at all. It only works if either InitializeCriticalSectionEx (Windows 7 and later) or InitializeCriticalSectionAndSpinCount (also available in Windows XP) is used for initializing it. The SetCriticalSectionSpinCount will only then actually change the SpinCount. I couldn't find anything about this in the documentation, but maybe I wasn't looking hard enough.

 

Also, apparently Microsoft has changed what InitializeCriticalSectionEx does. If has a Flag parameter which according to the documentation can be set to CRITICAL_SECTION_NO_DEBUG_INFO to prevent debug information to be generated. But my tests show that this has no effect at all on Windows 10. To get debug information, one has to specify the undocumented flag RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO. Again, maybe that's documented somewhere but I didn't find it.

 

Something else I learned is, that a Delphi RTL's TCriticalSection also has a TryEnter method which calls the TryEnterCriticalSection API function. Somehow I missed that for all those years.

 

The test results are also interesting on how different SpinCounts affect the general throughput for different numbers of threads. The default (which seems to be 2000 but I am not sure about this, as it is set dynamically by Windows and may depend on the hardware and the system load) is generally OK but using 100 gives a much better throughput in all my tests with different numbers of threads.

 

SyncTests.thumb.png.52f8d10471d22075e66b5ec9aedfe603.png

 

  • Simple+Sleep is a "spinlock" that only checks for access and then calls Sleep(1)
  • TdzCritSectTest uses a TRtlCriticalSection initialized with InitializeCriticalSection
  • TdzCritSectTestInit100 uses TRtlCriticalSection initialized with InitializeCriticalSectionAndSpinCount to SpinCount of 100

 

But these tests are very synthetic:

I have n + m threads running in an endless loop until aborted:

The n writers execute this code:

procedure TLockTest.doWrite;
var
  i: Integer;
  Value: Integer;
begin
  AcquireLock;
  Value := Random(100);
  for i := 0 to Length(FDataToSafguard) - 1 do
    FDataToSafguard[i] := Value;
  ReleaseLock;
end;

FDataToSaveguard is an array of 10 integers.

 

The m Checkers execute this code:

procedure TLockTest.doCheck;
var
  i: Integer;
  Value: Integer;
begin
  AcquireLock;
  Value := FDataToSafguard[0];
  for i := 1 to Length(FDataToSafguard) - 1 do
    if FDataToSafguard[i] <> Value then
      InterlockedIncrement(FErrors);
  ReleaseLock;
end;

So they not only read the data but also check that it is correct (that's why I call these "checkers" rather than "readers"). The reason for this code is that I wanted to make sure that the array is actually being protected because this test was also meant for my homegrown spinlock code.

 

I'm not sure what to make of this regarding real world code because it's very rarely that it doesn't matter which thread gets executed how often. E.g. if there is one or more writers filling a queue of some kind and one or more readers emptying it, it can happen that readers become idle if writers don't get access to the queue. In that scenario a high "throughput" as measured by my test program won't be a good thing.

 

I guess I'll have to come up with a better test.

 

If you want to look at the code, it's in my dzlib repository on OSDN: https://svn.osdn.net/svnroot/dzlib-tools/dzlib/trunk

TdzRtlCriticalSection is in src\u_dzCriticalSection, the test is in tests\SpinLockTest.

 

(Yes, I should probably have made this a blog post....)

 

Share this post


Link to post

Some more stuff you might not be aware of WRT critical sections_

https://devblogs.microsoft.com/oldnewthing/20140911-00/?p=44103

http://joeduffyblog.com/2006/12/14/anticonvoy-locks-in-windows-server-2003-sp1-and-windows-vista/

 

A spin count of 100 seems very low - but then again I don't know what your threads are doing and I don't really have time to look at your code to see what problem (if any...) you are trying to solve.

Share this post


Link to post
12 hours ago, Anders Melander said:

what problem (if any...) you are trying to solve.

None at all, this was purely out of curiosity.

 

Thanks for the links.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×