-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Tracking Issue for array_map
#75243
Comments
A possible followup optimization: Perform this in place when |
Add note & example about iter order Add doc changes Update doc comments
Add note & example about iter order Add doc changes Update doc comments
This feature is currently breaking error[E0658]: use of unstable library feature 'array_map'
--> lambda-twist/tests/consensus.rs:25:6
|
25 | .map(|&p| Point3::from(p));
| ^^^
|
= note: see issue #75243 <https://github.com/rust-lang/rust/issues/75243> for more information
= help: add `#![feature(array_map)]` to the crate attributes to enable This code started breaking on more recent Rust versions as it seems that it thinks I am attempting to use the map method on array created by this feature, but actually I am using Since I am depending on nightly anyways, I will simply use this feature instead to get around this issue. |
Ah I didn't know this crate existed, thanks for the heads up. I'm not quite sure how to resolve this, but will investigate. |
That seems like a compiler bug. I think a warning should show, but due to trait ArrayExt {
fn map<F: FnMut(i32) -> i32>(&self, f: F);
}
impl ArrayExt for [i32; 1] {
fn map<F: FnMut(i32) -> i32>(&self, f: F) {}
}
fn main() {
[4].map(|x| x);
} In my opinion, this should print the following:
That being said, I'm kinda wondering whether |
Hm, fair questions. It does usually make sense to take |
That should be solved by #75490. Then you can simply as |
What do you all think about specifying the order in which the closure is called and then already stabilizing this method? I know it hasn't been on nightly for very long, but I don't see what we would want to change about this. It's a pretty straight forward method. Let's go through the parts of the signature:
Regarding the order in which the closure is called: I think it is safe to specify the obvious order (index order, starting from 0, going up). We don't work on elements concurrently as mentioned above, so I don't see why we would chose another order. |
Thread usage mostly has to do with
|
There were some light discussion in #80094 about something like iterators that encode the length. Would that be something to consider before stabilizing this array_map? |
It seems that |
It seems that |
I'm also blocked by this hitting stable. |
With #82098, the assembly output has improve somewhat: https://godbolt.org/z/hsEdqv (Same link @eduardosm posted above, but Godbolt automatically updates the nightly). It's still not as good as the other two versions though. |
Sorry for the noise, please let me know if I should ask this question somewhere else and if so where :) What is the (or is there a) long term plan when it comes to iter-like methods on My question is If the plan is to make arrays behave a bit like iterators then why not make an iterator-like thing that works with arrays(and other collections of fixed size) instead of tacking the methods directly to With that, let res: [_; 4] = [1, 2, 3, 4]
.into_iter_fixed()
.map(...)
.collect(); a few more lines of code, sure, however it gives access to a whole lot of other methods and code can be made generic over the underlying collection. Edit: the proposed iter-like methods I know of this far are |
Most of methods in |
I'm pretty sure that the performance pitfall is not something that can in any way ever be mitigated other than through possibly clippy lints, which however is maybe good enough. |
Are you talking specifically about large arrays? For an |
No, this has nothing to do with moving (that's a separate issue, that can indeed be fixed). Instead it has something to do with the fact that the maps can't ever "merge together" as they would in a normal iterator chain. It is forced to fully complete the first map before starting the second, meaning that it is forced to do 3 entire loops over the array as opposed to just a single loop with the 3 closures merged and therefore optimized together. LLVM may be smart enough to at some point (atm it definitely doesn't do this) realize that the 3 closures are order independent and do the optimization anyway, but the moment either of those do anything that's not order independent, no optimization will ever be possible. Overall I guess it's fine merging this. There are many other ways to write slow code in Rust. This just needs a few lints and that's it. |
@CryZe I just played with a few examples and looked at the generated assembly. E.g. see this: https://rust.godbolt.org/z/M1KTbYfqv For some examples, LLVM really doesn't perform too well. I wasn't quite aware of that, so thanks for bringing that up again. But note that for smallish arrays, LLVM seems to unroll all loops. And in those cases, it seems to perform quite well and combine the different operations. In the example above, the threshold is somewhere between 32 and 50. And I would expect a considerable amount of And while I don't know anything about how LLVM works internally, I feel like there is nothing fundamental about the design of All that said, I think a note about this possible performance pitfall in the docs for |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
Even if this method is useless in 99% of cases, it lets people write default arrays. So for that alone it's worth it. And the 99% figure is itself hyperbolic. |
I don't know if this is the right place for this, but I'd like to report that Minimal example: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=2d96e8f66b3432a8d1ecf0ca4331f85a In the example, at some point more than 16 times more stack is used than the original array size. As far as I've looked into the actual code, the only thing that stood out to me is use of |
Works in release at least. In general, Rust debug performance has problems like this all the time, and can hit stack size limits easily. Particularly on embedded for example. I think Rust can work on this problem, but it's not specifically the fault of array_map. |
created #86912 to track that stack usage issue |
The final comment period, with a disposition to merge, as per the review above, is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. The RFC will be merged soon. |
…nytm Stabilize `[T; N]::map()` This stabilizes the `[T; N]::map()` function, gated by the `array_map` feature. The FCP has [already completed.](rust-lang#75243 (comment)) Closes rust-lang#75243.
…ocs, r=m-ou-se Add docs about performance and `Iterator::map` to `[T; N]::map` This suboptimal code gen for some usages of array::map got a bit of attention by multiple people throughout the community. Some cases: - rust-lang#75243 (comment) - rust-lang#75243 (comment) - https://www.reddit.com/r/rust/comments/oeqqf7/unexpected_high_stack_usage/ My *guess* is that this gets the attention it gets because in JavaScript (and potentially other languages), a `map` function on arrays is very commonly used since in those languages, arrays basically take the role of Rust's iterator. I considered explicitly naming JavaScript in the first paragraph I added, but I couldn't find precedence of mentioning other languages in standard library doc, so I didn't add it. When array::map was stabilized, we still wanted to add docs, but that somehow did not happen in time. So here we are. Not sure if this sounds crazy but maybe it is worth considering beta backporting this? Only if it's not a lot of work, of course! But yeah, stabilized array::map is already in beta and if this problem is really as big as it sometimes seems, might be worth having the docs in place when 1.55 is released. CC `@CryZe` r? `@m-ou-se` (since you were involved in that discussion and the stabilization)
…ocs, r=m-ou-se Add docs about performance and `Iterator::map` to `[T; N]::map` This suboptimal code gen for some usages of array::map got a bit of attention by multiple people throughout the community. Some cases: - rust-lang#75243 (comment) - rust-lang#75243 (comment) - https://www.reddit.com/r/rust/comments/oeqqf7/unexpected_high_stack_usage/ My *guess* is that this gets the attention it gets because in JavaScript (and potentially other languages), a `map` function on arrays is very commonly used since in those languages, arrays basically take the role of Rust's iterator. I considered explicitly naming JavaScript in the first paragraph I added, but I couldn't find precedence of mentioning other languages in standard library doc, so I didn't add it. When array::map was stabilized, we still wanted to add docs, but that somehow did not happen in time. So here we are. Not sure if this sounds crazy but maybe it is worth considering beta backporting this? Only if it's not a lot of work, of course! But yeah, stabilized array::map is already in beta and if this problem is really as big as it sometimes seems, might be worth having the docs in place when 1.55 is released. CC ``@CryZe`` r? ``@m-ou-se`` (since you were involved in that discussion and the stabilization)
The feature gate for the issue is
#![feature(array_map)]
.Public API
Steps / History
array
lang item and[T; N]::map(f: FnMut(T) -> S)
#75212[T; N]::map()
#87174Unresolved Questions
How should order be documented/allowed for map? See #75212 (comment) for discussion.
The text was updated successfully, but these errors were encountered: