You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Reduce the cost of upgrading a held UpgradableRwLock read guard #404
With no competing reader or writer, acquiring an upgradable read guard and promoting it to a write guard took 293 ms versus 155 ms for async-lock over 8.3 million rounds. Both implementations upgrade while continuously holding access. Ordinary uncontended reads are a useful control and were already close in the peer measurements.
Apple M4 Max, macOS arm64, rustc 1.101.0-nightly. Each cell gives rank, elapsed time relative to Asyncband and the whole-batch median over 10 interleaved runs. Lower is faster. Supplemental measurements
upgradable_read acquires a reservation and one data permit. upgrade requests the remaining data permits before converting the guard. In a separate run, changing only the internal lock reduced Asyncband from 311 to 270 ms, with async-lock at 158 ms. The number of permit transitions on the uncontended path is a candidate for investigation.
First contribution
Time acquisition and promotion separately on the same lock. Compare the uncontended path with a reader still present and with a writer waiting for the reservation. Preserve the documented ordering of the two queues, continuous access during upgrade, and cleanup of reservations and assigned permits on cancellation.
To run the supplemental peer_checks command below, save and run peer-checks.rb from the linked comment in a disposable checkout of 6102089. The benchmark and library sources match 36ba6f2. Generator and instructions
cargo x bench --bench primitives -- upgradable_read_then_upgrade --repeat 10 --raw
cargo x bench --bench peer_checks -- rwlock/upgrade --repeat 10 --raw
I ran the existing lock benchmarks again on my Windows 11 laptop (Intel Core i5-6300U, rustc 1.99.1, commit 36ba6f27c5003524c10fd348720c34825122ca87). The immediate try_upgrade case came in at 2,617 ms median over 8.3 million rounds (five runs, 2,405–3,113 ms); acquisition and reuse alone was 2,079 ms (1,812–2,106 ms). The spread is wide enough that I would not treat their difference as a reliable promotion cost yet.
With one reader held during upgrade, the queued-upgrade row measured 1,461 ms per million rounds (five runs); with four readers it was 1,920 ms. The 16-reader row was especially noisy: 978 ms median over 300,000 rounds, three runs (909–1,952 ms). These timings include setting up and releasing readers, so they are context rather than isolated upgrade timings.
I have updated #405 with the requested queue cases and support for the benchmark smoke run.
I ran cargo x bench --bench upgradable_stages -- --rounds 1000000 --repeat 5 on Windows 11, an Intel Core i5-6300U, and stable rustc 1.99.1. The medians were:
Case
Median per round
Upgradable acquisition
368 ns
Promotion
439 ns
Acquisition with an ordinary reader held
345 ns
Promotion with 1 reader held
948 ns
Promotion with 4 readers held
1,479 ns
Promotion with 16 readers held
2,611 ns
Promotion with a writer queued
508 ns
Upgradable acquisition behind a queued writer
1,058 ns
The batches were noisy: for example, the acquisition runs ranged from 344 to 594 ms, and the 16-reader promotion runs from 2,358 to 4,608 ms. The reader-promotion figures include dropping the read guards and waking the upgrader; the queued-acquisition figure includes the writer's handoff. These are diagnostic measurements on this laptop, not a cross-library comparison or a performance claim. The benchmark manually polls futures with a non-scheduling waker, so the timings exclude executor scheduling overhead.
The full workspace tests, project lint, and cargo x bench -- --test all pass locally.
Activity
ismaeelakin commentedon Nov 3, 2026
I ran the existing lock benchmarks again on my Windows 11 laptop (Intel Core i5-6300U, rustc 1.99.1, commit
36ba6f27c5003524c10fd348720c34825122ca87). The immediatetry_upgradecase came in at 2,617 ms median over 8.3 million rounds (five runs, 2,405–3,113 ms); acquisition and reuse alone was 2,079 ms (1,812–2,106 ms). The spread is wide enough that I would not treat their difference as a reliable promotion cost yet.With one reader held during upgrade, the queued-upgrade row measured 1,461 ms per million rounds (five runs); with four readers it was 1,920 ms. The 16-reader row was especially noisy: 978 ms median over 300,000 rounds, three runs (909–1,952 ms). These timings include setting up and releasing readers, so they are context rather than isolated upgrade timings.
Commands:
cargo x bench --bench primitives -- upgradable_read_then_try_upgrade --rounds 8300000 --repeat 5 --rawcargo x bench --bench primitives -- upgradable_read_reuse --rounds 8300000 --repeat 5 --rawcargo x bench --bench primitives -- readers_then_queued_upgrade --rounds 1000000 --repeat 5 --raw(one and four readers)cargo x bench --bench primitives -- readers_then_queued_upgrade/16 --rounds 300000 --repeat 3 --rawI do not have a performance change to propose yet. The next useful step is a stable stage-level measurement.
ismaeelakin commentedon Nov 4, 2026
I have updated #405 with the requested queue cases and support for the benchmark smoke run.
I ran
cargo x bench --bench upgradable_stages -- --rounds 1000000 --repeat 5on Windows 11, an Intel Core i5-6300U, and stable rustc 1.99.1. The medians were:The batches were noisy: for example, the acquisition runs ranged from 344 to 594 ms, and the 16-reader promotion runs from 2,358 to 4,608 ms. The reader-promotion figures include dropping the read guards and waking the upgrader; the queued-acquisition figure includes the writer's handoff. These are diagnostic measurements on this laptop, not a cross-library comparison or a performance claim. The benchmark manually polls futures with a non-scheduling waker, so the timings exclude executor scheduling overhead.
The full workspace tests, project lint, and
cargo x bench -- --testall pass locally.