-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Safe memset for slices #2067
Comments
Would this only exist for |
Would adding a method to |
I think a slightly more general impl would be a blanket impl like impl<T: Copy> [T] {
pub fn fill(val: T) {
for i in self.iter_mut() {
*i = val;
}
}
} |
@SimonSapin I would like Basically I would like a safe |
@sethjackson Right, I understand the goal, it’s just more tricky to get there than with First, would the argument to |
Ah I see. I was thinking the argument would be |
@sethjackson Right, that's what I was going for too. |
The implementation doesn't have to lower to |
Hmm... I guess I was imagining that it would be something the compiler
optimizes away, rather than explicitly writing code to choose...
…On Jul 22, 2017 9:58 PM, "Austin Bonander" ***@***.***> wrote:
The implementation doesn't have to lower to memset, it could hit a
vectorized write loop instead. It could have a check for if the bytes of
the value are all the same (ignoring padding) and lower to memset then.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#2067 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AIazwDyEwLsqP-K1BnDEQwPm-rC4w1lBks5sQqjPgaJpZM4OZIFP>
.
|
LLVM can still optimize it, of course. I'm just thinking about performance improvement for dynamic values. |
@sethjackson (and others) For anything other than |
@joshtriplett not sure I understand. What do you mean by full memset or just zeroing? For |
I would prefer this a generic method using either impl<T> [T] {
fn fill(&mut self, value: T) where T: Copy { ... }
} For (An alternative could potentially be a generalization, that it instead is a method on iterators - implemented for |
You could have an In fact, this abstraction is already possible in stable rust. Hopefully, something like this will be added to nightly soon. |
@Techcable We could also have a parent trait of that, for types that permit zero-filled memory as a valid representation. Floating-point types can implement that, too, as can Option. |
|
Indeed, I have a branch where |
@joshtriplett However, like @bluss suggested specialization could be used to provide much of the benefit transparently when the null-pointer representation is used. Specialization could also be used to optimize filling an |
One curious thing I saw when this came up in Discord: the simple loop will turn into memset for I assume that's because the padding is |
Add slice::fill Adds the `slice::fill` method to fill a slice with an item. This replaces manual for loops where items are copied one-by-one. This is a counterpart to C++20's [`std::fill`](https://en.cppreference.com/w/cpp/algorithm/fill) function. ## Usage ```rust let mut buf = vec![0; 10]; buf.fill(1); assert_eq!(buf, vec![1; 10]); ``` ## Performance When compiling in release mode, for `[u8]` and `[u16]` this method will optimize to a `memset(3)` call ([godbolt](https://godbolt.org/z/85El_c)). The initial implementation relies on LLVM's optimizer to make it as fast as possible for any given input. But as @jonas-schievink [pointed out](https://twitter.com/sheevink/status/1245756597453885442) this can later be optimized through specialization to guarantee it has a specific performance profile. ## Why now? Conversations about adding `slice::fill` are not new. In fact, rust-lang/rfcs#2067 was opened 3 years ago about this exact topic. However discussion stranded while discussing implementation details, and it's not seen much forward motion since. In ["The Hunt for the Fastest Zero"](https://travisdowns.github.io/blog/2020/01/20/zero.html) Travis Downs provides disects C++'s `std::fill` performance profile on gcc, comparing it among others to `memset(3)`. Even though `memset(3)` outperforms `std::fill` in their tests, the author notes the following: > That the optimization fails, perhaps unexpectedly, in some cases is unfortunate but it’s nice that you can fix it yourself. [...] Do we throw out modern C++ idioms, at least where performance matters, for example by replacing std::fill with memset? I don’t think so. Much of the article focuses on how how to fix the performance of `std::fill` by providing specializations for specific input. In Rust we don't have any dedicated methods to fill slices with values, so it either needs to be optimized at the MIR layer, or more likely rely on LLVM's optimizer. By adding a dedicated method for filling slices with values it opens up the ability for us to in the future guarantee that e.g. `Vec<u8>` will always optimize to `memset` even in debug mode. Or perhaps provide stronger guarantees about memory when zeroing values when a certain flag is passed. But regardless of that, it improves general ergonomics of working with slices by providing a dedicated method with documentation and examples. ## References - [slice-fill prototype on docs.rs](https://docs.rs/slice-fill/1.0.1/slice_fill/) - [The Hunt For The Fastest Zero](https://travisdowns.github.io/blog/2020/01/20/zero.html) - [Safe memset for slices](rust-lang/rfcs#2067) - [C++20 std::fill](https://en.cppreference.com/w/cpp/algorithm/fill) - [ASM output on Godbolt](https://godbolt.org/z/5-XU66)
The slice::fill method is now stable. |
RFC 1419 implemented safe
memcpy
but notmemset
.It would be awesome if we could fill slices with data in a safe manner that would lower into
memset
.Currently if I want to fill a slice with
memset
semantics I have to do this:I would like to be able to do something like this:
The text was updated successfully, but these errors were encountered: