Skip to content

Commit

Permalink
Make initialization in OnceNonZeroUsize::get_or_try_init #[cold].
Browse files Browse the repository at this point in the history
In typical use, the Some branch is going to be taken extremely
frequently but the None branch is only going to be taken once (or a
very small number of times) at startup. If `get_or_try_init` is in a
performance-sensitive segment of code, then it is important that
`get_or_try_init` be inlined and that the compiler understands that the
Some branch is (much) more likely than the None branch. When this
happens, the call site basically becomes a load followed by a
conditional jump that is basically never taken; which is ideal.

When `get_or_try_init` is used in many places in the user's code, it is
important to avoid inlining any of the None branch into the call sites.

Unfortunately, the Rust compiler is sometimes not good at recognizing
that code that calls a #[cold] function unconditionally must be cold
itself. So, sometimes it isn't enough to mark our f as
`#[cold] #[inline(never)]`.

Move the entire body of the None branch into a function that is marked
because some post-inlining optimization passes in the compiler seem to
not understand `#[cold]`, and because we don't want any part of that
branch to be in the calling code.
  • Loading branch information
briansmith committed Feb 6, 2025
1 parent 4fbd4a5 commit 598e8fb
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 14 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- Outline initialization in `race`: [#273](https://github.com/matklad/once_cell/pull/273).

## 1.20.2

- Remove `portable_atomic` from Cargo.lock if it is not, in fact, used: [#267](https://github.com/matklad/once_cell/pull/267)
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "once_cell"
version = "1.20.2"
version = "1.20.3"
authors = ["Aleksey Kladov <aleksey.kladov@gmail.com>"]
license = "MIT OR Apache-2.0"
edition = "2021"
Expand Down
28 changes: 15 additions & 13 deletions src/race.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,19 +93,21 @@ impl OnceNonZeroUsize {
F: FnOnce() -> Result<NonZeroUsize, E>,
{
let val = self.inner.load(Ordering::Acquire);
let res = match NonZeroUsize::new(val) {
Some(it) => it,
None => {
let mut val = f()?.get();
let exchange =
self.inner.compare_exchange(0, val, Ordering::AcqRel, Ordering::Acquire);
if let Err(old) = exchange {
val = old;
}
unsafe { NonZeroUsize::new_unchecked(val) }
}
};
Ok(res)
match NonZeroUsize::new(val) {
Some(it) => Ok(it),
None => self.init(f),
}
}

#[cold]
#[inline(never)]
fn init<E>(&self, f: impl FnOnce() -> Result<NonZeroUsize, E>) -> Result<NonZeroUsize, E> {
let mut val = f()?.get();
let exchange = self.inner.compare_exchange(0, val, Ordering::AcqRel, Ordering::Acquire);
if let Err(old) = exchange {
val = old;
}
Ok(unsafe { NonZeroUsize::new_unchecked(val) })
}
}

Expand Down

0 comments on commit 598e8fb

Please sign in to comment.