Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize LazyLock size #107329

Merged
merged 3 commits into from
Feb 18, 2023
Merged

Optimize LazyLock size #107329

merged 3 commits into from
Feb 18, 2023

Conversation

joboet
Copy link
Member

@joboet joboet commented Jan 26, 2023

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
Copy link
Collaborator

rustbot commented Jan 26, 2023

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
Copy link
Collaborator

rustbot commented Jan 26, 2023

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
Copy link
Contributor

Kobzol commented Jan 26, 2023

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

@joboet
Copy link
Member Author

joboet commented Jan 26, 2023

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
Copy link
Contributor

Kobzol commented Jan 26, 2023

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
Copy link
Member Author

joboet commented Jan 26, 2023

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.

@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
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
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
Copy link
Member

m-ou-se commented Feb 13, 2023

@bors r+

@bors
Copy link
Contributor

bors commented Feb 13, 2023

📌 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
Copy link
Contributor

bors commented Feb 18, 2023

⌛ Testing commit 6520488 with merge 3701bdc...

@bors
Copy link
Contributor

bors commented Feb 18, 2023

☀️ 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
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
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
Copy link
Member Author

joboet commented Apr 21, 2023

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.

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