You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin协程Dispatchers性能疑问:IO与Default对比及异常结果解析

Kotlin协程调度器CPU密集型任务基准测试分析

我针对Kotlin协程的Dispatchers.Default和Dispatchers.IO调度器处理CPU密集型任务(计算阶乘)做了基准测试,同时对比了原生线程的表现。

初始测试代码

@State(Scope.Benchmark)
class DispatchersCPUBenchMark {
    @Param("2000", "4000")
    var factorialSize = 0
    var nCoroutines = 4000

    @Benchmark
    fun coroutinesDispatchersIO() = run {
        repeat(nCoroutines) {
            GlobalScope.launch(Dispatchers.IO) {
                factorial(factorialSize)
            }
        }
    }

    @Benchmark
    fun coroutinesDispatchersDefault() = run {
        repeat(nCoroutines) {
            GlobalScope.launch(Dispatchers.Default) {
                factorial(factorialSize)
            }
        }
    }

    @Benchmark
    fun threads() {
        val threads = List(nCoroutines) {
            thread(start = true) {
                factorial(factorialSize)
            }
        }
        threads.forEach { it.join() }
    }

    val factorial = fun(n: Int): BigInteger? {
        var result = BigInteger.ONE
        for (i in 2..n) result = result.multiply(BigInteger.valueOf(i.toLong()))
        return result
    }
}

初始测试结果

main summary:
Benchmark                                             (factorialSize)  Mode  Cnt        Score        Error  Units
DispatchersCPUBenchMark.coroutinesDispatchersDefault             2000  avgt    3     7074.923 ±  82728.394  us/op
DispatchersCPUBenchMark.coroutinesDispatchersDefault             4000  avgt    3     5356.445 ±  52996.999  us/op
DispatchersCPUBenchMark.coroutinesDispatchersIO                  2000  avgt    3     5989.716 ±  22087.285  us/op
DispatchersCPUBenchMark.coroutinesDispatchersIO                  4000  avgt    3     7330.124 ±  37986.010  us/op
DispatchersCPUBenchMark.threads                                  2000  avgt    3  1655875.859 ± 285008.082  us/op
DispatchersCPUBenchMark.threads                                  4000  avgt    3  5904109.034 ± 312283.563  us/op

疑问解答

  1. 为什么Dispatchers.Default下factorialSize=4000的耗时反而更短?
    初始测试的核心问题是没有等待协程执行完成,仅测试了协程的启动开销。不管计算任务的大小,协程启动的逻辑是一致的,而结果中巨大的误差范围(比如Default 2000的误差超过8万微秒)也说明数据可信度很低,这种反直觉的差异完全是误差或者JVM即时编译优化导致的偶然结果,不能反映实际任务的执行耗时。

  2. 为什么Dispatchers.IO的耗时比Dispatchers.Default更短?
    同样因为初始测试没有等待协程完成,测的只是协程启动的开销。Dispatchers.IO和Default的协程启动开销本身差异极小,加上测试误差范围大,才出现了IO耗时更短的假象。这并不能说明IO调度器更适合CPU密集任务。

优化后测试结果

使用Blackhole保留计算结果(避免JVM优化掉无意义的计算)、runBlocking等待所有协程完成、async/await管理协程生命周期后,得到如下结果:

main summary:
Benchmark                                             (factorialSize)  Mode  Cnt         Score         Error  Units
DispatchersCPUBenchMark.coroutinesDispatchersDefault             2000  avgt    3   2526096.650 ±  111881.923  us/op
DispatchersCPUBenchMark.coroutinesDispatchersDefault             4000  avgt    3  12238701.967 ± 2404781.746  us/op
DispatchersCPUBenchMark.coroutinesDispatchersIO                  2000  avgt    3   2450406.747 ±  248896.953  us/op
DispatchersCPUBenchMark.coroutinesDispatchersIO                  4000  avgt    3  12465975.900 ± 6017831.426  us/op
DispatchersCPUBenchMark.threads                                  2000  avgt    3   1652601.100 ±  226224.137  us/op
DispatchersCPUBenchMark.threads                                  4000  avgt    3   5877652.850 ±  689588.189  us/op

优化后结果分析

优化后的结果符合预期:任务规模越大,总耗时越长。对于Dispatchers.Default和IO的差异:

  • Default调度器的线程数默认等于CPU核心数,专门针对CPU密集任务优化,避免过多线程导致的上下文切换开销;IO调度器的线程池默认可扩容至64个线程,当运行大量CPU密集任务时,过多的线程会增加上下文切换的开销,理论上Default应该更高效。
  • 测试中factorialSize=4000时IO仅比Default多0.2秒左右,且IO的误差范围极大(超过6秒),说明这个差异可能在误差范围内。结合CPU密集任务的特性,这个结果是符合预期的——两者的差异主要来自调度器的线程管理开销,而在纯CPU计算的任务中,这个开销占总耗时的比例很小,所以差异不明显。

内容的提问来源于stack exchange,提问作者Dzmitry Kashlach

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 19:25:06