什么是相当于Executor服务的Android协同程序



我有一个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())
}
  1. 我们可以将Dispatchers.Default用于非阻塞任务,而不是创建和停止新的执行器。

  2. 对于结构化并发,coroutineScope在其所有子协同程序完成之前不会返回。这就是为什么我们不需要明确地等待完成。

  3. 因为这个方法是在Dispatchers.Main中调用的,所以这些行也将在主线程中运行。


如果您真的想使用自定义线程池,可以在执行器上使用asCoroutineDispatcher扩展方法。


在知道OP对性能数据感兴趣后,我做了更多的调查。

在这个";"测试":

  1. 这个任务很便宜。因此,每个任务的任何开销都非常明显
  2. 此任务作用于竞争激烈的资源,因此并行化速度更快而不是。单线程运行耗时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()
}
}
}
}

最新更新