首页 > 解决方案 > Kotlin: coroutineScope is slower than GlobalScope

问题描述

I'm learning coroutines, and I encounter the following surprising (for me) behavior. I want to have a parallel map. I consider 4 solutions:

  1. Just map, no parallelism
  2. pmap from here.
  3. Modification of item 2: I removed coroutineScope and use GlobalScope.
  4. Java's parallelStream.

The code:

import kotlinx.coroutines.*
import kotlin.streams.toList
import kotlin.system.measureNanoTime

inline fun printTime(msg: String, f: () -> Unit) =
    println("${msg.padEnd(15)} time: ${measureNanoTime(f) / 1e9}")

suspend fun <T, U> List<T>.pmap(f: (T) -> U) = coroutineScope {
    map { async { f(it) } }.map { it.await() }
}

suspend fun <T, U> List<T>.pmapGlob(f: (T) -> U) =
    map { GlobalScope.async { f(it) } }.map { it.await() }


fun eval(i: Int) = (0 .. i).sumBy { it * it }

fun main() = runBlocking {
    val list = (0..200).map { it * it * it }
    printTime("No parallelism") { println(list.map(::eval).sum()) }
    printTime("CoroutineScope") { println(list.pmap(::eval).sum()) }
    printTime("GlobalScope") { println(list.pmapGlob(::eval).sum()) }
    printTime("ParallelStream") { println(list.parallelStream().map(::eval).toList().sum()) }
}

Output (without sums):

No parallelism  time: 0.85726849
CoroutineScope  time: 0.827426385
GlobalScope     time: 0.145788785
ParallelStream  time: 0.161423263

As you can see, with coroutineScope there is almost no gain, while with GlobalScope it works as fast as parallelStream. What is the reason? Can I have a solution which has all advantages of coroutineScope with the same speed gain?

标签: performancekotlinparallel-processingcoroutinekotlin-coroutines

解决方案


范围仅间接涉及您观察到的差异。

GlobalScope是一个单例,它定义了自己的调度程序,即Dispatchers.Default. 它由线程池支持。

coroutineScope没有定义它自己的调度器,所以你从调用者那里继承它,在这种情况下是由runBlocking. 它使用调用它的单个线程。

如果您替换coroutineScopewithContext(Dispatchers.Default),您将获得相同的时间。实际上,这就是您应该如何编写它(而不是GlobalScope),以便在面对某些并发任务可能失败时获得理智的行为。


推荐阅读