Repository navigation
Conversation
`from_iter` was the only caller of `insert(&mut self)`, which locked a table nobody else could reach. Fill a local table and wrap it in the mutex once it is complete; a duplicate key replaces its entry in place because no lock is held.
`Lookup` and `classify` wrapped a single `cell.get()` check that `compute` and `try_compute` only unpacked again. Returning the entry lets each caller take its own fast path and gives `get_or_insert` the same shape as the singleflight group, including the note on where duplicate keys drop. A test drops a duplicate key whose destructor discards another key, on the pending and on the ready path, so the unlock-before- drop order cannot regress silently. The entry is bound inside a block so that its scope ends before the await: a local that outlives the await keeps a slot in the future and made `compute` 8 bytes larger.
`get` delegated to `get_value`, which delegated to `find_entry`, each with one caller; the `Entries` alias had one use; `remove_entry` unlocked explicitly right before returning; and a comment still said "write lock" although the table is behind a mutex. All of them date from the sharded table, where the public map and the shard were different types. `get` still releases the lock before inspecting the entry.
Use the imported `pin!` and `Poll`, pin futures where they are built, write the release check as a plain conditional, and stop describing a "ready index" the map does not have.
The build path had no measurement. The two cases cover distinct keys and a 50% duplicate rate at the entry counts the lookup benches use.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
FromIteratorfills a local table and wraps it in the mutex once it is complete.insert(&mut self)was its only caller and locked a table nobody else could reach; a duplicate key now replaces its entry in place because no lock is held.get_or_insertreturns the entry andcomputeandtry_computecheck the cell themselves, replacing theLookupenum andclassify; the function now has the same shape as the singleflight group, including the note on where duplicate keys drop. A new test drops a duplicate key whose destructor discards another key, on both the pending and the ready path, so that order cannot regress silently. The entry is bound inside a block so that its scope ends before the await, which keeps thecomputeandtry_computefutures at 192 and 176 bytes.getdoes its own lookup andremove_entryreturns straight after the removal. The removed layers, theEntriesalias, and a comment about a "write lock" were left from the sharded table, where the public map and the shard were different types. Every value is still cloned after the table lock is released, and public behavior is unchanged.The unit tests use the imported
pin!andPoll, pin futures where they are built, and check the release flag with a plain conditional; the lookup bench comment no longer describes a "ready index". Existing tests cover duplicate keys incollect(), cleanup of abandoned entries, and growth under concurrentcompute. A new bench measures building a map from an iterator, which had no measurement before.Benchmarks
mainis44fc2d8. Apple M5, macOS 26.4, Rust 1.96.0.cargo x bench --bench primitives -- --threads 1with fixed sample counts, 100 samples of 64 iterations for the build rows and 200 samples of 256 for the lookup rows, both binaries alternating for ten rounds. Each cell is the median of the nine rounds after the first.once_map::build::collect_distinct_keys/64once_map::build::collect_distinct_keys/1024once_map::build::collect_duplicate_keys/64once_map::build::collect_duplicate_keys/1024once_map::lookup::get_hit_same_keyonce_map::lookup::get_hit_distributed/1024once_map::lookup::get_miss_distributed/1024once_map::lookup::compute_hit_same_keyonce_map::lookup::compute_hit_distributed/1024Building from an iterator saves one uncontended lock and unlock per element plus a second table probe for duplicate keys. The lookup rows are within one or two 0.16 ns timer steps of
main.Validation
cargo x test(594 passed),cargo x check(26 feature configurations), andcargo x lintFollow-up to #345.