-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Specialize arena alloc for cloned slices #81053
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
Conversation
r? @varkor (rust-highfive has picked a reviewer for you, use r? to override) |
@bors try @rust-timer queue |
Awaiting bors try build completion. |
⌛ Trying commit 9ab93f2 with merge 691e8249eca167a156669b07665b8be442f00e7c... |
@@ -133,6 +160,10 @@ where | |||
} | |||
} | |||
|
|||
#[rustc_unsafe_specialization_marker] | |||
trait Clonable: Clone {} | |||
impl<T: Clone> Clonable for T {} |
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.
My somewhat limited understanding of the min_specialization
rules is that you're here promising that any specialization relying on Clonable
will only be applied to types where T: Clone
is not dependent lifetimes (directly or indirectly)
E.g. that there's no impl Clone for &'static Foo
involved. That can only be guaranteed if you could also manually enumerate all T, i.e. they're not types supplied by external crates.
It would also mean that it's unsafe to use TypedArena
for such types and users of it would have to uphold that in the future.
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'm not quite sure if this is the case, but I don't know the feature well enough. I was going by the example of FusedIterator
and TrustedLen
, both of which are implemented for e.g. std::slice::Iterator<'a, T: 'a>
and I believe both are specialization marker traits.
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.
The thing is that FusedIterator
or TrustedLen
are not implemented for arbitrary T
. They're only implemented for structs controlled by std and those impl
are only conditional on additional bounds that themselves are other specialization traits.
What's different here is that you're essentially declaring that any and all Clone
types can act as a specialization marker for specializations relying on Clonable
. This would only be valid when you have some way of reasoning about the set of T
. And since T
is what goes into TypedArena
it means you've ensured that all uses of TypedArena
are compatible with the min specialization rules. This kind of far-reaching obligation is why there's the word unsafe
in that annotation.
But again, my understanding of min_specialization
is quite limited, so I may be wrong 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.
All right, makes sense. I wish I had another way to specialize for Cloned<slice::Iter>
:( Whatever else I tried, I got a conflicting implementation error....
But even then, perf may get back completely negative or neutral as well.
☀️ Try build successful - checks-actions |
Queued 691e8249eca167a156669b07665b8be442f00e7c with parent bc39d4d, future comparison URL. @rustbot label: +S-waiting-on-perf |
let start_ptr = arena.alloc_raw_slice(len); | ||
write_from_iter(self, len, start_ptr) | ||
} | ||
} |
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 doesn't seem to rely on anything specific to Cloned
. It just relies on the length. Wouldn't specializing on TrustedLen
be sufficient here, considering that's already a specialization trait?
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.
No, it wouldn't compile unfortunately, or at least I couldn't get it to. Cloned needs Clone on T.
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.
Also, just specializing on anything TrustedLen is unsound, see #77945 for that. In short, Map is TrustedLen and can allocate on the same arena, which makes handling a panic safely an impossible nightmare of dropping uninitialized memory.
I went through the uses of alloc_from_iter and I found that most of those are Map and FlatMap, but some places allocate from Clonedslice::Iter so hence the PR.
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 meant impl<I: TrustedLen> IterExt for I
. That'll give you the length.
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.
Well it's closed already. But for the record, you might have been able to use use impl<I: TrustedLen + IsClonedSliceIterMarker> for I
instead where the latter bound is a safe specialization marker.
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.
Indeed, this seems to compile:
#[rustc_specialization_trait]
trait IsClonedSliceIterMarker {}
impl<T> IsClonedSliceIterMarker for Cloned<Iter<'_, T>> {}
impl<T, I> IterExt<T> for I
where
I: IntoIterator<Item = T> + IsClonedSliceIterMarker + TrustedLen,
{
#[inline]
fn alloc_from_iter(self, arena: &TypedArena<T>) -> &mut [T] {
let iter = self.into_iter();
let len = iter.size_hint().0;
if len == 0 {
return &mut [];
}
// Move the content to the arena by copying and then forgetting it
unsafe {
let start_ptr = arena.alloc_raw_slice(len);
write_from_iter(iter, len, start_ptr)
}
}
}
Finished benchmarking try commit (691e8249eca167a156669b07665b8be442f00e7c): comparison url. Benchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. Please note that if the perf results are neutral, you should likely undo the rollup=never given below by specifying Importantly, though, if the results of this run are non-neutral do not roll this PR up -- it will mask other regressions or improvements in the roll up. @bors rollup=never |
Looks like noise. Given the specialization is in question, I think it makes sense to close this. |
Copy contents of a
slice.iter().cloned()
directly into the arena. Also replace implementation ofwrite_from_iter
to something that can maybe be optimized better: https://rust.godbolt.org/z/qox784Let me know if I did something entirely terribly incorrect with the custom
Clonable
specialization marker, I'm not sure if it's valid to add stuff like that.