Live data from Hacker News

Looking at Unity made me understand the point of C++ coroutines

mropert.github.io

91–100 of 190 posts

Re: Looking at Unity made me understand the point of C++ coroutines

#91

Earlier quoted context omitted.

std::future doesn't give you a state machine. You get the building blocks you have to assemble into one manually. Coroutines give you the same building blocks but let the compiler do the assembly, making the suspension points visible in the source while hiding the mechanical boilerplate. This is why coroutine-based frameworks (e.g., C++20 coroutines with cppcoro) have largely superseded future-chaining for async stat…

It doesn't seem like a clear win to me. The only "assembly" required with std::future is creating the associated promise and using it to signal when that async step is done, and the upside is a nice readable linear flow, as well as ease of integration (just create a thread to run the state machine function if want multiple in parallel). With the coroutine approach using yield, doesn't that mean the caller needs to de…

You are describing a single async step, not a state machine. "Create a promise, set it when done", that's one state. A real async state machine has N states with transitions, branching, error handling, and cleanup between them.

> "The only 'assembly' required is creating the associated promise"

Again, that is only true for one step. For a state machine with N states you need explicit state enums or a long chain of .then() continuations. You also need to the manage the shared state across continuations (normally on the heap). You need to manage manual error propagation across each boundary and handle the cancellation tokens.

You only get a "A nice readable linear flow" using std:future when 1) using a blocking .get() on a thread, or 2) .then() chaining, which isn't "nice" by any means.

Lastly, you seem to be conflating a co_yield (generator, pull-based) with co_await (event-driven, push-based). With co_await, the coroutine is resumed by whoever completes the awaitable.

But what do I know... I only worked on implementing coroutines in cl.exe for 4 years. ;-)

Re: Looking at Unity made me understand the point of C++ coroutines

#92
post #36

Earlier quoted context omitted.

It's ancient. The latest version of Unity only partially supports C# 9. We're up to C# 14 now. But that's just the language version. The Mono runtime is only equivalent to .NET Framework 4.8 so all of the standard library improvements since .NET (Core) are missing. Not directly related to age but it's performance is also significantly worse than .NET. And Unity's garbage collector is worse than the default one in Mon…

The runtime is absolutely ancient, but I think the version number says more about C#'s churn than about how outdated the language version is. Take my opinion on C# with a grain of salt, though, I was an F#-er until the increasing interop pains forced me to drop it.

There were also a lot of performance improvements to .NET over the last few years.

Re: Looking at Unity made me understand the point of C++ coroutines

#93
post #9

Simon Tatham, author of Putty, has quite a detailed blog post [0] on using the C++20's coroutine system. And yep, it's a lot to do on your own, C++26 really ought to give us some pre-built templates/patterns/scaffolds. [0] https://web.archive.org/web/20260105235513/https://www.chiar...

People love to complain about Rust async-await being too complicated, but somehow C++ manages to be even worse. C++ never disappoints!

Re: Looking at Unity made me understand the point of C++ coroutines

#95
I do not find so called "green threads" useful at all. In my opinion except some very esoteric cases they serve no purpose in "native" languages that have full access to all OS threading and IO facilities. Useful only in "deficient" environments like inherently single threaded request handlers like NodeJS.

Re: Looking at Unity made me understand the point of C++ coroutines

#96

You can roll stackful coroutines in C++ (or C) with 50-ish lines of Assembly. It's a matter of saving a few registers and switching the stack pointer, minicoro [1] is a pretty good C library that does it. I like this model a lot more than C++20 coroutines: 1. C++20 coros are stackless, in the general case every async "function call" heap allocates. 2. If you do your own stackful coroutines, every function can suspend…

As an x-gamedev, suspect/resume/stackful coroutines made them too heavy to have several thousand of them running during a game loop for our game. At the time we used GameMonkey Script: https://github.com/publicrepo/gmscript

That was over 20 years ago. No idea what the current hotness is.

Re: Looking at Unity made me understand the point of C++ coroutines

#97
post #73
post #63

Coroutines generally imply some sort of magic to me. I would just go straight to tbb and concurrent_unordered_map! The challenge of parallelism does not come from how to make things parallel, but how you share memory: How you avoid cache misses, make sure threads don't trample each other and design the higher level abstraction so that all layers can benefit from the performance without suffering turnaround problems.…

> C# (already has it but is terrible to write native/VM code for?) What do you mean here? Do you mean hand-writing MSIL or native interop (pinvoke) or something else?

No I meant this but for C# is a whole lot more complex:

http://move.rupy.se/file/jvm.txt

Re: Looking at Unity made me understand the point of C++ coroutines

#98

Coroutines is just a way to write continuations in an imperative style and with more overhead. I never understood the value. Just use lambdas/callbacks.

I'll take the bait. Here's a coroutine

    waitFrames(5); // wait 5 frames
    fireProjectile();
    waitFrames(15);
    turnLeft(-30/*deg*/, 120); // turn left over 120 frames
    waitFrames(10);
    fireProjectile();
    // spin and shoot
    for (i of range(0, 360, 60)) {
      turnRight(60, 90);  // turn 60 degrees over 90 frames
      fireProjectile();
    }
10 lines and I get behavior over time. What would your non-coroutine solution look like?

Re: Looking at Unity made me understand the point of C++ coroutines

#99
post #9

Simon Tatham, author of Putty, has quite a detailed blog post [0] on using the C++20's coroutine system. And yep, it's a lot to do on your own, C++26 really ought to give us some pre-built templates/patterns/scaffolds. [0] https://web.archive.org/web/20260105235513/https://www.chiar...

See also C++ coroutines resources (posts, research, software, talks): https://gist.github.com/MattPD/9b55db49537a90545a90447392ad3...

Re: Looking at Unity made me understand the point of C++ coroutines

#100

You can roll stackful coroutines in C++ (or C) with 50-ish lines of Assembly. It's a matter of saving a few registers and switching the stack pointer, minicoro [1] is a pretty good C library that does it. I like this model a lot more than C++20 coroutines: 1. C++20 coros are stackless, in the general case every async "function call" heap allocates. 2. If you do your own stackful coroutines, every function can suspend…

> every async "function call" heap allocates.

> require the STL

That it has to heap-allocate if non-inlined is a misconception. This is only the default behavior.

One can define:

void *operator new(size_t sz, Foo &foo)

in the coro's promise type, and this:

- removes the implicitly-defined operator new

- forces the coro's signature to be CoroType f(Foo &foo), and forwards arguments to the "operator new" one defined

Therefore, it's pretty trivial to support coroutines even when heap cannot be used, especially in the non-recursive case.

Yes, green threads ("stackful coroutines") are more straightforward to use, however:

- they can't be arbitrarily destroyed when suspended (this would require stack unwinding support and/or active support from the green thread runtime)

- they are very ABI dependent. Among the "few registers" one has to save FPU registers. Which, in the case of older Arm architectures, and codegen options similar to -mgeneral-regs-only (for code that runs "below" userspace). Said FPU registers also take a lot of space in the stack frame, too

Really, stackless coros are just FSM generators (which is obvious if one looks at disasm)

Post reply on HN