user3488765 user3488765 - 1 month ago 9
C# Question

Why is ReadWriteLockSlim so slow? // Is this custom implementation safe?

I wrote a small database where the usage is 95% reads. I was looking for bottlenecks and out of curiosity wanted to compare the speeds of the available locking mechanisms:

In a purely single-threaded environment I get this result with only read requests:

no lock: 1%
spinlock: 16%
rwLock: 60%
of ticks to complete.

I am aware that RWLock will look a lot better in a multi-threaded environment but I don't quite comprehend why it takes so much more time when just aquiring a lock for reading?

I wrote a small custom RW-lock and it performs 3 times better than rwLock at 20% of total ticks.

Here's the code:

class FastRWLock
{
private int _readers = 0;
private bool _isWriting = false;
private object _spinLock = new object();

private AutoResetEvent _notifier = new AutoResetEvent(false);
private bool needsNotify = false;

public void AquireReaderLock() {

if (_isWriting) {
lock (_spinLock) {

}
}
_readers++;
}

public void ExecuteWrite(Action a) {
if (_isWriting) {
lock (_spinLock) {//Only ends up here if another writer has already entered the following spinlock, thus, this one wont exite here until the previous write is done

}
}
try
{
_isWriting = true;
lock (_spinLock)//intention to write made public, idling until previous readers, that enqueued when another writeaction was active
{//probable deadlock?
if (_readers > 0) {
needsNotify = true;
_notifier.WaitOne();
needsNotify = false;
}
a.Invoke();
}
}
finally {
_isWriting = false;
}
}

public void ReleaseReaderLock() {
_readers--;
if (_readers < 1 && needsNotify) {
_notifier.Set();
}
}

}


Is this a viable solution or am I risking a deadlock? If not, what else is the standard RWLock doing that makes it perform so much worse?

Answer

You need to be using volatile reads and writes of your variables that are outside of lock blocks, you could be reading cached values.

Also you can't do _readers-- or _readers++ in a multi-threaded environment outside of a lock block. You will need to use Interlocked.Increment and Interlocked.Decrement

There may be other problems, but those where the first to major ones that jumped out at me.

If you fixed the problems in your code I would bet you would see a lot less of a "performance gain" because I think the performance gain is caused by bugs in your code that makes your version not reliable to use. Writing locking primitives is very hard to do correctly. I would recommend trying to use the built in classes where possible.