Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why kotlin coroutines are considered light weight?

If coroutines is still using threads to run code in parallel why it is considered light weight?

In my understanding kotlin's suspend functions is transformed by compiler into state machine where each branch can be run on the same or different thread defined by developer. Coroutine builder, e,g, launch{}, responsible for that and CoroutineContext is what defines a thread to run on.

Parallelism achieved by sending block of code to the thread pool which leverage the same threads

There was a benchmark on 100k coroutines and 100k threads where coroutines pass without issue and threads throw exception (likely OutOfMemory). It brings me to idea I am missing something here.

Could you help me to understand what is missed here what makes coroutines run code block 100k in parallel without exceeding memory limits like threads do?

like image 560
Joe Dow Avatar asked Sep 03 '20 08:09

Joe Dow


People also ask

Why are Kotlin coroutines lightweight?

Every Thread has its own stack, typically 1MB. 64k is the least amount of stack space allowed per thread in the JVM while a simple coroutine in Kotlin occupies only a few dozen bytes of heap memory. The Coroutine Dispatcher has a limit that only a certain amount of threads can be created.

What are Kotlin coroutines good for?

Coroutines were added to Kotlin in version 1.3 and are based on established concepts from other languages. On Android, coroutines help to manage long-running tasks that might otherwise block the main thread and cause your app to become unresponsive.

Why coroutines are better than threads?

The advantages of coroutines over threads are that they may be used in a hard-realtime context (switching between coroutines need not involve any system calls or any blocking calls whatsoever), there is no need for synchronization primitives such as mutexes, semaphores, etc.

Why coroutines are fast?

By default, tasks are run with threads, which is usually going to be sufficient for your everyday program. They tend to slow down when there are a lot of tasks being run, which is why using coroutines help to speed things up. Threads run tasks in serial, while coroutines runs many tasks at the same time.


1 Answers

Pointing from the article

Every Thread has its own stack, typically 1MB. 64k is the least amount of stack space allowed per thread in the JVM while a simple coroutine in Kotlin occupies only a few dozen bytes of heap memory.

The Coroutine Dispatcher has a limit that only a certain amount of threads can be created.

Such as Dispatchers.IO has limit of 64 threads, Dispatchers.Default has limit of number of cores on your processor (2, 4, 6, 8, etc.) Dispatchers.Unconfined cannot create new thread it runs on threads previously created by other dispatchers, here's proof: 500 operations having 10ms of sleep takes approx 5s (single-thread because it can't spawn a new) try it yourself.

Coroutines stick to a thread, and as soon as suspension point is reached, it leaves the Thread and frees it up letting it to pick up another coroutine if it is waiting. This way with less threads and less memory usage, that much concurrent work can be done.

The coroutines are managed to be suspended and resumed by a callback like object Continuation which is added as the last parameter to the function marked with suspend keyword at the time of compilation which lives in heap as other objects do and is responsible for the resume of coroutine, so Thousands of MBs space is not required in RAM to keep all the Threads alive. A typical 60-70 threads are created at max using CommonPool and are reused (if new coroutine is created it waits till another finishes).

like image 155
Animesh Sahu Avatar answered Oct 02 '22 06:10

Animesh Sahu