Skip to content

Make Rc<T>::deref and Arc<T>::deref zero-cost #132553

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

EFanZh
Copy link
Contributor

@EFanZh EFanZh commented Nov 3, 2024

Currently, Rc<T> and Arc<T> store pointers to RcInner<T> and ArcInner<T>. This PR changes the pointers so that they point to T directly instead.

This is based on the assumption that we access the T value more frequently than accessing reference counts. With this change, accessing the data can be done without offsetting pointers from RcInner<T> and ArcInner<T> to their contained data. This change might also enables some possibly useful future optimizations, such as:

  • Convert &[Rc<T>] into &[&T] within O(1) time.
  • Convert &[Rc<T>] into Vec<&T> utilizing memcpy.
  • Convert &Option<Rc<T>> into Option<&T> without branching.
  • Make Rc<T> and Arc<T> FFI compatible types where T: Sized.

@rustbot
Copy link
Collaborator

rustbot commented Nov 3, 2024

r? @jhpratt

rustbot has assigned @jhpratt.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Nov 3, 2024
@EFanZh EFanZh force-pushed the zero-cost-rc-arc-deref branch from b283c44 to ae36f44 Compare November 3, 2024 09:14
@rust-log-analyzer

This comment has been minimized.

@marmeladema
Copy link
Contributor

Would it potentially enable those types to have an ffi compatible ABI? So that they could be returned and passed directly from /to ffi function, like Box?

@rust-log-analyzer

This comment has been minimized.

@EFanZh
Copy link
Contributor Author

EFanZh commented Nov 3, 2024

Would it potentially enable those types to have an ffi compatible ABI? So that they could be returned and passed directly from /to ffi function, like Box?

I think in theory it is possible, at least for sized types, but I am not familiar with how to formally make it so.

@EFanZh EFanZh force-pushed the zero-cost-rc-arc-deref branch from ae36f44 to 0d6165f Compare November 3, 2024 11:21
@rust-log-analyzer

This comment has been minimized.

@EFanZh EFanZh force-pushed the zero-cost-rc-arc-deref branch from 0d6165f to 98edd5b Compare November 3, 2024 13:06
@rust-log-analyzer

This comment has been minimized.

@jhpratt
Copy link
Member

jhpratt commented Nov 3, 2024

r? libs

@rustbot rustbot assigned joboet and unassigned jhpratt Nov 3, 2024
@EFanZh EFanZh force-pushed the zero-cost-rc-arc-deref branch from 98edd5b to 8beb51d Compare November 4, 2024 16:29
@rust-log-analyzer

This comment has been minimized.

@EFanZh EFanZh force-pushed the zero-cost-rc-arc-deref branch from 8beb51d to d7879fa Compare November 4, 2024 17:26
@rust-log-analyzer

This comment has been minimized.

@EFanZh EFanZh force-pushed the zero-cost-rc-arc-deref branch from d7879fa to 317aa0e Compare November 4, 2024 18:40
@joboet
Copy link
Member

joboet commented Nov 7, 2024

@EFanZh Is this ready for review? If so, please un-draft the PR.

@EFanZh
Copy link
Contributor Author

EFanZh commented Nov 7, 2024

@joboet: The source code part is mostly done, but I haven’t finished updating LLDB and CDB pretty printers. The CI doesn’t seem to run those tests.

@joboet
Copy link
Member

joboet commented Nov 8, 2024

No worries! I just didn't want to keep you waiting in case you had forgotten to change the state.
@rustbot author

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Nov 8, 2024
@EFanZh EFanZh force-pushed the zero-cost-rc-arc-deref branch 3 times, most recently from f243654 to 1308bf6 Compare November 11, 2024 18:35
Copy link
Contributor

@thaliaarchi thaliaarchi left a comment

Choose a reason for hiding this comment

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

I don't have any substantial feedback, because it's a lot of code and I haven't done comparisons between what was deleted in Rc<T> and Arc<T> and the new code. But I'm excited for this change and it looks generally good!

But the largest thing that stands out to me is that there's a lot of tiny functions. Although they have descriptive names and are documented, it made it somewhat harder to follow for me. For the ones used only once, it might make sense to inline them for readability.

Copy link
Member

@joboet joboet left a comment

Choose a reason for hiding this comment

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

This will take some time to review, sorry. Some initial comments:


