[WG21 mailing] N4453 Resumable expressions
I'm finding Chris' paper on resumable expressions instead of compiler magic created resumable functions awfully compelling: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4453.pdf I essentially proposed the same with my functionally composed constexpr basic_future design some months ago. Would anyone else like to comment on this library based instead of compiler based resumable execution? I'm not against some compiler support for resumable execution, but I am finding the await based proposal highly problematic, and Chris does an excellent job in summarising my own thoughts on what's wrong with it. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
2015-04-15 21:27 GMT+08:00 Niall Douglas
I'm finding Chris' paper on resumable expressions instead of compiler magic created resumable functions awfully compelling:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4453.pdf
I essentially proposed the same with my functionally composed constexpr basic_future design some months ago.
Would anyone else like to comment on this library based instead of compiler based resumable execution? I'm not against some compiler support for resumable execution, but I am finding the await based proposal highly problematic, and Chris does an excellent job in summarising my own thoughts on what's wrong with it.
IIUC, this proposal requires more compiler magic and is definitely compiler based. Await/yield from the "Resumable Functions" proposal is not that mysterious as one may think, at least I can emulate it in some degree using preprocessor: (https://github.com/jamboree/co2) The main point of this proposal is the implicit propagation of the resumability, that is, if a function calls a resumable function, then the function itself becomes a resumable function, which makes the existing template-based algorithms also usable to resumable functions. A major benefit of this approach is that it provides a unified syntax for both stackful and stackless coroutine, and IMHO is better than the approach proposed in N4398. In general, I like the idea of N4453 and would love to see a working language implementation. That said, I do have some questions, for example, it's not clear to me that whether a ctor/dtor could be a resumable function or not, and there may be some other issues that I can't foresee without playing with it.
On 15 Apr 2015 at 22:55, TONGARI J wrote:
Would anyone else like to comment on this library based instead of compiler based resumable execution? I'm not against some compiler support for resumable execution, but I am finding the await based proposal highly problematic, and Chris does an excellent job in summarising my own thoughts on what's wrong with it.
IIUC, this proposal requires more compiler magic and is definitely compiler based.
This surprises me. He provides a reference implementation library which appears functionally complete except for the error checking which does require a compiler pass. The only compiler support needed is for reporting error messages, or at least that's what I read?
Await/yield from the "Resumable Functions" proposal is not that mysterious as one may think, at least I can emulate it in some degree using preprocessor: (https://github.com/jamboree/co2)
By "compiler magic" I mean a new compiler keyword which returns a STL type, and assumes a great deal about how that STL type is implemented. I find that possibly okay for brand new STL types newly added. I find that deeply worrying for existing STL types. Even with their proposed generic all types of future traits support. I also feel that resumable functions need to not require allocating memory. I know they hacked MSVC to magically optimise out the memory allocation in the future<T>, but that doesn't appeal to me.
The main point of this proposal is the implicit propagation of the resumability, that is, if a function calls a resumable function, then the function itself becomes a resumable function, which makes the existing template-based algorithms also usable to resumable functions.
Does this paragraph refer to the Microsoft resumable functions or to Chris' resumable expressions? The latter makes resumability like constexpr, and therefore resumability is caller determined instead of callee determined like the Microsoft proposal. Caller determined resumability has the enormous advantage that you only need to write implementations once, instead of with resumable and non-resumable variants.
A major benefit of this approach is that it provides a unified syntax for both stackful and stackless coroutine, and IMHO is better than the approach proposed in N4398.
In general, I like the idea of N4453 and would love to see a working language implementation.
That said, I do have some questions, for example, it's not clear to me that whether a ctor/dtor could be a resumable function or not, and there may be some other issues that I can't foresee without playing with it.
It seems to me that a constructor is an expression, and therefore capable of being resumed under Chris' proposal. The object being constructed would, I assume, remain partially constructed. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
2015-04-15 23:16 GMT+08:00 Niall Douglas
On 15 Apr 2015 at 22:55, TONGARI J wrote:
Would anyone else like to comment on this library based instead of compiler based resumable execution? I'm not against some compiler support for resumable execution, but I am finding the await based proposal highly problematic, and Chris does an excellent job in summarising my own thoughts on what's wrong with it.
IIUC, this proposal requires more compiler magic and is definitely compiler based.
This surprises me. He provides a reference implementation library which appears functionally complete except for the error checking which does require a compiler pass. The only compiler support needed is for reporting error messages, or at least that's what I read?
The reference implementation is just an emulation, not what he's proposing, what he really proposed is to add a language feature.
Await/yield from the "Resumable Functions" proposal is not that mysterious as one may think, at least I can emulate it in some degree using preprocessor: (https://github.com/jamboree/co2)
By "compiler magic" I mean a new compiler keyword which returns a STL type, and assumes a great deal about how that STL type is implemented.
Both the types that you can await on and the types that you can return from a resumable function are extensible and not restricted to STL. I find that possibly okay for brand new STL types newly added. I find
that deeply worrying for existing STL types. Even with their proposed generic all types of future traits support.
What's your concern here? I also feel that resumable functions need to not require allocating
memory. I know they hacked MSVC to magically optimise out the memory allocation in the future<T>, but that doesn't appeal to me.
The resumable functions in Chris' proposal have to be visible to the compiler in the same TU, if you want them to be in another TU, you need to abstract the resumability away and thus need allocation.
The main point of this proposal is the implicit propagation of the
resumability, that is, if a function calls a resumable function, then the function itself becomes a resumable function, which makes the existing template-based algorithms also usable to resumable functions.
Does this paragraph refer to the Microsoft resumable functions or to Chris' resumable expressions?
I was refering to Chris' resumable expressions.
The latter makes resumability like constexpr, and therefore resumability is caller determined instead of callee determined like the Microsoft proposal.
Well, it's just that you don't need the await keyword. I don't think describing resumable in analog of constexpr is a good idea, they seem to differ in many aspects.
Caller determined resumability has the enormous advantage that you only need to write implementations once, instead of with resumable and non-resumable variants.
As I said, the reusability and the unification are the major attractions of Chris' proposal.
both stackful and stackless coroutine, and IMHO is better than the approach proposed in N4398.
In general, I like the idea of N4453 and would love to see a working language implementation.
That said, I do have some questions, for example, it's not clear to me
A major benefit of this approach is that it provides a unified syntax for that
whether a ctor/dtor could be a resumable function or not, and there may be some other issues that I can't foresee without playing with it.
It seems to me that a constructor is an expression, and therefore capable of being resumed under Chris' proposal. The object being constructed would, I assume, remain partially constructed.
Now that I think of it, the resumable function in Chris' proposal is also not a first-class object, so I guess you can't take its type as it's completely a compile-time construct.
On 16 Apr 2015 at 0:25, TONGARI J wrote:
By "compiler magic" I mean a new compiler keyword which returns a STL type, and assumes a great deal about how that STL type is implemented.
Both the types that you can await on and the types that you can return from a resumable function are extensible and not restricted to STL.
I think therein lies exactly the problem. await is far too limited in usefulness - it has a very specific use case, and is hardcoded to that. What I'd far prefer is the ability to ask the compiler for: 1. An instantiation of a call tree which uses a guaranteed immutable amount of storage at the beginning of the call. "Fixed stacks" is a good description, though it includes any operator new which is always deleted - under fixedstacks they auto convert into fixedstack allocation. This is essentially the same thing as stackless python and how it implements tasklets - fixedstack call trees are like tasklets. 2. The ability to portably ask the compiler for a dump of the current fixedstack (fast) or C stack (slow) into some STL type - let's call it std::execution_context. And that is actually all I want. Because that is all I need to implement coroutines with ideal performance characteristics. What I do NOT want is: 1. A profusion of additional traits and extensions of STL threading primitives where the design is by committee with very little experience of real world use before it is written into stone with many potentially unexpected consequences.
I also feel that resumable functions need to not require allocating
memory. I know they hacked MSVC to magically optimise out the memory allocation in the future<T>, but that doesn't appeal to me.
The resumable functions in Chris' proposal have to be visible to the compiler in the same TU, if you want them to be in another TU, you need to abstract the resumability away and thus need allocation.
Fixedstacks makes that go away because the ABI for fixedstack functions exposes the relevant internal memory allocation state changes for consuming call trees. For call trees which are not fixedstack, you are correct.
Now that I think of it, the resumable function in Chris' proposal is also not a first-class object, so I guess you can't take its type as it's completely a compile-time construct.
Fixedstacks make enough of a call tree entirely known to the top most point of call stack construction, and I believe should make them a first class object. No dynamic memory allocation required. The consequence, of course, is that fixedstack call trees can't do some things if they are to remain fixedstack (same as constexpr where you can call a constexpr function in a non-constexpr fashion). The most obvious is you can't use malloc and cast the allocation into some void *, as that would defeat the whole point of fixed stack. I am sure the compiler could be made to error out if you try that though. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On 04/15/2015 04:27 PM, Niall Douglas wrote:
I'm finding Chris' paper on resumable expressions instead of compiler magic created resumable functions awfully compelling:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4453.pdf
I essentially proposed the same with my functionally composed constexpr basic_future design some months ago.
Regarding that, you may be interested in a similar effort, seatar [1], with very lightweight thread-unsafe futures and continuations (future<> size is 16 bytes) with multi-core utilization provided by explicit dispatching.
Would anyone else like to comment on this library based instead of compiler based resumable execution? I'm not against some compiler support for resumable execution, but I am finding the await based proposal highly problematic, and Chris does an excellent job in summarising my own thoughts on what's wrong with it.
[1] https://github.com/cloudius-systems/seastar, see core/future.hh
On 15 Apr 2015 at 22:26, Avi Kivity wrote:
On 04/15/2015 04:27 PM, Niall Douglas wrote:
I'm finding Chris' paper on resumable expressions instead of compiler magic created resumable functions awfully compelling:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4453.pdf
I essentially proposed the same with my functionally composed constexpr basic_future design some months ago.
Regarding that, you may be interested in a similar effort, seatar [1], with very lightweight thread-unsafe futures and continuations (future<> size is 16 bytes) with multi-core utilization provided by explicit dispatching.
Thanks for that. There has been a profusion of lightweight future object alternatives in recent months. I think yours is like the fourth I've seen now, each different but all similar in that they are much lighter weight than anything being proposed by the Concurrency TS. I fear, unfortunately, that WG21 is not as aware of these trends in real world use cases as they should be. Certainly the presently proposed continuable future is much fatter than the C++ 14 future, and I think that is not welcome. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On April 15, 2015 9:27:40 AM EDT, Niall Douglas
I'm finding Chris' paper on resumable expressions instead of compiler magic created resumable functions awfully compelling:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4453.pdf
I essentially proposed the same with my functionally composed constexpr basic_future design some months ago.
Would anyone else like to comment on this library based instead of compiler based resumable execution?
This is off-topic for this list. ___ Rob (Sent from my portable computation engine)
On 15 Apr 2015 at 15:40, Rob Stewart wrote:
I'm finding Chris' paper on resumable expressions instead of compiler magic created resumable functions awfully compelling:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4453.pdf
I essentially proposed the same with my functionally composed constexpr basic_future design some months ago.
Would anyone else like to comment on this library based instead of compiler based resumable execution?
This is off-topic for this list.
With respect to you as list moderator, I disagree. An already conditionally accepted library, Fiber, is fundamentally affected by what WG21 are up to here. An existing Boost library, ASIO, is fundamentally affected, hence its maintainer leading out the opposition to present developments. The Thread v5 rewrite is fundamentally affected, and shortly so will be AFIO which fair enough, is not a Boost library. Most WG21 papers aren't as important to Boost libraries, and hence I would ordinarily agree such discussion is off topic. The feedback already received here about resumable functions I have already found valuable - I hope so have many other library developers here. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On April 16, 2015 9:21:52 AM EDT, Niall Douglas
On 15 Apr 2015 at 15:40, Rob Stewart wrote:
This is off-topic for this list.
With respect to you as list moderator, I disagree.
An already conditionally accepted library, Fiber, is fundamentally affected by what WG21 are up to here. An existing Boost library, ASIO, is fundamentally affected, hence its maintainer leading out the opposition to present developments. The Thread v5 rewrite is fundamentally affected, and shortly so will be AFIO which fair enough, is not a Boost library.
Most WG21 papers aren't as important to Boost libraries, and hence I would ordinarily agree such discussion is off topic. The feedback already received here about resumable functions I have already found valuable - I hope so have many other library developers here.
There was no mention of any connection to Boost libraries. Now that you've created that connection, I agree with you. ___ Rob (Sent from my portable computation engine)
On 16 Apr 2015 at 14:05, Rob Stewart wrote:
An already conditionally accepted library, Fiber, is fundamentally affected by what WG21 are up to here. An existing Boost library, ASIO, is fundamentally affected, hence its maintainer leading out the opposition to present developments. The Thread v5 rewrite is fundamentally affected, and shortly so will be AFIO which fair enough, is not a Boost library.
There was no mention of any connection to Boost libraries. Now that you've created that connection, I agree with you.
Sorry. As I discovered in off list discussion about coroutines and resumable functions as well, the connections which are obvious to me need to be explicitly stated much more frequently. Especially as it would appear that the connections in my head are quite different to anyone else involved in the discussion. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
2015-04-15 10:27 GMT-03:00 Niall Douglas
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4453.pdf
revision 1 (p0114r0) is available: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0114r0.pdf -- Vinícius dos Santos Oliveira https://about.me/vinipsmaker
On 14/10/2015 08:32, Vinícius dos Santos Oliveira wrote:
2015-04-15 10:27 GMT-03:00 Niall Douglas
: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4453.pdf
revision 1 (p0114r0) is available: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0114r0.pdf
Let me just say up front that I'm definitely not an expert in this and I've only just recently read these and the await paper (N4286; not sure if there's a newer one). So please let me know if I've missed something. It bothers me a bit that it imposes limitations on use (given in 7.6, and presumably present so that the compiler can unambiguously know the required stack size of the call chain) that would render it nearly useless for practical purposes (since most code is not header-only templates, except in certain library contexts). It goes on to admit this in section 12 and offers some library-based solutions, but makes a point of stating that these aren't part of the proposal itself. There are some things I like about this -- namely that it potentially allows for different library implementations for different requirements, and theoretically even the potential for application writers to completely customise it for certain needs (eg. enforcing certain mutex types or memory patterns). But this is also a potential complication -- look at all the entertainment we're already having with interop between different pointer/future/thread/mutex types. I must admit to not being entirely happy with await (although most of my experience has been with C# code), due to some of the points mentioned in this paper (viral keywords, magic use of special library types, and duplication of implementation if desired to provide both async and non-async methods). However this proposal shares the latter limitation *unless* implemented as header-only templates (see 7.10 -- non-templates cannot be both resumable and non-resumable, even if header-only). And where async and non-async methods are required to be separate, then viral keywords are not a bad thing as it makes it more obvious where the potential suspension points are. But one advantage of await is that it does not have those limitations, and is intended to be used directly by application code with minimal library support.
15.10.2015 1:28, Gavin Lambert:
revision 1 (p0114r0) is available: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0114r0.pdf
Let me just say up front that I'm definitely not an expert in this and I've only just recently read these and the await paper (N4286; not sure if there's a newer one). So please let me know if I've missed something.
It bothers me a bit that it imposes limitations on use (given in 7.6, and presumably present so that the compiler can unambiguously know the required stack size of the call chain) that would render it nearly useless for practical purposes (since most code is not header-only templates, except in certain library contexts).
As I understand only asynchronous/resumable chain of calls should be inlined. I.e. if you want to call future<T> some_function(); which uses resumable stuff inside - you don't need to see it's body. If you want to compile each function of asynchronous/resumable chain separately - then you can do this by using "await" on each level, not only at the bottom of call stack. So this choice is up to user.
I must admit to not being entirely happy with await (although most of my experience has been with C# code), due to some of the points mentioned in this paper (viral keywords, magic use of special library types, and duplication of implementation if desired to provide both async and non-async methods). However this proposal shares the latter limitation *unless* implemented as header-only templates (see 7.10 -- non-templates cannot be both resumable and non-resumable, even if header-only).> And where async and non-async methods are required to be separate, then viral keywords are not a bad thing as it makes it more obvious where the potential suspension points are.
Viral keywords have another drawback - each function in asynchronous chain is stand-alone state machine. As I understand P0114R0 attempts to fuse several state machines into one, what results in less overhead.
On 10/13/15 12:32 PM, Vinícius dos Santos Oliveira wrote:
2015-04-15 10:27 GMT-03:00 Niall Douglas
: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4453.pdf
revision 1 (p0114r0) is available: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0114r0.pdf
I looked at this briefly and found it intriguing. But I have a couple of questions. Suppose I use something like Boost.Coroutine and declare the function which calls it as constexpr. Now this means that the constexpr stack is implemented at compile time and not at all at runtime. So how is not a "stackless" co-routine? How is it different than this proposal. Given this, why is this proposal even necessary? Robert Ramey
On Wed, Oct 14, 2015 at 11:42 PM, Robert Ramey
On 10/13/15 12:32 PM, Vinícius dos Santos Oliveira wrote:
2015-04-15 10:27 GMT-03:00 Niall Douglas
: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4453.pdf
revision 1 (p0114r0) is available: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0114r0.pdf
I looked at this briefly and found it intriguing. But I have a couple of questions.
Suppose I use something like Boost.Coroutine and declare the function which calls it as constexpr.
Now this means that the constexpr stack is implemented at compile time and not at all at runtime. So how is not a "stackless" co-routine? How is it different than this proposal. Given this, why is this proposal even necessary?
I'm not exactly sure what you are proposing with constexpr in this situation, but I think this will help. The problem is determining the memory needed for objects that must be "kept-alive" across resumption points. Stackful implementations allocate another stack of arbitrary size, whereas stackless implementations allocate memory for specific frame(s) that can be resumed. If the code is written like standard stack based code AND a stackless implementation is desired, compiler support is needed to determine which objects need to go into the allocated frame. I don't think its possible for a C++ function at compile-time to probe a function, even if constexpr, and ask "how large will its frame be?". However, it IS possible to write "stackless" code in C++03, but the programmer must ensure that objects needed across a resumption point are allocated in some other object (so it does NOT look like normal stack-based code). See ASIO stackless coroutines for examples. The stackless approaches should be faster since its simply doing an indirect function call + jumping to the current location within that function, instead of changing the entire runtime stack. Although I'm sure actual timings are a bit tricky with modern processors, as usual. Lee
On 15-04-2015 15:27, Niall Douglas wrote:
I'm finding Chris' paper on resumable expressions instead of compiler magic created resumable functions awfully compelling:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4453.pdf
I too find it compelling, though I'm not knowledgeable enough in this area. But I guess it will be hard to get traction if there are not proponents in WG21 willing to support the paper. From the last meeting, it seemed that one of the EWG comments was that they didn't see how it could be implemented. Maybe they didn't realize there's a lib emulation already, or perhaps there is a real problem somewhere. It does seem like no proponent of this paper was actually at the meeting when the paper was discussed. regards Thorsten
On 15-04-2015 15:27, Niall Douglas wrote:
I'm finding Chris' paper on resumable expressions instead of compiler magic created resumable functions awfully compelling:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4453.pdf
I too find it compelling, though I'm not knowledgeable enough in this area. But I guess it will be hard to get traction if there are not proponents in WG21 willing to support the paper. From the last meeting, it seemed that one of the EWG comments was that they didn't see how it could be implemented. Maybe they didn't realize there's a lib emulation already, or perhaps there is a real problem somewhere. It does seem like no proponent of this paper was actually at the meeting when the paper was discussed. regards Thorsten
participants (10)
-
Avi Kivity
-
Evgeny Panasyuk
-
Gavin Lambert
-
Lee Clagett
-
Niall Douglas
-
Rob Stewart
-
Robert Ramey
-
Thorsten Ottosen
-
TONGARI J
-
Vinícius dos Santos Oliveira