Live data from Hacker News

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

mropert.github.io

81–90 of 190 posts

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

#81
post #36
post #8

Earlier quoted context omitted.

Not that ancient, they just haven't bothered to update their coroutine mechanism to async/await. The Stride engine does it with their own scheduler, for example. Edit: Nevermind, they eventually bothered.

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.

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

#82
post #5

Always jarring to see how Unity is stuck on an ancient version of C#. The use of IEnumerable as a "generator" mechanic is quite a good hack though.

>The use of IEnumerable as a "generator" mechanic is quite a good hack though. Is that a hack? Is that not just exactly what IEnumerable and IEnumerator were built to do?

It feels hacky because you have to (had to?) use it as the async/await tool and because of that the types you're generating and how they are handled is a huge mess.

Really you're generating the vague concept of a yield instruction but you can return other coroutines that are implicitly run and nest your execution... Because of this you can't wait less than a frame so things are often needlessly complicated and slow.

It's like using a key to jam a door shut. Sure a key is for keeping doors closed but...

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

#83

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…

A much nicer code base to study is: https://swtch.com/libtask/

The stack save/restore happens in: https://swtch.com/libtask/asm.S

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

#84

Earlier quoted context omitted.

If you need to implement an async state machine, couldn't that just as easily be done with std::future? How do coroutines make this cleaner/better?

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 decide when to call it again? With the std::future approach where it's event driven by the promise being set when that state/step has completed.

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

#85

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.

The value is fewer indirect function calls heap allocations (so less overhead than callbacks) and well defined tasks that you can select/join/cancel.

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

#86
post #15

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…

> You can roll stackful coroutines in C++ (or C) with 50-ish lines of Assembly I'm not normally keen to "well actually" people with the C standard, but .. if you're writing in assembly, you're not writing in C. And the obvious consequence is that it stops being portable. Minicoro only supports three architectures. Granted, those are the three most popular ones, but other architectures exist. (just double checked and…

> Not that I'm expecting Microsoft to ship full conformance for C++23 any time soon,

They are actively working on it for their VS2026 C++ compiler. I think since 2017 or so they've kept up with C++ standards reasonably? I'm not a heavy C++ guy, so maybe I'm wrong, but my understanding is they match the standards.

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

#87
post #15

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…

> You can roll stackful coroutines in C++ (or C) with 50-ish lines of Assembly I'm not normally keen to "well actually" people with the C standard, but .. if you're writing in assembly, you're not writing in C. And the obvious consequence is that it stops being portable. Minicoro only supports three architectures. Granted, those are the three most popular ones, but other architectures exist. (just double checked and…

Looking at the repo, it falls back to Windows fibers on Windows/ARM. If you'd like a coroutine with more backends, I'm a fan of libco: https://github.com/higan-emu/libco/ which has assembly backends for x86, amd64, ppc, ppc-64, arm, and arm64 (and falls back to setjmp on POSIX platforms and fibers on Windows). Obviously the real solution would be for the C or C++ committees to add stackful coroutines to the standard, but unless that happens I would rather give up support for hppa or alpha or 8-bit AVR or whatever than not be able to use stackful corountines.

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

#88

Earlier quoted context omitted.

Just out of interest, how many serious unity devs have you talked to?

I've talked to some non-serious unity devs, like Peter Molyneux... https://news.ycombinator.com/item?id=47110605 >1h 48m 06s, with arms spread out like Jesus H Christ on a crucifix: "Because we can dynamically put on ANY surface of the cube ANY image we like. So THAT's how we're going to surprise the world, is by giving clues about what's in the middle later on." https://youtu.be/24AY4fJ66xA?t=6486 Click. Click. Clic…

Molyneux is obviously a well-known gamedev figure, but he's always been much more on the design side than programming side, as opposed to someone like Carmack or even J Blow. I wouldn't take his opinions on minutiae like coroutines as authoritative.

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

#89

Not an expert in game development, but I'd say the issue with C++ coroutines (and 'colored' async functions in general) is that the whole call stack must be written to support that. From a practical perspective, that must in turn be backed by a multithreaded event loop to be useful, which is very difficult to write performantly and correctly. Hence, most people end up using coroutines with something like boost::asio,…

> From a practical perspective, that must in turn be backed by a multithreaded event loop to be useful Multithreaded? Nope. You can do C++ coroutines just fine in a single-threaded context. Event loop? Only if you're wanting to do IO in your coroutines and not block other coroutines while waiting for that IO to finish. > most people end up using coroutines with something like boost::asio Sure. But you don't have to.…

I use asio at work for coroutine. It's one of the most opaque library I've ever used. The doc is awful and impenetrable.

The most helpful resource about it is a guy on stackoverflow (sehe). No idea how to get help once SO will have closed

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

#90
post #15

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…

> You can roll stackful coroutines in C++ (or C) with 50-ish lines of Assembly I'm not normally keen to "well actually" people with the C standard, but .. if you're writing in assembly, you're not writing in C. And the obvious consequence is that it stops being portable. Minicoro only supports three architectures. Granted, those are the three most popular ones, but other architectures exist. (just double checked and…

> I'm not normally keen to "well actually" people with the C standard, but .. if you're writing in assembly, you're not writing in C.

These days on Linux/BSD/Solaris/macOS you can use makecontext()/swapcontext() from ucontext.h and it will turn out roughly the same performance on important architectures as what everyone used to do with custom assembly. And you already have fiber functions as part of the Windows API to trampoline.

I had to support a number of architectures in libdex for Debian. This is GNOME code of course, which isn't everyone's cup of C. (It also supports BSDs/Linux/macOS/Solaris/Windows).

* https://packages.debian.org/sid/libdex-1-1

* https://gitlab.gnome.org/GNOME/libdex

Post reply on HN