-
Notifications
You must be signed in to change notification settings - Fork 13.2k
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
Make Rc<T>::deref
and Arc<T>::deref
zero-cost
#132553
base: master
Are you sure you want to change the base?
Conversation
b283c44
to
ae36f44
Compare
This comment has been minimized.
This comment has been minimized.
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 |
This comment has been minimized.
This comment has been minimized.
I think in theory it is possible, at least for sized types, but I am not familiar with how to formally make it so. |
ae36f44
to
0d6165f
Compare
This comment has been minimized.
This comment has been minimized.
0d6165f
to
98edd5b
Compare
This comment has been minimized.
This comment has been minimized.
r? libs |
98edd5b
to
8beb51d
Compare
This comment has been minimized.
This comment has been minimized.
8beb51d
to
d7879fa
Compare
This comment has been minimized.
This comment has been minimized.
d7879fa
to
317aa0e
Compare
@EFanZh Is this ready for review? If so, please un-draft the PR. |
@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. |
No worries! I just didn't want to keep you waiting in case you had forgotten to change the state. |
f243654
to
1308bf6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for tackling this, and especially finding a way to keep the debug visualizers working.
It generally looks good to me, though TBH my eyes are starting to glaze over a bit after multiple 3k-line files. I've left a bunch of thoughts as I was going along, most of them should be hopefully easy or don't actually need anything this PR.
The one big one is https://github.com/rust-lang/rust/pull/132553/files#r2008914982 about how best to handle what's essentially the strategy pattern here. Don't take that as an immutable directive, though, I'd be happy to discuss how best to handle the thoughts I had there.
(And if you wouldn't mind, it'd be great to find some useful file splits for the raw_rc
file -- can any of it be split out to help people review in chunks?)
178880a
to
bd899f5
Compare
This comment has been minimized.
This comment has been minimized.
bd899f5
to
17888f9
Compare
This comment has been minimized.
This comment has been minimized.
42b7a8b
to
5224b3c
Compare
This comment has been minimized.
This comment has been minimized.
e510e8c
to
940cc65
Compare
There was a problem hiding this 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.
There was a problem hiding this 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>( |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 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.
impl Deref for RcLayout { | ||
type Target = Layout; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
&self.0 | ||
} | ||
} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
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"), | ||
}; | ||
} |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
376c802
to
ac5a4e6
Compare
This comment has been minimized.
This comment has been minimized.
ac5a4e6
to
41d9c4c
Compare
This comment has been minimized.
This comment has been minimized.
41d9c4c
to
9d4fbaf
Compare
@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 |
☔ The latest upstream changes (presumably #133572) made this pull request unmergeable. Please resolve the merge conflicts. |
689d406
to
a72c6d5
Compare
a72c6d5
to
2ae47dd
Compare
Currently,
Rc<T>
andArc<T>
store pointers toRcInner<T>
andArcInner<T>
. This PR changes the pointers so that they point toT
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 fromRcInner<T>
andArcInner<T>
to their contained data. This change might also enables some possibly useful future optimizations, such as:&[Rc<T>]
into&[&T]
within O(1) time.&[Rc<T>]
intoVec<&T>
utilizingmemcpy
.&Option<Rc<T>>
intoOption<&T>
without branching.Rc<T>
andArc<T>
FFI compatible types whereT: Sized
.