Re:Await in Rust
by pron on Monday August 12, 2019 @03:27PM
(#20678986)
Delimited continuations (I'm talking about one-shot, or non-reentrant continuations, AKA "stackful" coroutines) don't require more memory than async/await, as they capture the exact same state. That's the approach we're taking in Java, but I don't think it's particularly novel.
[ Parent
| on HN ]
One shot delimited continuations capture strictly more state than a simple async/await implementation. The latter only captures bindings that are referenced after resumption, thus naturally implementing a sort of liveness analysis that discards unnecessary state. You could of course do the same thing during regular compilation, but it's not strictly necessary, and the interaction with GC means this state could potentially live much longer.
That said, the extra object headers from heap-allocated activations will make up some of the difference.
Why would this be the case? Normal stack layout and register allocation already discards things that are not live across function calls. (With some potential wiggle room because that's not the primary goal, I suppose?)
I guess that the savings obtained by a more aggressive spilling optimizations are more appealing in case of long running async state. In any case real workload measurements should be the only judge.
This liveness analysis isn't strictly necessary for async/await, either. You can do it in both cases or in none. In any event, it is not a matter of storing more stack, but of the level of optimization in the stack representation. Interaction with a GC brings its own complications as well as opportunities.
You're right that it's not strictly necessary, but as I said, it falls out naturally from computing the required continuations. This optimization doesn't necessarily happen naturally when compiling direct code, depending on your intermediate IR of course, ie. say, if your IR is already continuation-based.
It'll be interesting to see how this plays out on the JVM though.