Attempt to compare rwlock and LMDB internal locking mechanism.

The benchmark first writes 1000 key/values (actually, the same key is
re-used) in LMDB and read it 1000 times. Then it does the same thing
using concurrency, on a 16 cores machine, with the following
configurations:

- 32 threads (29 readers and 3 writer)
- 8 threads (6 readers and 2 writer)
- 8 threads (8 readers and 0 writer)

See [3] for LMDB internal locking result and [4] for rwlock result.

What the result seems to say is that LMDB internal locking mechanism
is doing way better (100x) when there are lot of concurrency
involved. I'm kinda surprised by such factor and tried various things
in case I've done something wrong, but eventually always end up with
similar difference. The only explanation I have is that LMDB actually
does uses an userspace RCU mechanism internally, so readers are never
blocked.

Using rwlock would block the readers while a writer lock is
alive. LMDB doc seems to say [1] that if we use our own
synchronization mechanism, we should only have one writer at the time,
and hold a write lock from the beginning of the transaction up to the
commit/revert (I tried holding a read lock on a write transaction then
upgrade to a write lock only at commit time... but of course I end up
in lot of odd crashes). I think this explain the massive congestion
when using 32 threads, as lot of readers are waiting.

When there is no thread congestion (the 2 later configuration with 8
threads) using the rwlock actually seems a little bit faster (same
order of magnitude though). I guess this might be explained by the
fact that, while LMDB doesn't lock readers, there is still a quick
lock-unlock to maintains a reader table (and eventually free unused
old versions once a writer commit). [2]

[1] http://www.lmdb.tech/doc/group__mdb.html#:~:text=must%20ensure%20that%20no%20readers%20are%20using%20old%20transactions%20while%20a%20writer%20is%20active

[2] http://www.lmdb.tech/doc/group__readers.html

[3] two runs using LMDB internal locking (HEAD~2)

    sequential writes and read
    avg write 0.005878 ms
    avg read 0.001640 ms
    concurrent 29 readers and 3 writers
    avg write 0.746217 ms
    avg read 6.644507 ms
    concurrent 6 readers and 2 writers
    avg write 0.077576 ms
    avg read 0.177316 ms
    concurrent 8 readers and 0 writer
    avg write 0.000000 ms
    avg read 0.273563 ms

    sequential writes and read
    avg write 0.005799 ms
    avg read 0.001656 ms
    concurrent 29 readers and 3 writers
    avg write 0.737782 ms
    avg read 6.616162 ms
    concurrent 6 readers and 2 writers
    avg write 0.075893 ms
    avg read 0.178631 ms
    concurrent 8 readers and 0 writer
    avg write 0.000000 ms
    avg read 0.303209 ms

[4] two runs using rwlock (HEAD~1)

    sequential writes and read
    avg write 0.005931 ms
    avg read 0.001658 ms
    concurrent 29 readers and 3 writers
    avg write 17.249417 ms
    avg read 163.805094 ms
    concurrent 6 readers and 2 writers
    avg write 0.040559 ms
    avg read 0.168439 ms
    concurrent 8 readers and 0 writer
    avg write 0.000000 ms
    avg read 0.200699 ms

    sequential writes and read
    avg write 0.007423 ms
    avg read 0.001656 ms
    concurrent 29 readers and 3 writers
    avg write 20.312584 ms
    avg read 190.565366 ms
    concurrent 6 readers and 2 writers
    avg write 0.026039 ms
    avg read 0.147365 ms
    concurrent 8 readers and 0 writer
    avg write 0.000000 ms
    avg read 0.174471 ms
This commit is contained in:
Colin Vidal
2025-01-02 10:43:53 +01:00
parent 27fab42e6f
commit 31be352cb2

Diff Content Not Available