Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

No. Preemptive scheduling plus M:N mapping combination that Go has is not common in other major implementations.

Other languages and their implementations of green threads usually have cooperative scheduling or M:1 mapping

 help



C# and Rust (via Tokio) both have M:N threading. They both use a work-stealing algorithm to map many tasks onto a finite thread pool. But you're correct that they are cooperative via async/await, not pre-emptive.

I feel like you can't describe something as m:n when it the m uses async/await and doesn't actually have a green thread?

Define the "green thread"? By every definition I've seen[1], the task objects I mention are green threads.

[1]: https://en.wikipedia.org/wiki/Green_thread


Very curious how much Go wins by this. How worse would typical Go programs run with pre-emption disabled?

You can try this yourself: GODEBUG=asyncpreemptoff=1

Also platforms like Wasm still do Mx1 scheduling without async preemption, where Gosched is required at places.

E.g.: my "transpiled" SQLite driver takes special care to make sure long running SQL queries (and the busy handler) can be canceled with contexts even on platforms without async preemption.


I believe it’s more about robustness than performance in typical cases. Without pre-emption, there’s always a risk of one goroutine using disproportionate CPU time if it gets into an infinite (or just very long) loop without doing any IO.

I'm curious; using hardware threads is M logical threads preemptively scheduled on N physical cores. In what way does this not satisfy the original criteria?

They are still full OS threads: they have a full-size stack and have all the same overheads for context switching. Why would you think that a marketing term for a CPU feature is equivalent to an M:N scheduler?

A "hyperthread" can schedule work for two OS threads simultaneously on a single core. An M:N scheduler will schedule millions of green threads on as many cores/hardware threads as you give it (typically you'd give it all of them).


That's just not what is referred to as green threads.

Yes, in implementation they are not. I'm curious what the difference in subjective experience is.

Green threads don’t allocate stack frames and do not require such a massive context switch, which in turn allows for many more threads.

But if you happen to call a blocking syscall everything breaks.

That is not true of preemptive green threading systems. Go/Erlang/Haskell all run native foreign code on its own threads, and manage all internal I/O with async schedulers that never block. They also preempt user code in tight loops.

Those are the only mature languages that have all of these features.


Not sure if purposefully but you left out java. It doesn't preempt a CPU hot loop, but arguably you don't really want to disturb the CPU there / the whole "green thread" model doesn't make much sense with that kind of workload. That's why you have ordinary threads as well.

That's why higher level languages are in an advantage. E.g. in java - outside of FFI - syscalls are basically "only within" standard library functions. So it was possible to make them virtual thread aware, e.g. park a virt thread, use something like io uring for the actual IO call on the JVM level and resume it when it returns, doing work on another virt thread in the meanwhile.

Haskell had preemptive M:N green-threading before Go was invented.

I believe Go didn't originally have it, and added it in 2020, 14 years after Haskell.


Before that, Go did preempt on function calls. Haskell preempts on memory allocation. There is no idiomatic Haskell code that loops without allocating, but it is possible. Go code that loops without function calls is probably a lot more common but still avoidable.

I wasn't comparing Go to every language ever, just the ones people are most likely to pick. In that group Go (and Elixir) have more unique concurrency models.

Java's virtual threads are M:N.

>> Preemptive scheduling plus M:N mapping

AFAIK, in the current implementation, Java's virtual threads yields only when they block (cooperative). But the spec allows a JVM to implement them as preemptive.


Well, they do only yield on block and whatnot, but that's very much different from adding manual async/await keywords, I would say.



Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: