-
Notifications
You must be signed in to change notification settings - Fork 94
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Avoid contention on reference count for readers #3
Comments
jonhoo
pushed a commit
that referenced
this issue
Sep 9, 2023
`cargo test --all-features` does not run doc-tests. For more information see rust-lang/cargo#6669.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The current implementation has an
Arc
behind the atomic pointer to the currently active map. This is used by the writer to determine when there are no more readers present for the previous map when doing arefresh()
. Unfortunately, this also leads to readers contending on the cache-line of thatArc
, even when there are no writes.evmap
should move away from theArc
-based scheme to a solution that does not incur this cost (nor the extra pointer indirection thatArc
introduces). We can do that by shifting more work onto the writer. The gist of the idea is that every reader will have a local epoch number, which they increment when they read from the map. After swapping, the writer will wait until it has seen the epoch number change for every reader, which ensures that every reader has moved on to the new map.Specifically,
WriteHandle
andReadHandle
will both hold anArc<Mutex<Vec<Arc<AtomicUsize>>>>
. Whenever aReadHandle
is cloned, a newAtomicUsize
will be allocated, and will be added to theVec
by taking theMutex
. This may seem as though it adds a lot of overhead, but notice that a) the outerArc
is only ever updated when a newReader
is created; b) theMutex
is only taken by readers when one is cloned — in the common case, only the writer is accessing it; and c) theArc
wrapping theAtomicUsize
will always carry a reference count of 2, and will only be modified when a reader arrives or leaves (i.e., during creation and destruction). Note also that only the writer needs to use strong consistency when accessing theAtomicUsize
; the readers can update it withOrdering::Relaxed
.It is worth noting that the scheme as described thus far encounters problems when a reader does a single read, and then never reads again. The writer will never observe its epoch number change, and thus will wait forever after doing a swap. To combat this, the per-reader
AtomicUsize
also includes a bit to indicate that the reader has finished its read. The writer can now ignore any reader epochs that are marked as "finished", since it knows they cannot be accessing the map through the old pointer.This should significantly improve the read performance in situations where there are many fast readers, especially when working with many cores or on NUMA architectures. Implementation of this will likely not happen for a little while, as more pressing tasks are awaiting elsewhere, but at least we now have a plan.
The text was updated successfully, but these errors were encountered: