-
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
Generator size: borrowed variables are assumed live across following yield points #59087
Comments
Closing as a sub-issue of #52924. (I've edited the top message in that thread to reference this one) |
There definitely seems to be something causing locals to be unnecessarily put into the generator struct instead of staying as true-locals. Using a simplified function (with the same async fn composed() {
let inner = i_am_1kb();
{ let _foo = &inner; }
await!(inner);
} and running |
@Nemo157 The generator transformation conservatively assumes that any borrow can be converted to a raw pointer and the locals can be accessed with that until their storage slot is dead. That's why |
Yeah, @eddyb and I discussed this at the all-hands, and that making a type implement |
Ok, so an optimisation to fix this example would be to add a less conservative check that can see when the borrow definitely wasn’t converted to a raw pointer. That seems relatively straightforward to check when the borrow never enters any unsafe code. |
Note that for this to be very useful at all it would have to be able to see through functions (via MIR inlining) and intrinsics (e.g. |
Can it not trust the lifetimes on safe functions signatures? |
No, lifetimes in function signatures cannot necessarily be used to determine the scope of accesses to the resulting pointer. Without a memory model it's completely unclear when accesses would or wouldn't be allowed to the underlying memory. @RalfJung's work on stacked borrows is the only thing I'm aware of that would allow proper analysis, and in general anywhere there's a ref-to-ptr conversion, all bets are sort of off. |
I want to emphasize that this is still a problem even when the variable is not borrowed:
So there is likely progress to be made here without doing the analysis being discussed by @Nemo157 / @cramertj. |
From skimming the MIR of async fn composed() {
let inner = i_am_1kb();
{ foo(); fn foo() { } }
await!(inner);
} it looks like that could be related to the unwind edge from the function call (and (I tried a couple of other random snippets of code and couldn't see anything else done by EDIT: Actually, because of how drop chains work it looks like it's going to be more complex than that since |
Opened #59123 about the unwinding and drop interaction. |
We discussed this issue and decided to label it as deferred for the purposes of stabilization -- it's a bit too broad. We might consider trying to fix specific instances of this problem. Certainly, to start, we would want to fix #52924 and revisit. |
#57478 contains a similar issue. |
The size growth in this issue now goes away when the However, I think we should leave this issue open to track the general behavior that borrowing a future and then awaiting it causes us to double-allocate space for it, with #62321 tracking the specific case of |
Here's the solution I mentioned in #62321 (comment):
As noted by @cramertj and @withoutboats, there is an alternative that resolves most cases we care about: get rid of the move in
|
I meant to say: Right now I prefer the approach of getting rid of the move in Resolving the general problem of borrow-then-move would be nice, but it doesn't seem like the most bang for our buck at this point. |
It would be so much easier to do that drop in place and have borrowck understand the value is no longer accessible, if we were desugaring |
This commit favors FutureExt::map over async blocks to mitigate the issue of async block doubled memory usage. Through the sysbench oltp_read_only test, it was observed that this adjustment resulted in approximately 26% reduction in memory usage. See: rust-lang/rust#59087 Signed-off-by: Neil Shen <[email protected]>
close #16540 *: enable linters about async and futures We should be pedantic about writing async code, as it's easy to write suboptimal or even bloat code. See: rust-lang/rust#69826 *: remove unnecessary async blocks to save memory This commit favors FutureExt::map over async blocks to mitigate the issue of async block doubled memory usage. Through the sysbench oltp_read_only test, it was observed that this adjustment resulted in approximately 26% reduction in memory usage. See: rust-lang/rust#59087 Signed-off-by: Neil Shen <[email protected]> Co-authored-by: ti-chi-bot[bot] <108142056+ti-chi-bot[bot]@users.noreply.github.com>
This commit favors FutureExt::map over async blocks to mitigate the issue of async block doubled memory usage. Through the sysbench oltp_read_only test, it was observed that this adjustment resulted in approximately 26% reduction in memory usage. See: rust-lang/rust#59087 Signed-off-by: Neil Shen <[email protected]>
close #16540 *: enable linters about async and futures We should be pedantic about writing async code, as it's easy to write suboptimal or even bloat code. See: rust-lang/rust#69826 *: remove unnecessary async blocks to save memory This commit favors FutureExt::map over async blocks to mitigate the issue of async block doubled memory usage. Through the sysbench oltp_read_only test, it was observed that this adjustment resulted in approximately 26% reduction in memory usage. See: rust-lang/rust#59087 Signed-off-by: Neil Shen <[email protected]> Co-authored-by: Neil Shen <[email protected]>
close tikv#16540 *: enable linters about async and futures We should be pedantic about writing async code, as it's easy to write suboptimal or even bloat code. See: rust-lang/rust#69826 *: remove unnecessary async blocks to save memory This commit favors FutureExt::map over async blocks to mitigate the issue of async block doubled memory usage. Through the sysbench oltp_read_only test, it was observed that this adjustment resulted in approximately 26% reduction in memory usage. See: rust-lang/rust#59087 Signed-off-by: Neil Shen <[email protected]> Co-authored-by: ti-chi-bot[bot] <108142056+ti-chi-bot[bot]@users.noreply.github.com> Signed-off-by: dbsid <[email protected]>
This commit favors FutureExt::map over async blocks to mitigate the issue of async block doubled memory usage. Through the sysbench oltp_read_only test, it was observed that this adjustment resulted in approximately 26% reduction in memory usage. See: rust-lang/rust#59087 Signed-off-by: Neil Shen <[email protected]>
close #16540 *: enable linters about async and futures We should be pedantic about writing async code, as it's easy to write suboptimal or even bloat code. See: rust-lang/rust#69826 *: remove unnecessary async blocks to save memory This commit favors FutureExt::map over async blocks to mitigate the issue of async block doubled memory usage. Through the sysbench oltp_read_only test, it was observed that this adjustment resulted in approximately 26% reduction in memory usage. See: rust-lang/rust#59087 Signed-off-by: Neil Shen <[email protected]> Co-authored-by: Neil Shen <[email protected]> Co-authored-by: ti-chi-bot[bot] <108142056+ti-chi-bot[bot]@users.noreply.github.com>
Maybe a duplicate of #52924, but maybe also something else.
I observed that the sizes of
Future
s generated by async fns can grow exponentially.The following code shows an async fn, which produces a 1kB future. Each layering in another async fn doubles it's size:
Output:
It doesn't matter whether the statement between the future generation and
await!
references the future or not. A simplyprintln("")
will have the same effect.Only if the future is directly awaited (as in
composed_1
) the size will stay constant.cc @cramertj , @nikomatsakis , @Nemo157
The text was updated successfully, but these errors were encountered: