3 ms·
> Goroutines are stackful, whereas in C++ you'll need to manually chain coroutines. That means a dynamic allocation and deallocation for _each_ call frame in th
by lossolo 10y ago
> Goroutines are stackful, whereas in C++ you'll need to manually chain coroutines. That means a dynamic allocation and deallocation for _each_ call frame in the chain.
You can elide that heap allocation.
Coroutines proposed for C++ are like functions, when you call function inside goroutine you also allocate function frame, as you allocate frame for coroutine.
Every gorutine has it's own stack which means you also allocate minimum of 2 KB for every gorutine. When stack grows over 2 KB you need to copy + reallocate in Go.
Coroutines are like functions, easily optimized by compilers which Gor showed. You can get coroutines inlined and state machines are not so easily optimized.
> At the end of the day, without stackful coroutines networking still won't be as elegant as in Go. And for maximum performance, state machines (e.g. using computed gotos) will still be useful. You'll either need to sacrifice code clarity and composition by explicitly minimizing chains, or you'll sacrifice performance by having deep coroutine chains.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0055r0.html http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p005...
EDIT: Citation from Gor discussing this in more detail:
"In an ordinary function, locals are stored in the registers and in the activation frame on the stack. Coroutines, in addition to the activation frame/registers, have a separate coroutine frame that stores things that need to persist across suspends. Activation frame is created every time coroutine is entered and torn down when it is suspended or completed. It looks as a normal function call and return. Coroutine frame is created the first time you activate a particular coroutine instance and persists across suspend until a control flow reaches the end of the function or hits a return statement.
In general, a call to a coroutine (with suspend points) is more expensive than a function call, since we need to allocate both the activation frame (sub rsp,X) and a coroutine frame (operator new). When the coroutine lifetime is fully enclosed in the lifetime of the caller we simply put the coroutine state as a temporary on the frame of the caller, thus avoiding heap allocation.
To summarize:
suspend/resume = cost of the function call
coroutine call/return >= const of the function call
"
- wahern 10y agoCoroutines proposed for C++ are indeed, "like functions". But they're not functions. It matters. While the proposal will allow more optimization than the existing alternatives, they won't allow all the same optimizations possible as for stackful coroutines. For example, you can't inline a non-stackful coroutine that has independent state. But a stackful coroutines could be inlined. If all you care about is using coroutines as an interface to an async API, then it's not a big deal. But that avoids questions of how and at what cost you could make use of coroutines to implement the API. (You know, the _hard_ part!) And without experience using coroutines in languages like Lua, you can't even _imagine_ all the possible programming patterns you're missing out on. As a general matter I think it's really short-sighted to add a language abstraction and only use as your frame of reference one niche usage pattern--namely, async completion. As an abstraction coroutines are _much_ more general than that, but they're unlikely to be used in those ways if they're not transparent--that is, if they're a leaky abstraction that constantly forces you to keep in mind various tradeoffs. I don't see why the reality needs to be controversial. Stackful coroutines are just more powerful. Full stop. But that doesn't mean non-stackful are useless or shouldn't be added to C++. Just don't expect to see the same dividends that you could theoretically get from stackful coroutines, and definitely not the same dividends as from Goroutines. Don't forget that Go has first-class functions with lexical binding and garbage collection. All of those language features compound the benefits of stackful coroutines.
- lossolo 10y agoI am not saying stackful coroutines are bad, I'am using Go on daily basis, I've used libmill stackfull coroutines in C etc. They have their usage cases in which they will be better than stackless coroutines, there are tradeoffs in both designs of coroutines. Stackless coroutines for my use case they are ideal (async IO) and can be easily optimized. I don't agree with what you wrote about using coroutines (and similar concepts) for async completion is niche. In most code bases I saw coroutines/tasks/gorutines etc are used mainly for async IO.
- gpderetta 10y ago> For example, you can't inline a non-stackful coroutine that has independent state I'm a huge fan of stackful coroutines (having written multiple variants myself) and I have been lobbying hard for them. Having said that, stackless coroutines, as long as the next continuation is statically known or knowable, are trivially inlineable (they desugar to the equivalent of a switch statement).