Колтин: coroutineScope работает медленнее, чем GlobalScope - PullRequest
3 голосов
/ 05 апреля 2019

Я изучаю сопрограммы и сталкиваюсь со следующим удивительным (для меня) поведением.Я хочу иметь параллельную карту.Я рассматриваю 4 решения:

  1. Просто map, нет параллелизма
  2. pmap от здесь .
  3. Модификация пункта 2:Я удалил coroutineScope и использую GlobalScope.
  4. Java parallelStream.

Код:

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()) }
}

Вывод (без сумм):

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

Как видите, с coroutineScope усиления почти нет, а с GlobalScope он работает так же быстро, как parallelStream.Какова причина?Могу ли я найти решение, которое имеет все преимущества coroutineScope с таким же приростом скорости?

1 Ответ

3 голосов
/ 05 апреля 2019

Сферы только косвенно участвуют в наблюдаемых вами различиях.

GlobalScope - это синглтон, который определяет свой собственный диспетчер, который равен Dispatchers.Default.Он поддерживается пулом потоков.

coroutineScope не определяет свой собственный диспетчер, поэтому вы наследуете его от вызывающего, в данном случае тот, который создан runBlocking.Он использует единственный поток, для которого он вызывается.

Если вы замените coroutineScope на withContext(Dispatchers.Default), вы получите те же тайминги.Фактически именно так следует написать это (вместо GlobalScope), чтобы получить разумное поведение перед лицом возможных сбоев некоторых из одновременных задач.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...