Skip to content

Commit 70301da

Browse files
authored
Rollup merge of #143881 - orlp:once-state-repr, r=tgross35
Use zero for initialized Once state By re-labeling which integer represents which internal state for `Once` we can ensure that the initialized state is the all-zero state. This is beneficial because some CPU architectures (such as Arm) have specialized instructions to specifically branch on non-zero, and checking for the initialized state is by far the most important operation. As an example, take this: ```rust use std::sync::atomic::{AtomicU32, Ordering}; const INIT: u32 = 3; #[inline(never)] #[cold] pub fn slow(state: &AtomicU32) { state.store(INIT, Ordering::Release); } pub fn ensure_init(state: &AtomicU32) { if state.load(Ordering::Acquire) != INIT { slow(state) } } ``` If `INIT` is 3 (as is currently the state for `Once`), we see the following assembly on `aarch64-apple-darwin`: ```asm example::ensure_init::h332061368366e313: ldapr w8, [x0] cmp w8, #3 b.ne LBB1_2 ret LBB1_2: b example::slow::ha042bd6a4f33724e ``` By changing the `INIT` state to zero we get the following: ```asm example::ensure_init::h332061368366e313: ldapr w8, [x0] cbnz w8, LBB1_2 ret LBB1_2: b example::slow::ha042bd6a4f33724e ``` So this PR saves 1 instruction every time a `LazyLock` gets accessed on platforms such as these.
2 parents a7ad680 + f041962 commit 70301da

File tree

2 files changed

+12
-9
lines changed

2 files changed

+12
-9
lines changed

library/std/src/sys/sync/once/futex.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,18 @@ use crate::sys::futex::{Futex, Primitive, futex_wait, futex_wake_all};
88
// This means we only need one atomic value with 4 states:
99

1010
/// No initialization has run yet, and no thread is currently using the Once.
11-
const INCOMPLETE: Primitive = 0;
11+
const INCOMPLETE: Primitive = 3;
1212
/// Some thread has previously attempted to initialize the Once, but it panicked,
1313
/// so the Once is now poisoned. There are no other threads currently accessing
1414
/// this Once.
15-
const POISONED: Primitive = 1;
15+
const POISONED: Primitive = 2;
1616
/// Some thread is currently attempting to run initialization. It may succeed,
1717
/// so all future threads need to wait for it to finish.
18-
const RUNNING: Primitive = 2;
18+
const RUNNING: Primitive = 1;
1919
/// Initialization has completed and all future calls should finish immediately.
20-
const COMPLETE: Primitive = 3;
20+
/// By choosing this state as the all-zero state the `is_completed` check can be
21+
/// a bit faster on some platforms.
22+
const COMPLETE: Primitive = 0;
2123

2224
// An additional bit indicates whether there are waiting threads:
2325

library/std/src/sys/sync/once/queue.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,12 @@ pub struct OnceState {
7474
}
7575

7676
// Four states that a Once can be in, encoded into the lower bits of
77-
// `state_and_queue` in the Once structure.
78-
const INCOMPLETE: usize = 0x0;
79-
const POISONED: usize = 0x1;
80-
const RUNNING: usize = 0x2;
81-
const COMPLETE: usize = 0x3;
77+
// `state_and_queue` in the Once structure. By choosing COMPLETE as the all-zero
78+
// state the `is_completed` check can be a bit faster on some platforms.
79+
const INCOMPLETE: usize = 0x3;
80+
const POISONED: usize = 0x2;
81+
const RUNNING: usize = 0x1;
82+
const COMPLETE: usize = 0x0;
8283

8384
// Mask to learn about the state. All other bits are the queue of waiters if
8485
// this is in the RUNNING state.

0 commit comments

Comments
 (0)