Kotlin:coroutineScope 比 GlobalScope 慢
Kotlin: coroutineScope is slower than GlobalScope
我正在学习协程,我遇到了以下令人惊讶的(对我来说)行为。我想要一张平行地图。我考虑了 4 种解决方案:
- 只是
map
,没有并行
pmap
来自 here.
- 第 2 项的修改:我删除了
coroutineScope
并使用 GlobalScope
。
- 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
的所有优点的解决方案吗?
范围仅间接涉及您观察到的差异。
GlobalScope
是一个单例,定义了自己的dispatcher,也就是Dispatchers.Default
。它由线程池支持。
coroutineScope
没有定义自己的调度程序,因此您可以从调用者那里继承它,在本例中是由 runBlocking
创建的调度程序。它使用调用它的单个线程。
如果将 coroutineScope
替换为 withContext(Dispatchers.Default)
,您将获得相同的计时。实际上,这就是您应该编写此代码(而不是GlobalScope
)的方式,以便在某些并发任务可能失败的情况下获得理智的行为。
我正在学习协程,我遇到了以下令人惊讶的(对我来说)行为。我想要一张平行地图。我考虑了 4 种解决方案:
- 只是
map
,没有并行 pmap
来自 here.- 第 2 项的修改:我删除了
coroutineScope
并使用GlobalScope
。 - 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
的所有优点的解决方案吗?
范围仅间接涉及您观察到的差异。
GlobalScope
是一个单例,定义了自己的dispatcher,也就是Dispatchers.Default
。它由线程池支持。
coroutineScope
没有定义自己的调度程序,因此您可以从调用者那里继承它,在本例中是由 runBlocking
创建的调度程序。它使用调用它的单个线程。
如果将 coroutineScope
替换为 withContext(Dispatchers.Default)
,您将获得相同的计时。实际上,这就是您应该编写此代码(而不是GlobalScope
)的方式,以便在某些并发任务可能失败的情况下获得理智的行为。