-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
improve clone_from_slice performance #17960
Conversation
Using |
Updated with clone_from. |
This will be optimized to a memcpy for Copy types. I don't see how Zip and Chain can be optimized for this case because Rust doesn't allow you to write a specialized implementation for ExactSize iterators. |
The lack of a specialized implementation isn't a blocker for having it optimize. Attempting to fix the underlying issue is far better than just working around it across the standard library before it's deemed to be infeasible. @zwarich worked on fixing missed optimizations for iterators and probably has a good idea about what would be necessary here. |
If we want the void f(int *a1, int *a2, size_t len1, size_t len2) {
for (size_t i = 0, j = 0; i < len1 && j < len2; ++i, ++j) {
a1[i] = a2[j];
}
} It is able to vectorize it, albeit with an aliasing check for the two arrays outside of the vectorized loop. It seems like it really should be able to realize that it can call void g(int *a1, int *a2, size_t len1, size_t len2) {
for (size_t i = 0; i < len1 && i < len2; ++i) {
a1[i] = a2[i];
}
} If I add void f(int * restrict a1, int * restrict a2, size_t len1, size_t len2) {
for (size_t i = 0, j = 0; i < len1 && j < len2; ++i, ++j) {
a1[i] = a2[j];
}
} Turning the If we can get the Rust IR to look like the IR from the C code, then we have two options for getting a lib call out of it:
|
Actually, shouldn't we be able to say that an |
@zwarich: Yes, but we'd need to mark it via the new scoped noalias metadata as parameter attributes only work for the outermost pointer. |
@thestinger We would have to split slices into their components at the top of functions, and then use scoped aliasing metadata with a scope corresponding to the whole function. This is morally equivalent to changing the calling convention to pass slices as separate components. It isn't sound to use scoped metadata with a scope of the entire function for every |
@zwarich: I don't think non-overlapping borrows violates LLVM's NoAlias guarantee. There is no memory dependency between them so it doesn't matter.
|
LLVM converts our usage of |
@thestinger Consider a program fragment like the following: let mut i = 0i;
{
let p = &mut i;
*p = 1;
}
{
let q = &mut i;
*q = 2;
} Ignoring the reality that the LLVM optimizer will just convert this to SSA form and ignore any alias info we give, saying that both |
@zwarich: Those borrows don't overlap in the same scope though. The scoped noalias metadata isn't just a function scope thing. |
@thestinger My original comment that I thought you were disagreeing with mentioned that we can't put them all in the same scope, unless we unpack the slice once at the beginning of the function:
|
Old vs. New vs. Vec::push_all ``` test slice ... bench: 3091942 ns/iter (+/- 54460) test slice_new ... bench: 1800065 ns/iter (+/- 69513) test vec ... bench: 1804805 ns/iter (+/- 75609) ```
fix: add extra_test_bin_args to test explorer test runner `@HKalbasi` I thought I included this in rust-lang#17470 but it appears not so I have created a new issue rust-lang#17959 for this fix.
Old vs. New vs. Vec::push_all