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

为什么Kotlin同逻辑的pmap扩展函数比手动实现代码运行更慢?

问题核心原因

  • 首先是协程调度器冷启动开销:你的测试代码优先执行pmap测试,此时Kotlin协程的默认调度器Dispatchers.Default、后台线程池还未完成初始化,第一次启动协程会产生额外的初始化开销,后续执行的manualpmap测试已经复用了初始化完成的调度器,所以耗时更低。你可以调换两个测试的执行顺序,会发现manualpmap的耗时反而会高于pmap。
  • 其次是pmap实现本身存在不合理设计:你在遍历Deferred列表时,为每个await()单独创建了runBlocking上下文,每次创建runBlocking都要初始化独立的事件循环、协程上下文,会产生不必要的额外开销。
  • 最后是参数传递的额外间接调用:你调用pmap时传入的lambda是{ transform(it) },相比manualpmap里直接在async块中调用transform,多了一层函数转发的开销,虽然这个开销占比很低,但也是差异来源之一。

优化方案

你可以按如下方式修改pmap实现,性能会和手动实现完全一致:

fun <T, U> List<T>.pmap(
    scope: CoroutineScope = GlobalScope,
    transform: suspend (T) -> U
): List<U> = runBlocking {
    // 一次性启动所有异步任务,然后统一等待所有任务完成,仅用一次runBlocking
    map { scope.async { transform(it) } }.awaitAll()
}

如果要进一步优化,可以给函数加上inline修饰,同时给transform参数加上crossinline关键字,消除lambda调用的额外开销:

inline fun <T, U> List<T>.pmap(
    scope: CoroutineScope = GlobalScope,
    crossinline transform: suspend (T) -> U
): List<U> = runBlocking {
    map { scope.async { transform(it) } }.awaitAll()
}

额外建议

  • 尽量避免默认使用GlobalScope,它不支持结构化并发,任务抛出异常或者被取消时无法自动联动,推荐由调用方传入自己的CoroutineScope。
  • 性能测试时要注意排除冷启动影响,可以先预热执行几次测试逻辑,再统计正式的耗时数据,避免单次运行的初始化开销干扰结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 09:12:08