Skip to content

Optimize LazyLock size - #107329

Merged
bors merged 3 commits into
rust-lang:masterfrom
joboet:optimize_lazylock
Feb 18, 2023
Merged

bors merged 3 commits into
rust-lang:masterfrom
joboet:optimize_lazylock

Conversation

@joboet

@joboet joboet commented Jan 26, 2023 •

Copy link
Copy Markdown
Member

The initialization function was unnecessarily stored separately from the data to be initialized. Since both cannot exist at the same time, a union can be used, with the Once acting as discriminant. This unfortunately requires some extra methods on Once so that Drop can be implemented correctly and efficiently.

@rustbot label +T-libs +A-atomic

@rustbot

rustbot commented Jan 26, 2023

Copy link
Copy Markdown
Collaborator

r? @joshtriplett

(rustbot has picked a reviewer for you, use r? to override)

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jan 26, 2023
@rustbot

rustbot commented Jan 26, 2023

Copy link
Copy Markdown
Collaborator

Hey! It looks like you've submitted a new PR for the library teams!

If this PR contains changes to any rust-lang/rust public library APIs then please comment with @rustbot label +T-libs-api -T-libs to tag it appropriately. If this PR contains changes to any unstable APIs please edit the PR description to add a link to the relevant API Change Proposal or create one if you haven't already. If you're unsure where your change falls no worries, just leave it as is and the reviewer will take a look and make a decision to forward on if necessary.

Examples of T-libs-api changes:

  • Stabilizing library features
  • Introducing insta-stable changes such as new implementations of existing stable traits on existing stable types
  • Introducing new or changing existing unstable library APIs (excluding permanently unstable features / features without a tracking issue)
  • Changing public documentation in ways that create new stability guarantees
  • Changing observable runtime behavior of library APIs

@Kobzol

Kobzol commented Jan 26, 2023

Copy link
Copy Markdown
Member

Since this optimizes the size, it would be nice to add a static_assert_size!(LazyLock, XXX); somewhere.

@joboet

joboet commented Jan 26, 2023

Copy link
Copy Markdown
Member Author

Since this optimizes the size, it would be nice to add a static_assert_size!(LazyLock, XXX); somewhere.

I'm a bit hesitant to do so because the size is very platform and compiler specific. Also, I'm not really convinced that a test is necessary here. If required, however, would something like this suffice?

/// Test that the total size is less than the size required to store both the closure
/// and the value at the same time.
#[test]
fn test_size() {
    const SIZE: usize = 1024;

    type InitFn = impl FnOnce() -> [u8; SIZE];

    fn init_fn(data: [u8; SIZE]) -> InitFn {
        move || data
    }

    assert!(size_of::<LazyLock<[u8; SIZE], InitFn>>() < size_of::<Once>() + SIZE + size_of::<InitFn>());
}

@Kobzol

Kobzol commented Jan 26, 2023

Copy link
Copy Markdown
Member

The size asserts are usually checked only for x64:

#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
. But it's not needed for every structure of course.

@joboet

joboet commented Jan 26, 2023 •

Copy link
Copy Markdown
Member Author

The size asserts are usually checked only for x64:

#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]

. But it's not needed for every structure of course.

The problem is that even on systems with the same pointer size, Once has a different size and alignment on Linux (4 bytes) that on e.g. macOS (8 bytes). So the test would need to be limited to one system with one pointer width, which doesn't seem very useful. IMHO the change in implementation alone is enough to guarantee the improvements.

Comment thread library/std/src/sys_common/once/queue.rs
Comment thread library/std/src/sync/lazy_lock.rs
Comment thread library/std/src/sync/lazy_lock.rs Outdated
Comment thread library/std/src/sync/lazy_lock.rs Outdated
Comment thread library/std/src/sync/lazy_lock.rs Outdated
@rustbot rustbot added the A-atomic Area: Atomics, barriers, and sync primitives label Jan 27, 2023
Comment on lines +160 to +163
match self.get() {
Some(v) => f.debug_tuple("LazyLock").field(v).finish(),
None => f.write_str("LazyLock(Uninit)"),
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: With a #[derive(Debug)] struct Uninit;, a fully initialized LazyLock<Uninit> would also produce "LazyLock(Uninit)". Maybe it's better to show a nested Option (LazyLock(Some(..))), or to use LazyLock(<uninitialized>) for the uninitialized case.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see we also already format an empty OnceCell as OnceCell(Uninit). So no need to change that in this PR.

@m-ou-se m-ou-se assigned m-ou-se and unassigned joshtriplett Feb 13, 2023
@m-ou-se

m-ou-se commented Feb 13, 2023

Copy link
Copy Markdown
Member

@bors r+

@bors

bors commented Feb 13, 2023

Copy link
Copy Markdown
Collaborator

📌 Commit 6520488 has been approved by m-ou-se

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Feb 13, 2023
@bors

bors commented Feb 18, 2023

Copy link
Copy Markdown
Collaborator

⌛ Testing commit 6520488 with merge 3701bdc...

@bors

bors commented Feb 18, 2023

Copy link
Copy Markdown
Collaborator

☀️ Test successful - checks-actions
Approved by: m-ou-se
Pushing 3701bdc to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Feb 18, 2023
@bors
bors merged commit 3701bdc into rust-lang:master Feb 18, 2023
@rustbot rustbot added this to the 1.69.0 milestone Feb 18, 2023
@rust-timer

Copy link
Copy Markdown
Collaborator

Finished benchmarking commit (3701bdc): comparison URL.

Overall result: ❌✅ regressions and improvements - no action needed

@rustbot label: -perf-regression

Instruction count

This is a highly reliable metric that was used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
0.7% [0.7%, 0.7%] 1
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-1.4% [-1.4%, -1.4%] 1
All ❌✅ (primary) 0.7% [0.7%, 0.7%] 1

Max RSS (memory usage)

This benchmark run did not return any relevant results for this metric.

Cycles

This benchmark run did not return any relevant results for this metric.

@joboet
joboet deleted the optimize_lazylock branch February 18, 2023 17:41
@joboet joboet mentioned this pull request Mar 22, 2023
bors added a commit to rust-lang-ci/rust that referenced this pull request Apr 1, 2023
…ulacrum

Optimize `LazyCell` size

`LazyCell` can only store either the initializing function or the data it produces, so it does not need to reserve the space for both. Similar to rust-lang#107329, but uses an `enum` instead of a `union`.
@SimonSapin

Copy link
Copy Markdown
Contributor

This unfortunately requires some extra methods on Once so that Drop can be implemented correctly and efficiently.

If this new Once method is useful to LazyLock, could it be useful to other similar types defined outside of std? Should there be a tracking issue to consider making it a public API?

@joboet

joboet commented Apr 21, 2023

Copy link
Copy Markdown
Member Author

This unfortunately requires some extra methods on Once so that Drop can be implemented correctly and efficiently.

If this new Once method is useful to LazyLock, could it be useful to other similar types defined outside of std? Should there be a tracking issue to consider making it a public API?

I don't anticipate a need for it, considering that LazyLock should hopefully be part of stable std at some point. It is currently an internal API only, so making it public would need an ACP and a new PR.

@tgross35 tgross35 mentioned this pull request Feb 22, 2024
1 of 4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-atomic Area: Atomics, barriers, and sync primitives merged-by-bors This PR was explicitly merged by bors. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants