Back to all articles

The Gophers will win the future

I read this article Async runtimes benchmarks 2024, I immediately thought it was a of a waste of time. Java and go use green threads and everything else was async/await, this is very simply apples and oranges. green threads are a fundamentally different model to async/await.

erlang figured this out 30 years ago. green threads + mailboxes are the correct 'doing multiple things at the same time' abstraction.

green threads, as used by erlang, elixir, go, and now in (new) Java as well are the perfect abstraction and I will explain why.

cpus are moving towards more cores rather than faster cores

As many manufacturers start to offer ARM cpus for their servers, we are noticing a shift in the market.

64-96-128 core servers are becoming more common. They have similar single thread performance to older x86 intel cores (not quite the speed of modern AMD cpus) something that is becoming more clear is that we are moving towards more cores rather than faster cores.

CPU speeds, particularly in the datacentre have leveled off. Improved single thread performance is only increasing at a fraction of the rate it used to. The real improvements are coming from how many more cores we can fit on a server.

async/await is a bad abstraction

REF: Async/await is real and can hurt you

async/await is great until you have to do something blocking. Inevitability you will do something blocking, parsing JSON, validating an object, rendering an email... and so on. Something that many JS devs never seem to consider is that the recent move towards react server components now means they're running considerably more blocking JS on their server, its very expensive cpu bound work!

I also find the cognitive overhead of async/await to be very high. If you're writing something performance sensitive (which is most things) you're going to be writing a lot of blocking code, you will constantly be thinking about the fact that this will block the main thread and slow down application performance... With go, write a goroutine and you're golden.

OS/Platform threads are great because your platform will spread them across your available CPU cores, the question is what happens when we need to do a million things at the same time? is that completely out of the question that some apps would have to do millions of mixed blocking/non-blocking tasks concurrently?

It's not possible to launch millions of OS/Platform threads, your machine will fail.

elastic/plastic scalability

in material design, there is a concept of elastic/plastic deformation.

Bend a material within the elastic range, it will return to its original shape. Bend it further and it enters into the plastic range, it will not return to its original shape.

Async/await is exactly the same. Your go app will get slower and slower but will only fail spectacularly very far down the line. Async/await runtimes scale elastically, then they very quickly become plastic.

cpu bound work?

"but if you run CPU bound work on a green thread you're going to run into context switching overhead...?" platform threads also have context switching overhead, but they are much more expensive. userspace/green threads are much cheaper. Obviously, you're going to run into your applications overhead as well as your platforms overhead, but assuming your applications runtime is tuned correctly, it should only allocate as many threads as CPUs exist on your machine.

as much as I like the beam ecosystem, I don't believe functional programming can attract enough programmers to make it pick up attention, universities are still teaching java oop 101 classes, not monads are like burritos classes, this will not change. Not to mention that elixir/erlang kind of sucks for cpu bound work.

go ONLY has green threads?

goroutines are green threads. you can't do async/await or launch platform threads (easily) in golang. no-one argues that go is bad for cpu-bound work or mass amounts of networked/async requests. it does both very well. Many people seem to want to argue that there is a right tool for the job, sure. But, most server tasks would benefit by being written in go.

what about thread pools

thread pools are great if you want to most efficiently complete work thats fine to be queued, low-priority background jobs and so on. What happens when you actually need to do work concurrently?