为何初始基准测试中ExecutorService比协程快?[已解决]
更新
我犯了两个低级错误!
- 在ExecutorService示例中仅提交了1个任务
- 忘记等待任务完成
修正测试后,三个示例的延迟均约为190-200 ms/op。
基准测试说明
我使用kotlinx-benchmark(基于jmh)创建基准测试,对比协程与线程池处理阻塞调用的性能。
做此测试的理由:
- 协程执行阻塞调用时会阻塞底层线程
- 网络调用通常是阻塞的
- 常规服务中需要处理百万级网络调用
- 这种场景下使用协程能否获得收益?
测试场景与结果
我通过Thread.sleep(10) // 模拟10ms阻塞调用创建1000个调用,设置三个测试案例,结果如下:
Dispatchers.io
采用官方推荐的IO操作处理方式Dispatchers.IO:
@Benchmark fun withCoroutines() { runBlocking { val coroutines = (0 until 1000).map { CoroutineScope(Dispatchers.IO).async { sleep(10) } } coroutines.joinAll() } }
平均耗时:188.418 ms/op
固定线程池
Dispatcher.IO默认会创建64个线程(静态环境下数量不确定),因此我设置60个线程保证场景可比性:
@Benchmark fun withExecutorService() { val executors = Executors.newFixedThreadPool(60) executors.submit { sleep(10) } executors.shutdown() }
平均耗时:0.054 ms/op
线程池调度器
由于结果差异过大,我将上述线程池转换为协程调度器使用:
Executors.newFixedThreadPool(60).asCoroutineDispatcher()
平均耗时:206,260 ms/op
疑问
- 为何协程在此测试中表现异常糟糕?
- 使用
limitedParallelism(10)选项后,协程性能提升至30ms/op。IO调度器默认线程数为64,是否说明协程调度器因过多上下文切换导致性能下降?但与线程池的性能差距仍较大。 - 我假设网络调用始终是阻塞的是否正确?ExecutorService与协程均通过底层线程调度执行,且不阻塞主线程,二者是否为直接竞品?
测试配置
运行jmh时使用如下配置:
@State(Scope.Benchmark) @Fork(1) @Warmup(iterations = 50) @Measurement(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS) @OutputTimeUnit(TimeUnit.MILLISECONDS) @BenchmarkMode(Mode.AverageTime)
内容的提问来源于stack exchange,提问作者Mangat Rai Modi
相关产品推荐
相关产品推荐

