Skip to content

Reduce the cost of forcing a fresh LazyCell #399

Open

Description

@tisonkun

Constructing and forcing a fresh LazyCell with an immediately ready initializer took 314 ms for seven million cells. This measures the first shared force_pin on each cell. The peer starts with a stored future, while LazyCell::new starts with a factory that produces one.

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

Scenario Fixed rounds Asyncband async-once-cell
Construct and force once 7,000,000 #2, 1.00× (314 ms) #1, 0.21× (67 ms)

force_pin locks the initializer state and drives the stored future before publishing its value. The macOS allocation probe observed one 64-byte allocation per fresh Asyncband cell and zero for async-once-cell. A separate run with only the internal lock switched to parking_lot took 135 ms versus 339 ms for the original, with the peer at 67 ms.

First contribution

Compare LazyCell::new with LazyCell::from_future using the same immediately ready value and fixed round count. Measure forcing an already initialized cell separately. For a change to shared initialization, verify that a second caller resumes the same pinned future after the first caller cancels, and that a panicking initializer still poisons the cell.

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 peer_checks -- extra/lazy-cell-first-force --rounds 7000000 --repeat 10 --raw

Performance tracking issue #396

Activity

  1. rokadepiyush49-rgb commented on Nov 6, 2026

    @rokadepiyush49-rgb

    Could you assign this to me? I'd compare LazyCell::new and LazyCell::from_future on an Apple M4 (10 cores, macOS 27.0.1), and measure forcing an initialized cell separately.

    force_pin goes through the async Mutex, whose guard calls release_with_lock, so I expect the 64-byte allocation comes from the first lock of waiters on macOS. Since #406 skips that lock when uncontended, I'd measure with and without it before proposing a change here.

    I haven't run these yet. I used Claude to help me read the code.

  2. tisonkun commented on Nov 6, 2026

    @tisonkun
    MemberAuthor

    Thanks for your interest! We don't assign issues to individual contributors. Anyone is welcome to submit a PR directly for review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions