英文:
When does a goroutine/coroutine switch to another coroutine/goroutine?
问题
我有一个程序,我正在测量goroutine完成任意任务(在这种情况下是归并排序)所需的时间。我已经在Kotlin和Go中创建了完全相同的程序(据我所知),以便粗略比较Goroutines和coroutines。
Kotlin程序在大约15秒内完成,Go程序在我的机器上大约需要1分钟,因此我预计每个goroutine要慢大约4倍。然而,根据Kotlin的说法,每个coroutine需要大约3毫秒。每个goroutine需要大约25秒,显然比3毫秒慢得多。
我认为这可能是由于Go切换goroutines,因此每个goroutine平均需要很长时间,因为它们都在“并行”工作,而不管我的机器有多少个核心。然而,在Kotlin中,似乎只有与机器核心数相同数量的coroutine同时工作,因此coroutine的平均耗时要低得多。有人可以确认吗?两个程序都在对相同的数据进行排序。
如果有人感兴趣,可以查看代码链接,不包括归并排序部分和获取数据的部分,因为这些与讨论不太相关。
我原以为coroutine和goroutine需要大致相同的时间。
英文:
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
得分: 1
我对Go了解不多,但在Kotlin中,协程只在协程执行某些操作时(例如调用另一个挂起函数、调用delay
等)才会被挂起。否则,协程将一直运行直到完成执行。
在你分享的示例中,每个Kotlin协程一次性对整个数组进行排序,然后在终止后加载下一个协程,进程重复。这也使得它运行得更快,因为没有协程上下文的挂载/卸载(我希望我在这里使用了正确的术语!)。该示例使用默认的调度器,它具有与CPU核心数量相同的线程(对于CPU密集型操作来说是正确的调度器)。如果使用IO调度器,它将运行得更慢,因为Kotlin将启动64个线程,并且这些线程将竞争获取CPU时间并使用一些CPU时间进行上下文切换。
通过一些研究,我发现差异(如上所述)是由调度方式造成的。
- Kotlin使用协作调度器,协程负责自己挂起。
- Go使用抢占式调度器,因此它可以在多个点暂停协程并调度其他协程。
正如你提到的,Go启动所有协程并且它们都同时进行。
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论