The Performance Cost of RwLock in Our Read-Heavy Workload

8 points by ohrv


noncrab

I ran into a similar thing with our metrics at work, funnily enough. Except for us, it was the metrics scrape holding a read lock scripts several (possibly slow) collectors while metrics updates required the write lock.

I ended up replacing it with an atomic pointer to some immutable collections, so we effectively did snapshot reads.

alper

I read my fair share of advanced Rust books and none of them explained whatever the hell is happening here:

    let guard = crossbeam_epoch::pin();
    let mut max_count = 0;

    for data in block {
        let metrics = data.metrics.load(Ordering::Acquire, &guard);

        // SAFETY: The epoch guard ensures the pointer remains valid
        if let Some(metrics) = unsafe { metrics.as_ref() } {
            max_count = max_count.max(metrics.count);
        }
    }

    max_count

The documentation for crossbeam is also barely a help.

pflanze

Nice post, it was interesting to learn about crossbeam_epoch.

While our Metrics changed frequently, new entries were added to the Block only once or twice per second. For this, a simple RwLock around the Vec was sufficient.

I haven't double checked with the code, but it appears that the frequency of writes to Block doesn't matter here (as they discuss, "even" 500 writes per second weren't relevant for the access to Metrics--the much more frequent reads were), but just that the number of read locks is much lower: if they wrap Block in a lock, and then proceed through all n entries inside, the number of accesses to Metrics is n times higher. Since they say n is 16384, the number of read locks on Block is accordingly low enough to not matter.