Repository navigation
Restore O(1) skip/clear for items without drop glue - #64
Merged
Merged
Conversation
The panic-safe skip counts removed items one at a time so its guard can publish the right count if a destructor unwinds. For items without drop glue that loop is not optimised away, making skip, clear and ring drop O(n). Such items have no destructor to run or unwind, so release their slots with a single advance_read_index. Adds a skip_bytes benchmark.
Owner
|
Merged and published. |
tfenne
added a commit
to tfenne/rawb-io
that referenced
this pull request
Oct 8, 2026
Raises the `ringbuf` requirement from `0.5` to `0.5.3` and updates the lockfile from 0.5.1. Supersedes #9. In ringbuf 0.5.2, `Consumer::skip` takes time proportional to the number of items skipped, even for `u8`. Every byte `ReadAhead` and `WriteBehind` move goes through `skip`, so in-memory throughput fell from ~80–100 GB/s to ~3.5 GB/s. ringbuf 0.5.3 includes agerasev/ringbuf#64, which restores the constant-time path. Requiring 0.5.3 ensures downstream builds can't resolve to 0.5.2. 0.5.3 also includes two other upstream changes. I reviewed both against how rawb-io uses the ring: - a80f211 removes deferred index updates from the caching and frozen wrappers. rawb-io uses neither `freeze` nor `pop_iter`. `advance_write_index` and `advance_read_index` on the caching endpoints still publish straight to the shared atomic, so the publication order our protocol relies on is unchanged. - bb72627 caps capacity at `usize::MAX / 2` and makes the index arithmetic overflow-safe. Every count rawb-io passes to `advance_*_index` is at most one capacity, so it meets the new `add_mod` precondition. Testing (local, macOS): - `cargo ci-fmt`, `ci-lint`, `ci-test` (55 passed), `ci-doctest` and `cargo deny check` all pass. - `LOOM_MAX_PREEMPTIONS=0 scripts/loom.sh`: all 6 models pass against the patched 0.5.3 sources, including the new `skip` fast path. - `cargo ci-soak`: 7/7 pass. - End-to-end throughput benchmark (separate scratch crate, not committed) on 0.5.3: ~95–108 GB/s read and ~88–90 GB/s write, back to 0.5.1 levels. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Fixed a performance regression affecting in-memory read-ahead and write-behind operations. Buffering throughput is no longer significantly reduced when skipping bytes, restoring faster data handling. Users of `rawb-io` 0.1.0 can receive the fix by updating their dependencies. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
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.
Since 0.5.2 (#60),
Consumer::skipcounts removed items one at a time so its guard can publish the right count if a destructor panics. That's needed for items with drop glue, but for items without it (e.g.u8) the counting loop isn't optimised away the way the 0.5.1 loop was, soskip(n)is now O(n), about 0.3 ns per item on an Apple M4 Max.clear(), and dropping a ring (which callsclear()), have the same cost.This adds a
mem::needs_drop::<Self::Item>()fast path that releasesmin(count, occupied_len())slots with a singleadvance_read_index, which is what 0.5.1 effectively compiled to.needs_dropis resolved at compile time for each item type, so the branch has no runtime cost. Items with drop glue still take the panic-safe path from #60, unchanged.New
skip_bytesbenchmark (push then skip 32 KiB ofu8),cargo +nightly bench --features=bench skip_bytes:The remaining time is the
push_slicecopy.We found this in rawb-io, a read-ahead / write-behind byte IO crate that calls
skipon every byte it moves. On 0.5.2 its in-memory throughput dropped from ~80–100 GB/s to ~3.5 GB/s; with this change it's back to the 0.5.1 numbers.Testing:
scripts/test.shpasses. The existingskiptests cover both paths (i8for the fast path;Rcand the panicking-drop types for the guarded path), and they pass under Miri with and withouttest_local.