Amanieu / parking_lot

Compact and efficient synchronization primitives for Rust. Also provides an API for creating custom synchronization primitives.
Apache License 2.0
2.76k stars 217 forks source link

Add SegRwLock : a segment RwLock Type #424

Open pingzhaozz opened 10 months ago

pingzhaozz commented 10 months ago

Add a new RwLock type as SegRwLock which separate the cache line for Read state and Write state in RwLock to solve the cache-line false sharing problem in RwLock in multi threads scenarios.

Since the 'Data' protected by RwLock shares the same cache-line with lock state. When lock state changes the whole cache-line need update which is not necessary for 'Data' in Read lock, and cause unnecessary cache-line update. SegRwLock put Read state in one cache-line and Write state and 'Data' in the other cache-line, to avoid the false sharing between Read state and 'Data'.

Amanieu commented 9 months ago

It's not clear to me what use case this is trying to solve. The most common use case for RwLock is to have many readers but few writers. Your implementation does not help with this case since all the readers are still thrashing read_state.

Have you considered a different implementation using a sharded RwLock? The basic idea is to have an array of [CachePadded<RawRwLock>; N] and assign readers to one of the locks depending on its thread ID. This is optimized for read performance: writers will need to acquire a write lock on all the locks in the array before being able to grant write access.

pingzhaozz commented 9 months ago

Original RwLock memory layout likes this:

pub struct RwLock<T: ?Sized> {
    raw: RawRwLock,
    data: UnsafeCell<T>,
}

raw and data share the same cache-line. In multi read locks, raw change will cause false sharing with data since the data doesn't change in read, only the read state changes. SegRwLock separate read cache-line and write + data cache-line, which memory layout looks like:

pub struct SegRwLock<T: ?Sized> {
    read: ReadLock,
    padded: <CachePadded>
    write: WriteLock,
    data: UnsafeCell<T>,
}

One matter need to care is that repr(align(n)) doesn't work inside the struct. So manually added the pad.