我有一个ExecutorService
代码片段,我正试图将其转换为协程以测试性能。然而,无论我如何构建协同程序代码,ExecutorService
都要快得多。我认为协同程序应该提高的性能
功能:
- 在后台线程中运行
- 执行200000个操作(计数器++(
- 传递给UI线程的发布时间
- 代码在ViewModel中运行,更新时间文本视图
- 执行器服务代码在模拟器上花费约150毫秒
- 我写的任何协同程序代码都需要更长的时间
什么是等价于以下代码的协同程序:
fun runCodeExecutorService() {
spinner.value = true
val executorService = Executors.newFixedThreadPool(NUMBER_OF_CORES * 2)
val result = AtomicInteger()
val startTime = System.currentTimeMillis()
val handler: Handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(inputMessage: Message) {
time.value = toTime(System.currentTimeMillis() - startTime)
spinner.value = false
Log.d("tag", "counter Executor = " + result.get())
}
}
thread(start = true) {
for (i in 1..NUMBER_OF_THREADS) {
executorService.execute {
result.getAndIncrement()
}
}
executorService.shutdown();
executorService.awaitTermination(2, TimeUnit.MINUTES)
val msg: Message = handler.obtainMessage()
val bundle = Bundle()
bundle.putInt("MSG_KEY", result.get())
msg.data = bundle
handler.sendMessage(msg)
}
}
其中NUMBER_OF_CORES是val NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors()
NUMBER_OF_THREADS为200000
@George实际上您的代码正在阻塞,实际的代码可能看起来像这样:
fun runCodeCoroutines() = viewModelScope.launch {
spinner.value = true
val result = AtomicInteger()
val startTime = System.currentTimeMillis()
withContext(Dispatchers.Default) {
result.aLotOfCoroutines()
}
// 3
time.value = toTime(System.currentTimeMillis() - startTime)
spinner.value = false
Log.d("tag", "counter Dispatchers.Default = " + result.get())
}
suspend fun AtomicInteger.aLotOfCoroutines() {
coroutineScope {
repeat(NUMBER_OF_THREADS) {
launch(Dispatchers.Default) { // 1
getAndIncrement()
}
}
} // 2
}
其中aLotOfCoroutines是您的代码这大约是我得到的结果。
基准:协同程序代码~1.2秒执行程序代码~150毫秒
还有另一个版本的代码,我将线程数分解为200*1000
suspend fun massiveRun(action: suspend () -> Unit) {
coroutineScope { // scope for coroutines
repeat(NUMBER_OF_TASKS) {
launch {
repeat(NUMBER_OF_ACTIONS) { action() }
}
}
}
}
大约需要35-40毫秒然而,Executor服务中的相同故障需要大约25-35毫秒的
更接近但总体上更好的
我的结论是,当考虑性能时,Executitor Service仍然比Coroutines更具性能
协同程序只在语法上更好,并且当精确性能不重要时(即网络调用等(
一个粗略的等价物是调度器。
suspend fun aLotOfCoroutines() {
spinner.value = true
val result = AtomicInteger()
val startTime = System.currentTimeMillis()
coroutineScope {
repeat(NUMBER_OF_THREADS) {
launch(Dispatchers.Default) { // 1
result.getAndIncrement()
}
}
} // 2
// 3
time.value = toTime(System.currentTimeMillis() - startTime)
spinner.value = false
Log.d("tag", "counter Dispatchers.Default = " + result.get())
}
我们可以将
Dispatchers.Default
用于非阻塞任务,而不是创建和停止新的执行器。对于结构化并发,
coroutineScope
在其所有子协同程序完成之前不会返回。这就是为什么我们不需要明确地等待完成。因为这个方法是在
Dispatchers.Main
中调用的,所以这些行也将在主线程中运行。
如果您真的想使用自定义线程池,可以在执行器上使用asCoroutineDispatcher
扩展方法。
在知道OP对性能数据感兴趣后,我做了更多的调查。
在这个";"测试":
- 这个任务很便宜。因此,每个任务的任何开销都非常明显
- 此任务作用于竞争激烈的资源,因此并行化速度更快而不是。单线程运行耗时1毫秒
我认为可以公平地说,这不像任何实际的工作量。
无论如何,下面的工作池与线程池实现了类似的时间。
coroutineScope {
val channel = produce(capacity = 64) {
repeat(JOB_COUNT) { send(Unit) }
}
// The fewer workers we launch, the faster it runs
repeat(Runtime.getRuntime().availableProcessors() * 2) {
launch {
for (task in channel) {
result.getAndIncrement()
}
}
}
}