Understanding Kotlin's coroutines performance

#1

Hi, everybody, I’m kind of new in a Kotlin, but I’ve got a few questions already. Fortunately, about a single topic)

Somewhere in the Kotlin’s documentation, developers have mentioned the fact, that coroutines are lightweight. But how “lightweight” they actually are in comparison to, for example, Elixir processes? Is it okay to spawn thousands of coroutines in Kotlin to develop in “fail fast” manner? How fast they are spawning in comparison with threads?

#2

Hi there,

I’m not an elixir user, nor a coroutine expert but i’m gonna give it a shot.

Coroutines are a simple compiler trick, you transform a method containing suspension points into a huge switch case where each case is the code between two suspension points. There is a whole bunch of machinery above that to correctly pass contexts around but you get the idea.

This allows us to create thousands of coroutines whithout problem, their cost is a few bytes of memory for the internal objects they allocate. If you are interested I suggest you take a look at what the Kotlin/JS compiler produces when dealing with coroutines, it’s really interesting and it helps you understand the underlying machinery a bit better.

Threads are what coroutines are dispatched to, they are simple workers that execute code until reaching the next suspension point. This architecture is light in term of memory, and in term of scheduling for the OS, you can execute a lot of coroutines on the same threads easily, allowing you to decrease the number of threads your app needs, reducing the number of context-switch.

Another good thing with coroutines is that it is way easier to correctly do concurrency (with things like the actor paradigm

The main cost of coroutines in my experience is dispatching latency and garbage collection pressure. As they are made of objects, suspension can become costly if you do a lot of them. In my case it was a networking library doing thousands of suspension per second, leading to a bit of garbage collection pressure and a bit of dispatching latency (nothing catastrophic but still).

To come back to your initial question, yes coroutines spawn way faster than threads, but if your worker pool is busy, you can end up with a bit of dispatching latency. I can’t compare with elixir processes as I don’t know them, but if they are actual processes, there is a good chance coroutines spawn faster too.

For me coroutines are light, scalable, fast, and I can’t recommend them enough :slight_smile:

2 Likes
#3

Thank you for such a quick and detailed answer! All things much clearer to me now about coroutines and threads!

Going back to the initial question and your answer, I figured out, that coroutines are somehow close to the Elixir’s processes in terms of memory/CPU cost and not the only. Therefore, I need to say, that Elixir’s processes are not the same as OS processes - they are both products of a VM (Erlang one).

1 Like
#4

A lot of everything “can become costly”, but a lot of threads can become unacceptable.
The coroutine requires less cpu and memory than a thread, like a thread is cheaper than a process.

#5

True enough