问题描述
我正在学习协程,并且遇到以下令人惊讶的行为(对我而言).我想要一张平行的地图.我考虑了4种解决方案:
I'm learning coroutines, and I encounter the following surprising (for me) behavior. I want to have a parallel map. I consider 4 solutions:
- 仅
map
,没有并行性 -
pmap
来自此处. - 第2项的修改:我删除了
coroutineScope
并使用了GlobalScope
. - Java的
parallelStream
.
- Just
map
, no parallelism pmap
from here.- Modification of item 2: I removed
coroutineScope
and useGlobalScope
. - Java's
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
的所有优点且速度增益相同的解决方案吗?
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?
推荐答案
范围仅间接涉及您观察到的差异.
Scopes are only indirectly involved in the differences you observed.
GlobalScope
是一个单例,定义了自己的调度程序,即Dispatchers.Default
.它由线程池支持.
GlobalScope
is a singleton that defines its own dispatcher, which is Dispatchers.Default
. It is backed by a thread pool.
coroutineScope
没有定义其自己的调度程序,因此您可以从调用方继承它,在本例中为runBlocking
创建的调度程序.它使用被调用的单线程.
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.
如果将coroutineScope
替换为withContext(Dispatchers.Default)
,您将获得相同的计时.实际上,这是您应该(而不是GlobalScope
)编写此代码的方式,以便在某些并发任务可能失败的情况下获得理智的行为.
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.
这篇关于Kotlin:coroutineScope比GlobalScope慢的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!