Kotlin: coroutineScope is slower than GlobalScope

不羁岁月 提交于 2020-06-27 12:51:48

问题


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?


回答1:


Scopes are only indirectly involved in the differences you observed.

GlobalScope is a singleton that defines its own dispatcher, which is Dispatchers.Default. It is backed by a thread pool.

coroutineScope does not define its own dispatcher so you inherit it from the caller, in this case the one created by runBlocking. It uses the single thread it is called on.

If you replace coroutineScope with withContext(Dispatchers.Default), you'll get the same timings. This is in fact how you should write this (instead of GlobalScope) in order to get sane behavior in the face of possible failures of some of the concurrent tasks.



来源:https://stackoverflow.com/questions/55531464/kotlin-coroutinescope-is-slower-than-globalscope

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!