When does a goroutine/coroutine switch to another coroutine/goroutine?

130 Views Asked by At

I have a program where I'm measuring how long it takes for a goroutine to complete an arbitrary task, in this case mergesort. I've created the exact same program (as far as i know) in both Kotlin and Go, to vaguely compare Goroutines and coroutines.

The Kotlin program finishes in about 15 seconds, the Go program in about a minute on my machine, as such i am expecting each goroutine to be about 4 times slower. However, according to Kotlin, each coroutine takes about 3 milliseconds. Each goroutine takes about 25 seconds, which us obviously much slower than 3 milliseconds. I'm thinking this might be due to Go switching goroutines, and as such each goroutine on average takes a very long time, as they're all being worked on in "parallel", regardless of how many cores my machine has. However in Kotlin, it seems that only as many coroutine as the machine has cores is being worked on simultaneously, and as such the average time for a coroutine elapsed time is much, much lower. Can anyone confirm? Both programs are sorting the same data.

If anyone is interested, the code is linked, minus the mergesort part, and the part which fetches data as that is not very relevant to the discussion.

I expected the coroutine and the goroutine to take about the same time.

1

There are 1 best solutions below

0
Augusto On

I know little of go, but in Kotlin a coroutines are suspended only when the coroutine does certain operations (e.g. call another suspend function, call dalay, etc). Otherwise the corroutine will run until it completes executing.

In the example you shared, each kotlin corroutine does the sort of the full array in one go, after it terminates, the next coroutine is loaded and the process repeats. This also makes it run faster as there is no mouting/unmounting of the coroutine context (I hope I recall the right terminology here!). The example uses the default dispatcher, which has as many threads as CPU cores (and it's the right one to use for CPU intensive operations). If you would use the IO dispatcher, it will run slower as kotlin will launch 64 threads, and those will be competing to get CPU time and use some of the CPU time to do context switching.

Doing a bit of research I found that the difference (in a way as mentioned above) is due to the scheduling.

  • Kotlin uses a cooperative scheduler, and the coroutine is responsible for suspending themselves.
  • Go uses a pre-emptive scheduler, so it can pause a coroutine at multiple points and schedule a different one.

As you mentioned, go launches all coroutines and they all progress together.