/// Creates an allocator of type `A`, then tries to allocate a chunk of reference-counted memory
/// that is described by `rc_layout`.
fn try_allocate_uninit<A, const STRONG_COUNT: usize>(
Copy link
Member

Choose a reason for hiding this comment

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

Do you have a particular reason for making this a separate function instead of just passing Global into the generic function?

Copy link
Contributor Author

@EFanZh EFanZh Mar 29, 2025

Choose a reason for hiding this comment

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

This is because passing a ZST is zero-cost, but passing a reference to a ZST is not. I assumed that most of the time, the allocator will be the ZST type Global, so if for some reason (maybe with opt-level=z) this function is not inlined into caller, caller does’t have to pay for passing an allocator.

Another reason is that if A::default is not a trivial function, code for constructing an allocator could be potentially shared by all callers, so that the binary sized could benefit from this.

Comment on lines 159 to 165
impl Deref for RcLayout {
type Target = Layout;

fn deref(&self) -> &Self::Target {
&self.0
}
}
Copy link
Member

Choose a reason for hiding this comment

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

This is never really a good idea, RcLayout isn't a smart pointer.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have replaced dereference with a get function.

Comment on lines +167 to +170
pub(crate) trait RcLayoutExt {
/// Computes `RcLayout` at compile time if `Self` is `Sized`.
const RC_LAYOUT: RcLayout;
}

impl<T> RcLayoutExt for T {
const RC_LAYOUT: RcLayout = match RcLayout::try_from_value_layout(T::LAYOUT) {
Ok(rc_layout) => rc_layout,
Err(_) => panic!("value is too big to store in a reference-counted allocation"),
};
}
Copy link
Member

Choose a reason for hiding this comment

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

Inline const is stable, would that be an alternative here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

T::RC_LAYOUT is used in multiple places, I think making it an associated const could potentially avoid calculating it multiple times. This trait has a similar role as the core::mem::SizedTypeProperties trait.

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Mar 28, 2025
@EFanZh EFanZh force-pushed the zero-cost-rc-arc-deref branch 2 times, most recently from 376c802 to ac5a4e6 Compare March 29, 2025 12:16
@rust-log-analyzer

This comment has been minimized.

@EFanZh EFanZh force-pushed the zero-cost-rc-arc-deref branch from ac5a4e6 to 41d9c4c Compare March 29, 2025 13:01
@rust-log-analyzer

This comment has been minimized.

@EFanZh EFanZh force-pushed the zero-cost-rc-arc-deref branch from 41d9c4c to 9d4fbaf Compare March 29, 2025 13:40
@EFanZh
Copy link
Contributor Author

EFanZh commented Mar 29, 2025

But the largest thing that stands out to me is that there's a lot of tiny functions. Although they have descriptive names and are documented, it made it somewhat harder to follow for me. For the ones used only once, it might make sense to inline them for readability.

@thaliaarchi: Do you mean the allocation functions? I have checked those functions, most functions are used more than once, and the function that are used once are used for unifying non-generic codes used in generic functions.

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Mar 29, 2025
@bors
Copy link
Collaborator

bors commented Mar 29, 2025

☔ The latest upstream changes (presumably #133572) made this pull request unmergeable. Please resolve the merge conflicts.

@EFanZh EFanZh force-pushed the zero-cost-rc-arc-deref branch 3 times, most recently from a72c6d5 to 2ae47dd Compare March 30, 2025 06:42
@bors
Copy link
Collaborator

bors commented Apr 28, 2025

☔ The latest upstream changes (presumably #140378) made this pull request unmergeable. Please resolve the merge conflicts.

@EFanZh EFanZh force-pushed the zero-cost-rc-arc-deref branch from 2ae47dd to 7999ed8 Compare May 1, 2025 02:26
@EFanZh EFanZh requested review from joboet, scottmcm and thaliaarchi May 1, 2025 04:45
@bors
Copy link
Collaborator

bors commented May 7, 2025

☔ The latest upstream changes (presumably #140726) made this pull request unmergeable. Please resolve the merge conflicts.

@joboet
Copy link
Member

joboet commented May 15, 2025

This PR is way too large for me to review. Is there any way you could split this up? E.g. by moving the Arc changes to a different PR...

@EFanZh
Copy link
Contributor Author

EFanZh commented May 15, 2025

@joboet: The majority codes are in alloc::raw_rc module, which has to be submitted with at least one of alloc::rc and alloc::sync changes, otherwise they will be unused codes. Do you accept that I split this PR into these two PRs?

  1. PR containing alloc::raw_rc + alloc::rc changes: this one will still contain a large chunk of code, which I don’t have a good way to further split.
  2. PR containing alloc::sync changes.

@thaliaarchi
Copy link
Contributor

Alternatively, an approach to consider, if splitting horizontally isn’t feasible, you could split it vertically: Split it into a commit history of discrete, atomic steps progressing towards the goal of the PR. Even if it needs to be merged all at once, this can aid reviewing.

I know the Rust project prefers smaller PRs, though, so that should be preferred if possible, but the approaches could be combined.

@joboet
Copy link
Member

joboet commented May 16, 2025

@joboet: The majority codes are in alloc::raw_rc module, which has to be submitted with at least one of alloc::rc and alloc::sync changes, otherwise they will be unused codes. Do you accept that I split this PR into these two PRs?

1. PR containing `alloc::raw_rc` + `alloc::rc` changes: this one will still contain a large chunk of code, which I don’t have a good way to further split.

2. PR containing `alloc::sync` changes.

That sounds very reasonable!

@EFanZh EFanZh force-pushed the zero-cost-rc-arc-deref branch from 7999ed8 to 369a658 Compare May 17, 2025 02:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
perf-regression Performance regression. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. 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.