Which of coroutines (goroutines and kotlin coroutines) are faster?

12,282

Coroutines in Kotlin are implemented in a different way than goroutines in Go, so which one is "faster" depends on the problem that you are solving and the kind of code you are writing.

In general, it is very hard to tell in advance which one is going to work better for a problem you have at hand. You have to run benchmarks for your particular workloads to figure it out. However, here is a general summary of key differences that should give you some guidance.

  • Kotlin coroutines require less memory per simple instance than Go goroutines. A simple coroutine in Kotlin occupies only a few dozen bytes of heap memory, while a Go goroutine starts with 4KiB of stack space. It means, that if you are planning to have literally millions of coroutines, then coroutines in Kotlin might give you an edge versus Go. It also makes Kotlin coroutines better suited for very short-lived and small tasks like generators and lazy sequences.

  • Kotlin coroutines can go to any stack depth, however each invocation of suspending function allocates object in heap for its stack. An invocation stack in Kotlin coroutines is currently implemented as a linked list of heap objects. In contrast, goroutines in Go use linear stack space. This makes suspension on deep stacks more efficient in Go. So, if the code you are writing suspends very deep down the stack, you may find that goroutines are more efficient for you.

  • Efficient asynchronous IO is a very multidimensional design problem. An approach that is efficient for one kind of application may not give the best performance to another one. All IO operations in Kotlin coroutines are implemented by libraries written in Kotlin or Java. There is a huge variety of IO libraries available to Kotlin code. In Go asynchronous IO is implemented by Go runtime using primitives that are not available to general Go code. If Go approach to implementing IO operations is well suited to your application, then you might find that its tight integration with Go runtime gives you an advantage. On the other side, in Kotlin you can find a library or write one yourself that implements asynchronous IO in a way that is best suited to your application.

  • Go runtime takes complete control of scheduling goroutines execution on the physical OS threads. The advantage of this approach is that you don't have to think about it all. With Kotlin coroutines you have fine-grained control on the execution environment of your coroutines. This is error-prone (e.g. you may simply create too many different thread-pools and waste your CPU time on context switching between them). However, it gives you ability to fine-tune your thread allocation and context switches for your application. For example, in Kotlin it is easy to execute your whole application or a subset of its code in a single OS thread (or thread pool) to completely avoid switching contexts between OS threads just by writing an appropriate code for that.

Share:
12,282

Related videos on Youtube

Max
Author by

Max

Updated on September 15, 2022

Comments

  • Max
    Max over 1 year

    Kotlin corutines is sugar for finite state machine and some task runner (for example, default ForkJoinPool). https://github.com/Kotlin/kotlin-coroutines/blob/master/kotlin-coroutines-informal.md#implementation-details

    In other words, there is no runtime coroutines in java/kotlin runtime yet (but this can change with http://cr.openjdk.java.net/~rpressler/loom/Loom-Proposal.html ). Kotlin coroutine is just sequential of tasks, which are executed one by one. Each task can be executed in any thread from thread pool.

    Go runtime supports "coroutines". But goroutines is not the real coroutines. Goroutines does not allow to set yield points in program. Also, Go does not allow to set custom thread pool. You can to set only size of threads in default pool.

    First difference between kotlin coroutines and goroutines is Go runtime manages which coroutine is running at this moment. When goroutine are blocked at some IO operation (or synchronization primitives), Go choices next Job to execute it. In JVM there is no intellectual job switching in such terms.

    Because of this, Go can cheaply change currently running job. Go has only to change few registries https://groups.google.com/forum/#!msg/golang-nuts/j51G7ieoKh4/wxNaKkFEfvcJ. But some people say, that JVM can use stack of threads instead of using registers. So there is no saving and loading of registers at all.

    The second difference between kotlin coroutines and goroutines is type of coroutines. Kotlin coroutines is stackless coroutines. Goroutines are stackful coroutines. All state of Kotlin coroutines are stored in Kotlin context, which is stored in heap. Goroutines state is stored in registers and thread stack.

    I want to know, which coroutines (goroutines and kotlin coroutines) are faster in IO bound tasks? CPU bound tasks? What about memory consumption?

    • Roman  Elizarov
      Roman Elizarov over 6 years
      The distinction between "stackful coroutines" and "stackless coroutines" is ill-defined and moot. See my JVMLS talk for details: youtube.com/watch?v=3xalVUY69Ok
  • kostix
    kostix over 6 years
    The «There is no way to say in code "run these goroutines on the same OS thread".» is not 100% true: the runtime.LockOSThread() calls locks the calling goroutine to the OS thread it's currently running on. This will ensure that the calling goroutine will always be scheduled on that same thread and no other goroutine will. OTOH, except for very rare cases, this is not needed, and in fact is often counter-productive as the Go scheduler tries very hard to ensure a goroutine removed from a thread is scheduled again on it, if possible.
  • kostix
    kostix over 6 years
    @Max, consider reading this classic essay on the difference between concurrency-as-a-library and concurrency-via-runtime-scheduler distinction (here: Java+anything vs Go).
  • Roman  Elizarov
    Roman Elizarov over 6 years
    @kostix Thanks. Corrected, by removing this section for accuracy.
  • Mangat Rai Modi
    Mangat Rai Modi almost 4 years
    @kostix I actually found runtime.LockOSThread() useful. I had a CPU bound task which I parallelized by goroutines. I actually needed threads, but we don't have that in Go. I found locking goroutine to be much faster.