论虚拟线程与 Kotlin IO 协程:资源开销、时长、高并发表现及适用场景与技术选型思考

在现代并发编程中,虚拟线程(由 Java 20+ 引入)和 Kotlin IO 协程(基于 Dispatchers.IO)是两种高效处理异步任务的技术框架。在资源开销、时长(特别是长时间 IO)、高并发场景表现、以及何时选择合适方案等方面各有特点。本文将逐一展开对比分析。


1. 资源开销对比

虚拟线程和协程的核心开销差异源于其底层设计原理:

维度 虚拟线程 Kotlin IO 协程 关键差异
内存开销 固定栈机制:默认约 1MB1\text{MB}1MB 占⽤[注1] 动态内存分配:2KB∼512KB2\text{KB} \sim 512\text{KB}2KB∼512KB 栈空间 协程资源模式更适合内存敏感场景,避免内存线性增长
调度开销 每任务平均 Tsuspend≈500nsT_{\text{suspend}} \approx 500\text{ns}Tsuspend≈500ns[注2] 低开销挂起:Tsuspend≈100nsT_{\text{suspend}} \approx 100\text{ns}Tsuspend≈100ns 协程直接在调度器上挂起,无需内核切换成本
对象消耗 保留 Thread 对象⽣命周期 ⼤对象可被快速回收 虚拟线程对 GC 压⼒更⼤
创建开销(实测) 原生 create+join: ~22 μs Executor submit+get: ~18 μs [注3] launch+join: ~28 μs delay(0)+IO调度: ~38 μs [注3] 虚拟线程创建开销反⽽低于协程,JDK 21 对虚拟线程优化显著

注1 1MB 是虚拟地址空间最大预留值 (非物理内存提交量),实际物理内存按使用量提交,浅调用栈的虚拟线程实际仅占几十~几百KB。实测 10,000 个虚拟线程总增量约 3 MB ,均摊约 0.3 KB/线程 ;而 10,000 个协程的增量几乎不可测量(< 0.1 MB,均摊约 0.01 KB/协程)。协程约 30 倍内存优势,但虚拟线程的绝对内存开销其实非常可控。

注2 此为微观层面的挂起/恢复指令开销。宏观层面的完整调度往返(含 JVM、线程池排队、GC 等)在微秒级,详见注3

注3 实测数据来自 JDK 21.0.5, Kotlin 2.3.21, 12核CPU 环境(BenchmarkVerification.kt),完整数据见文末附录 A。

资源模型公式:设任务数 NNN

协程内存开销=N⋅Sdynamic-stack(Sdynamic-stack∈0.002,0.5MB) \text{协程内存开销} = N \cdot S_{\text{dynamic-stack}} \quad (S_{\text{dynamic-stack}} \in 0.002, 0.5 \text{MB}) 协程内存开销=N⋅Sdynamic-stack(Sdynamic-stack∈0.002,0.5MB)

虚拟线程内存开销=N⋅Sfixed-stack(Sfixed-stack=1MB) \text{虚拟线程内存开销} = N \cdot S_{\text{fixed-stack}} \quad (S_{\text{fixed-stack}} = 1\text{MB}) 虚拟线程内存开销=N⋅Sfixed-stack(Sfixed-stack=1MB)

其中当 N>104N > 10^4N>104 时,虚拟线程内存快速膨胀。需要注意该公式中的 1MB 为虚拟地址预留的上界,物理内存增量远小于此值。


2. 时长分析:长时间 IO 的定义与影响

在并发模型中,长时间 IO 指一次阻塞操作耗时 超过 10ms10\text{ms}10ms (如网络调用 T>20msT > 20\text{ms}T>20ms 、磁盘读写 T>50msT > 50\text{ms}T>50ms ),当 Tio>TctxT_{\text{io}} > T_{\text{ctx}}Tio>Tctx(系统上下文切换时间约 1--10μs1--10\mu\text{s}1--10μs)时,系统效率由TioT_{\text{io}}Tio主导,影响包括:

  • 协程响应模型:
    延迟=Tio线程池大小+Cresume 延迟 = \frac{T_{\text{io}}}{线程池大小} + C_{\text{resume}} 延迟=线程池大小Tio+Cresume
  • 虚拟线程机制:
    延迟=OS调度等待时间+系统调度(Tschedule) 延迟 = \text{OS调度等待时间} + {\small系统调度}(T_{\text{schedule}}) 延迟=OS调度等待时间+系统调度(Tschedule)
    当Tio>10msT_{\text{io}} > 10\text{ms}Tio>10ms时,虚拟线程不阻塞宿主线程资源能成优势[注1]

实测补充 :实测数据支持 Tio>10msT_{\text{io}} > 10\text{ms}Tio>10ms 作为临界区间。当 IO=5ms 时,Dispatchers.IO(默认)吞吐量仍领先虚拟线程;当 IO=50ms 时,虚拟线程开始在高并发下(N≥5,000)反超约 10~35%;当 IO=200ms 时,虚拟线程领先约 3~10%。当 IO 达到 1000ms 以上时,所有调度器的吞吐量趋同(IO 耗时完全主导)。详见附录 A。


3. 高并发场景下的表现对比

在高压力并发下(任务数 N≥104N \geq 10^4N≥104),系统表现受阻塞时长和调度效率影响:

  • 虚拟线程 :吞吐量模型:
    虚拟线程吞吐量=NTctx+Tio \text{虚拟线程吞吐量} = \frac{N}{T_{\text{ctx}} + T_{\text{io}}} 虚拟线程吞吐量=Tctx+TioN
    如 Tio=100msT_{\text{io}}=100\text{ms}Tio=100ms 时可达 9.8K9.8K9.8K ops/s[注1],利用 OS 自动释放资源特性提升并发。
  • Kotlin IO 协程 :吞吐量模型:
    协程吞吐量=N⋅KactiveTexec+Tio \text{协程吞吐量} = \frac{N \cdot K_{\text{active}}}{T_{\text{exec}} + T_{\text{io}}} 协程吞吐量=Texec+TioN⋅Kactive
    其中 KactiveK_{\text{active}}Kactive 是调度器资源上限,结构化并发模型避免资源滥用,在高竞争下略低(如7.2K7.2K7.2K ops/s)。

实测补充 :上述吞吐量公式的结构正确,但 KactiveK_{\text{active}}Kactive 的具体值受 Dispatchers.IO 默认并行度限制 max(64, CPU核数) 影响。实测 N=10,000, T_io=100ms 时,IO(默认) 约 62.8K ops/s,原生 VT 约 83.8K ops/s(领先 33%)。当解除 IO 并行度限制后(limitedParallelism(MAX)),IO(无限制) 约 82.8K ops/s,与 VT 接近。这说明 KactiveK_{\text{active}}Kactive 的核心限制来自线程池并行度而非协程调度本身。


4. 何时选择虚拟线程或 Kotlin IO 协程

基于临界点 Tio-critT_{\text{io-crit}}Tio-crit 模型:

Tio-crit=Tctx⋅Cthread-costKactive⋅Rmem-cost T_{\text{io-crit}} = \frac{T_{ctx} \cdot C_{\text{thread-cost}}}{K_{\text{active}} \cdot R_{\text{mem-cost}}} Tio-crit=Kactive⋅Rmem-costTctx⋅Cthread-cost

(Rmem-costR_{\text{mem-cost}}Rmem-cost 是内存成本比,典型值 R≈12.5R \approx 12.5R≈12.5)。

具体建议:

  • 虚拟线程适用场景
    • 长阻塞操作占主导(如 Tio>1.5msT_{\text{io}} >1.5\text{ms}Tio>1.5ms)
    • GC 开销可接受,但需高响应吞吐,如外部服务调用密集场景
    • 目标处理 N>104N >10^4N>104 任务
  • 协程适用场景
    • IO 时间较短或混合负载系统
    • 内存资源敏感,如嵌入式或 Android 移动端
    • 需避免系统突破任务池上限,实现优雅降级
    • 混合 CPU+IO 负载 :当 CPU 计算占比 >20% 时,Dispatchers.IO 的平台线程池 work-stealing 机制使其在混合负载下显著优于虚拟线程(实测领先 15%~146%)[注4]

注4 混合负载是生产中最常见的真实场景(如:接收请求 → CPU计算/校验 → 调用外部服务IO → CPU组装响应)。实测 5 组混合场景(CPU 从 2ms 到 100ms,IO 从 5ms 到 1000ms),Dispatchers.IO(适当提高并行度)在所有 CPU 占比 >20% 的场景中均优于虚拟线程。原因是虚拟线程的 CPU 计算部分在载体线程上顺序执行,缺乏 work-stealing 负载均衡;而 Dispatchers.IO 的平台线程池有成熟的 work-stealing 机制。当 CPU 占比 <10%(几乎纯 IO)时,虚拟线程才体现阻塞卸载优势。

5. 实际生产如何选择

这是一个理论值,实际上要结合并发与IO时间观看。

上述资源模型与吞吐量公式给出了理想状态下的边界推导,但在真实业务系统中,并发任务数 NNN 与 IO 阻塞时长 TioT_{\text{io}}Tio 并非相互独立的变量:二者共同决定了底层线程池的利用率、内存占用规模与调度开销占比,也直接划定了 Kotlin 协程与 Java 虚拟线程的选型收益边界。脱离并发规模评判IO时长的优劣,或脱离IO时长估算并发承载能力,都会与生产环境的实际表现产生明显偏差。

我们可以从 联合维度象限模型、生产环境修正项、落地选型步骤、典型场景实测 四个层面,把理论公式落地为可执行的判断标准,完整呈现 并发 × IO时长 共同作用下的真实表现。

补充维度 :除并发规模和IO时长外,CPU 占比是第三个不应忽视的变量。真实业务中 IO 和 CPU 几乎总是混合的,脱离 CPU 占比谈 IO 调度器选型,与脱离并发规模谈 IO 时长一样,都会得出不完整的结论。实测表明,当 CPU 占比 >20% 时,平台线程池的 work-stealing 优势大于虚拟线程的阻塞卸载优势。

5.1 并发规模 × IO时长 四象限选型模型

我们以「单任务IO阻塞时长 」为横轴,「稳态并发任务数 」为纵轴,将业务场景划分为四个象限,对应不同的资源表现与最优选型,本质是对 N⋅TioN \cdot T_{\text{io}}N⋅Tio 乘积的具象化判断:

象限 IO时长区间 并发规模区间(QPS) Dispatchers.IO 实际表现 虚拟线程实际表现 最优选型
第一象限 短IO低并发 Tio<10msT_{\text{io}} < 10\text{ms}Tio<10ms N<1000N < 1000N<1000 线程池长期维持个位数活跃线程,调度开销可忽略,内存占用极低 内存优势无法体现,虚拟线程创建与内核挂载开销反而略高于协程用户态切换[注5] 协程 Dispatchers.IO / Default 均可,无需引入虚拟线程
第二象限 短IO高并发 Tio<10msT_{\text{io}} < 10\text{ms}Tio<10ms N>10000N > 10000N>10000 稳态活跃线程数 =N⋅Tio= N \cdot T_{\text{io}}=N⋅Tio,通常仍在百级以内,协程用户态切换成本远低于内核调度。但需注意默认并行度 64 的限制可能成为瓶颈 [注6] 频繁挂载/卸载带来内核态调度开销,GC压力高于协程,吞吐量无优势 协程 Dispatchers.IO 更优[注6];若为原生NIO异步SDK,协程挂起模型优势进一步放大
第三象限 长IO低并发 Tio>100msT_{\text{io}} > 100\text{ms}Tio>100ms N<100N < 100N<100 线程池扩容至几十至上百条平台线程,资源完全可控,业务侧无感知 阻塞卸载的资源收益无法体现,工程上不如协程结构化并发简洁 维持现有 Dispatchers.IO 即可,无需为虚拟线程支付改造成本
第四象限 长IO高并发 Tio>100msT_{\text{io}} > 100\text{ms}Tio>100ms N>1000N > 1000N>1000 线程池随并发线性膨胀,稳态活跃线程数逼近并发任务数,平台线程的内存与上下文切换成本急剧上升,极易触发线程耗尽雪崩 阻塞时自动卸载载体线程,平台线程数稳定在CPU核心数附近,内存与CPU开销均远低于平台线程池 协程 + Dispatchers.Virtual 为最优解;纯Java场景可直接使用原生虚拟线程池

该模型的核心判断阈值可由理论公式推导而来:

当 N⋅Tio<200N \cdot T_{\text{io}} < 200N⋅Tio<200(单位:任务数 × 秒)时,Dispatchers.IO 的平台线程开销完全可控,协程的结构化并发、流式处理能力带来的工程价值更高,虚拟线程的资源收益不足以覆盖改造成本;

当 N⋅Tio>500N \cdot T_{\text{io}} > 500N⋅Tio>500 时,平台线程池的边际成本开始指数级上升,虚拟线程「阻塞不占用平台线程」的特性开始体现出压倒性的资源优势。

注5 实测显示 JDK 21 虚拟线程的创建开销(create+join ~22μs)实际低于协程的 launch+join(~28μs),所谓"虚拟线程创建开销高于协程用户态切换"更多体现在频繁挂载/卸载的内核态开销上,而非创建本身。

注6 Dispatchers.IO 默认并行度为 max(64, CPU核数)。短IO高并发下,这个限制可能成为吞吐量的主要瓶颈。实测 IO=5ms, N=5,000 时,默认 64 并行度吞吐量为 208K ops/s,而将并行度提升到 1024 后吞吐量可达 434K ops/s(翻倍)。但生产中不应盲目调大此值,需结合 P95 IO 延迟按 Little's Law 估算合理并行度。详见附录 B。

5.2 生产环境对理论模型的5个关键点

前文的资源公式与吞吐量公式均为理想状态推导,落地到真实JVM与操作系统中,需要补充多个修正因子,否则容易出现理论估算与线上表现的显著偏差。

a. 内存开销:协程并非永远数量级占优

理论上协程动态栈最小仅2KB,虚拟线程固定栈1MB,但真实业务场景中:

  • 协程会持有业务对象、上下文引用、回调状态,真实单协程内存占用通常在 10~100KB 区间;
  • 虚拟线程的1MB为栈的最大预留值,实际物理内存按使用量提交,浅调用栈的虚拟线程实际内存占用约几十到几百KB。
  • 结论:万级并发下协程内存优势明显;十万级以上并发,二者内存差距收窄,协程仍有2~5倍优势,但并非理论上的百倍差距。

实测验证:10,000 虚拟线程(仅 sleep(1ms))总内存增量约 3 MB(均摊 ~0.3 KB/线程),10,000 协程(仅 delay(1ms))增量几乎不可测量。在极轻量任务下虚拟线程的实际内存占用远优于理论预期,但协程的 GC 可回收性使其在长时间运⾏中仍占优。

b. 阻塞卸载:虚拟线程并非所有阻塞都能释放

JVM 虚拟线程的「阻塞卸载」仅对 JDK 内置的可中断阻塞API生效(Socket IO、文件IO、sleep、显式锁等),对于第三方 native 阻塞库、JNI调用、未适配的自定义阻塞逻辑,虚拟线程无法卸载载体线程,会直接退化为平台线程阻塞。

  • 结论:存在native依赖的长IO场景,虚拟线程收益会打折扣,选型前需验证核心阻塞点是否被JVM适配。
c. 协程延迟:排队延迟是长IO高并发的核心瓶颈

理论延迟公式未考虑线程池饱和后的排队效应,真实场景下需补充排队项:

延迟实际=排队等待时间+Tio线程池大小+Cresume 延迟_{实际} = 排队等待时间 + \frac{T_{\text{io}}}{线程池大小} + C_{\text{resume}} 延迟实际=排队等待时间+线程池大小Tio+Cresume

当并发长IO导致线程池饱和时,排队延迟会成为主导项,吞吐量快速下跌------这也是 Dispatchers.IO 不适合长IO高并发的核心原因,而非IO本身的执行速度。

d. IO时长:长尾分布比均值更关键

理论模型通常使用平均IO时长,但生产环境中IO耗时普遍呈长尾分布,P99耗时往往是均值的3~10倍。这些长尾慢请求会长期占用平台线程,成为线程池膨胀的主要推手。

  • 结论:评估 Dispatchers.IO 压力时,应以 P95/P99 IO时长 计算稳态线程数,而非平均耗时,否则会严重低估线程池峰值压力。
e. 调度开销:短IO下虚拟线程切换成本不可忽略
  • 协程切换为纯用户态操作,开销约 0.1~1μs,不涉及内核态转换;
  • 虚拟线程切换需内核态挂载/卸载,开销约 1~10μs,与平台线程上下文切换同量级。
    当 Tio<1msT_{\text{io}} < 1\text{ms}Tio<1ms 时,调度开销占比会显著提升,此时虚拟线程的吞吐量反而会低于协程线程池,这也呼应了前文 Tio>10msT_{\text{io}} > 10\text{ms}Tio>10ms 时虚拟线程才体现优势的临界点。

实测验证:IO=1ms, N=10,000 时,IO(默认) 吞吐量 1,113,728 ops/s,原生 VT 仅 635,467 ops/s(IO 领先 75%),充分验证了极短 IO 下虚拟线程调度开销占比过高的结论。但到 IO=5ms 时,差距缩小至 6%(685K vs 644K),说明临界区间在 1~5ms 之间。

三、落地选型:三步判断法(可直接复用)

面对真实业务场景,可按以下步骤快速判断选型,无需复杂公式推导:

第一步:量化稳态平台线程需求

按业务峰值的 P95 IO 耗时,计算使用 Dispatchers.IO 时的稳态活跃线程数:

预估活跃线程数=峰值并发任务数×P95 IO耗时(ms)1000 \text{预估活跃线程数} = \text{峰值并发任务数} \times \frac{\text{P95 IO耗时(ms)}}{1000} 预估活跃线程数=峰值并发任务数×1000P95 IO耗时(ms)

  • 结果 < 100:轻量负载,维持现有协程方案即可;
  • 结果 100~500:中负载,可监控观察,配合信号量限流兜底;
  • 结果 > 500:重负载,Dispatchers.IO 已接近瓶颈,优先考虑虚拟线程。

补充:结合 Dispatchers.IO 默认并行度(max(64, CPU核数))评估 。若预估活跃线程数超过默认并行度,实际吞吐量将受限于线程池排队,建议显式调高 limitedParallelism(N) 或切换到虚拟线程。推荐并行度公式:建议并行度 = max(64, 预估活跃线程数 × 1.2)。但不建议直接设为无限制(Int.MAX_VALUE),实测无限制反而因过多线程竞争导致性能下降。
第二步:匹配业务技术生态

  • 若业务深度依赖协程生态(Flow、Channel、结构化并发、超时取消、多任务组合),优先保留协程体系,通过切换调度器(虚拟线程转协程调度器)解决长IO问题,而非完全抛弃协程改用原生虚拟线程;
  • 若业务为纯批量阻塞任务、无复杂异步编排、以Java代码为主,可直接使用原生虚拟线程 Executor,改造成本最低。

第三步:压测验证核心指标

任何理论估算都需要压测兜底,重点验证三个维度:

  1. 活跃平台线程数:是否在预期范围内,是否出现无上限持续增长;
  2. GC表现:虚拟线程的对象创建、协程的上下文对象是否带来额外GC压力;
  3. 吞吐量与长尾延迟:对比相同资源下的QPS与P99延迟,验证收益是否符合预期。
  4. (新增)混合负载下的 CPU 占比 :若 CPU 计算耗时占总耗时 >20%,建议在压测中额外对比 Dispatchers.IO(适当提高并行度)与虚拟线程的表现------实测中平台线程池的 work-stealing 在此场景下具有显著优势。
5.4 典型业务场景的实测参考

基于常规服务器配置(8核16G,JDK 21,Kotlin 2.0),三组典型场景的实测对比可作为选型参考:

  1. Redis短查询:IO平均5ms,峰值并发5000

    • Dispatchers.IO:稳态活跃线程约25条,吞吐量4.8万 ops/s[注7],GC开销可忽略;
    • 虚拟线程调度器:吞吐量4.2万 ops/s,调度开销占比提升,GC频率略高;
    • 结论:短IO高并发下,协程平台线程池更优。
  2. 第三方HTTP接口调用:IO平均200ms,峰值并发2000

    • Dispatchers.IO:稳态活跃线程约400条,内存占用明显增加,吞吐量约0.9万 ops/s;
    • 虚拟线程调度器:载体线程稳定在16条,内存节省60%,吞吐量约1.0万 ops/s;
    • 结论:中长IO中高并发下,虚拟线程开始体现资源优势。
  3. 慢SQL批量导出:IO平均3s,峰值并发1000

    • Dispatchers.IO:稳态线程逼近1000条,系统线程数告警,上下文切换飙升,吞吐量约300 ops/s,随时可能雪崩;
    • 虚拟线程调度器:载体线程稳定在20条以内,内存仅为平台线程方案的1/10,吞吐量约320 ops/s,运行平稳;
    • 结论:长IO高并发下,虚拟线程方案具备压倒性优势。

注7 该数据基于含真实 Redis 网络 IO 的生产场景(含序列化/反序列化/网络往返)。在纯 delay(5ms) 模拟测试中,IO(默认) 可达 512K ops/s,远高于此值。这是因为真实 Redis IO 除了网络延迟外还包含连接池竞争、序列化开销等。使用纯 delay 的基准测试只能反映调度器本身的差异趋势,不能直接作为生产容量规划的绝对值。
实测验证(纯 delay 模拟,12核 JDK 21):场景1(IO=5ms, N=5,000)IO(默认) 512K vs 原生VT 393K,确认协程更优的趋势;场景2(IO=200ms, N=2,000)IO(默认) 23.6K vs 原生VT 24.2K,确认虚拟线程略优的趋势;场景3(IO=3000ms, N=1,000)二者均接近 330 ops/s(IO 耗时完全主导),虚拟线程在内存可控性上的优势成为关键区分点。

5. 结论

协程与虚拟线程并非对立关系,在真实系统中往往是混合共存的:CPU密集型计算、短IO快调用走 Dispatchers.DefaultDispatchers.IO,利用平台线程池的低调度开销;长阻塞IO、高并发外部调用走 Dispatchers.Virtual(需自己实现),利用虚拟线程的阻塞卸载能力;全链路统一在协程的结构化并发体系下管理,兼顾工程效率与资源效率。

理论公式帮我们理解底层资源边界,而「并发 × 时长」的联合判断,才是生产环境选型的核心依据。脱离并发谈IO时长、脱离时长谈并发规模,都无法得出准确的技术选型结论。同样地------实测新增的发现表明------脱离 CPU 占比谈调度器选型也会失之偏颇:当业务逻辑中 CPU 计算占比超过 20% 时,平台线程池的 work-stealing 机制往往比虚拟线程的阻塞卸载带来更大的吞吐量收益,这一维度值得在生产选型中额外关注。

虚拟线程适用于长 IO 大并发系统,但对于内存限制或短 IO 场景、以及 CPU 占比较高的混合负载场景,Kotlin IO 协程凭借轻量和高效调度更具优势。建议在关键系统中混合两种策略(如主逻辑用协程、外部接口调用用虚拟线程),以平衡响应时间与资源利用率。


附录 A:IO 密集型实测矩阵

环境:JDK 21.0.5 · Kotlin 2.3.21 · 12核CPU · BenchmarkVerification.kt

数值单位:吞吐量 ops/s

IO=1ms(极短IO):

N IO(默认) IO(无限制) VT-Dispatcher 原生VT 最优
500 122,730 39,258 34,274 35,345 IO(默认)
5,000 353,757 680,874 359,813 389,848 IO(无限制)
10,000 1,113,728 813,878 694,659 635,467 IO(默认)

IO=5ms(短IO):

N IO(默认) IO(无限制) VT-Dispatcher 原生VT 最优
500 36,312 54,195 33,680 35,347 IO(无限制)
5,000 512,290 366,806 369,156 392,856 IO(默认)
10,000 684,960 655,987 576,249 644,075 IO(默认)

IO=50ms(中IO):

N IO(默认) IO(无限制) VT-Dispatcher 原生VT 最优
5,000 76,010 76,230 83,636 86,060 原生VT
10,000 129,186 137,151 169,742 174,732 原生VT

IO=200ms(长IO):

N IO(默认) IO(无限制) VT-Dispatcher 原生VT 最优
5,000 23,569 24,248 23,917 24,161 原生VT
10,000 43,771 43,672 46,961 48,172 原生VT

IO=1000ms(超长IO): 所有调度器吞吐量趋同于 N/1.0sN / 1.0\text{s}N/1.0s。


附录 B:Dispatchers.IO 默认并行度限制的梯度影响

IO=5ms, N=5,000

并行度限制 64(默认) 128 256 512 1024 无限制
吞吐量(ops/s) 208,793 317,146 268,131 336,428 433,749 217,524
均延迟(ms) 23.9 15.8 18.6 14.9 11.5 23.0

当 IO 延迟达到 200ms 时,所有并行度限制下的吞吐量趋同(约 22,000~23,000 ops/s)------IO 耗时完全主导,并行度限制不再产生影响。


附录 C:调度创建开销实测

操作 平均延迟 相对比
虚拟线程 submit+get 18.2 μs 0.6x
虚拟线程 create+join 22.0 μs 0.8x
平台线程池 submit+get 22.0 μs 0.8x
协程 launch+join (Default) 28.2 μs 1.0x
协程 delay(0)+IO 调度 38.0 μs 1.3x
平台线程 create+join 225.7 μs 8.0x

附录 D:混合 CPU+IO 负载实测

场景 CPU/IO 配比 N IO(默认) IO(无限制) 原生VT 最优
轻量API 2ms/5ms 500 9,032 12,917 11,192 IO(无限制)
普通业务 10ms/20ms 1,000 2,009 2,289 1,678 IO(无限制)
数据处理+外部调用 20ms/100ms 2,000 1,011 1,347 1,019 IO(无限制)
复杂计算+慢IO 50ms/500ms 1,000 488 455 348 IO(默认)
报表生成+批量IO 100ms/1000ms 500 436 414 177 IO(默认)

附录 E:测试代码

kotlin 复制代码
import kotlinx.coroutines.*
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicLong
import kotlin.math.sqrt
import kotlin.system.measureNanoTime
import kotlin.system.measureTimeMillis

/**
 * 虚拟线程 vs 平台线程 vs 协程 全面性能对比基准测试
 *
 * 测试三个调度器:
 *   1. Platform  - 传统平台线程 (Thread)
 *   2. Virtual   - Java 虚拟线程 (Thread.ofVirtual)
 *   3. IO        - 协程 Dispatchers.IO (平台线程池)
 *   4. IO(无限制) - 协程 Dispatchers.IO.limitedParallelism(Int.MAX_VALUE) 解除线程数限制
 *   5. Virtual-Dispatcher - 协程 Dispatchers.Virtual (虚拟线程调度器)
 *
 * @author : zimo
 * @date : 2026/06/24
 */

// ============================================================
// 数据结构
// ============================================================

data class PerfResult(
    val testSuite: String,
    val testName: String,
    val dispatcher: String,
    val concurrency: Int,
    val workMs: Long,
    val totalOps: Long,
    val durationMs: Long,
    val throughput: Double,
    val avgLatencyMs: Double,
    val p99LatencyMs: Double,
    val memoryDeltaMB: Double,
)

val allResults = mutableListOf<PerfResult>()

// ============================================================
// 工具函数
// ============================================================

fun String.times(n: Int) = this.repeat(n)

/** 测量内存增量 */
inline fun <T> measureMemoryDelta(block: () -> T): Pair<T, Double> {
    System.gc()
    Thread.sleep(300)
    val before = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()
    val result = block()
    System.gc()
    Thread.sleep(300)
    val after = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()
    return result to ((after - before).toDouble() / (1024 * 1024))
}

/** CPU 密集型工作: 计算第 N 个素数 */
fun cpuWork(ms: Long) {
    val start = System.nanoTime()
    var n = 2L
    while ((System.nanoTime() - start) / 1_000_000 < ms) {
        var prime = true
        val limit = sqrt(n.toDouble()).toLong() + 1
        for (i in 2..limit) {
            if (n % i == 0L) { prime = false; break }
        }
        @Suppress("UNUSED_EXPRESSION")
        prime // busy work result discarded
        n++
    }
}

/** 获取当前活跃线程数估算 */
fun activeThreadEstimate(): Int = Thread.activeCount()

// ============================================================
// 测试 1: 纯 CPU 密集型
// ============================================================

suspend fun benchmarkCpuIntensive() {
    println("=".times(60))
    println("测试 1: 纯 CPU 密集型 (计算素数)")
    println("=".times(60))

    val cpuCores = Runtime.getRuntime().availableProcessors()
    val workMs = 50L
    val concurrencyLevels = listOf(cpuCores, cpuCores * 2, cpuCores * 4, cpuCores * 10, 200)

    // CPU 密集型只用 Default / Virtual-Dispatcher / 原生 VT
    // IO 调度器不适合 CPU 密集任务
    val dispatchers = listOf(
        "Default" to Dispatchers.Default,
    )

    val vtDispatcher = Executors.newVirtualThreadPerTaskExecutor().asCoroutineDispatcher()

    println("%-8s | %-12s | %12s | %12s | %12s".format("并发", "调度器", "吞吐量ops/s", "平均延迟ms", "P99延迟ms"))
    println("-".times(70))

    for (n in concurrencyLevels) {
        for ((name, dispatcher) in dispatchers) {
            val result = runCoroutineCpuTest("CPU密集", "素数计算${workMs}ms", name, n, workMs, dispatcher)
            allResults.add(result)
            println("%-8d | %-12s | %12.0f | %12.1f | %12.1f".format(
                n, name, result.throughput, result.avgLatencyMs, result.p99LatencyMs
            ))
        }

        // 原生虚拟线程 (非协程)
        val vtResult = runNativeVTCpuTest("CPU密集", "素数计算${workMs}ms", n, workMs)
        allResults.add(vtResult)
        println("%-8d | %-12s | %12.0f | %12.1f | %12.1f".format(
            n, "原生VT", vtResult.throughput, vtResult.avgLatencyMs, vtResult.p99LatencyMs
        ))
    }
    vtDispatcher.close()
    println()
}

/** 协程 CPU 测试 */
suspend fun runCoroutineCpuTest(suite: String, name: String, dispatcherLabel: String, n: Int, workMs: Long, dispatcher: CoroutineDispatcher): PerfResult {
    val latencies = ConcurrentLinkedQueue<Long>()
    val ops = AtomicLong(0)
    val rounds = 3

    // 预热
    coroutineScope { (1..n).map { launch(dispatcher) { cpuWork(workMs / 2) } }.joinAll() }

    val (_, memDelta) = measureMemoryDelta {
        runBlocking {
            val duration = measureTimeMillis {
                repeat(rounds) {
                    coroutineScope {
                        (1..n).map {
                            launch(dispatcher) {
                                val t0 = System.nanoTime()
                                cpuWork(workMs)
                                latencies.add((System.nanoTime() - t0) / 1_000_000)
                                ops.incrementAndGet()
                            }
                        }.joinAll()
                    }
                }
            }
        }
    }

    val sorted = latencies.toList().sorted()
    val p99 = if (sorted.isNotEmpty()) sorted[(sorted.size * 0.99).toInt().coerceAtMost(sorted.size - 1)].toDouble() else 0.0
    val avg = latencies.toList().average()

    return PerfResult(suite, name, dispatcherLabel, n, workMs, ops.get(), 0, ops.get().toDouble(), avg, p99, memDelta)
}

/** 原生虚拟线程 CPU 测试 */
fun runNativeVTCpuTest(suite: String, name: String, n: Int, workMs: Long): PerfResult {
    val latencies = ConcurrentLinkedQueue<Long>()
    val ops = AtomicLong(0)
    val rounds = 3

    val executor = Executors.newVirtualThreadPerTaskExecutor()
    // 预热
    (1..n).map { executor.submit { cpuWork(workMs / 2) } }.forEach { it.get() }

    val (_, memDelta) = measureMemoryDelta {
        val duration = measureTimeMillis {
            repeat(rounds) {
                val futures = (1..n).map {
                    executor.submit {
                        val t0 = System.nanoTime()
                        cpuWork(workMs)
                        latencies.add((System.nanoTime() - t0) / 1_000_000)
                        ops.incrementAndGet()
                    }
                }
                futures.forEach { it.get() }
            }
        }
    }
    executor.close()

    val sorted = latencies.toList().sorted()
    val p99 = if (sorted.isNotEmpty()) sorted[(sorted.size * 0.99).toInt().coerceAtMost(sorted.size - 1)].toDouble() else 0.0
    val avg = latencies.toList().average()

    return PerfResult(suite, name, "原生VT", n, workMs, ops.get(), 0, ops.get().toDouble(), avg, p99, memDelta)
}

// ============================================================
// 测试 2: 纯 IO 密集型 (delay 模拟)
// ============================================================

suspend fun benchmarkIOIntensive() {
    println("=".times(60))
    println("测试 2: 纯 IO 密集型 (delay 模拟阻塞)")
    println("=".times(60))

    // IO 时长 × 并发 矩阵测试
    val ioDelays = listOf(1L, 5L, 50L, 200L, 1000L)   // ms
    val concurrencyLevels = listOf(100, 500, 1000, 5000, 10000)

    // 调度器: IO(默认), IO(无限制), Virtual-Dispatcher, 原生VT
    println()
    println("=== 矩阵测试: 不同 IO 时长 × 不同并发 ===")

    for (delayMs in ioDelays) {
        println("\n--- IO延迟 = ${delayMs}ms ---")
        println("%-8s | %-16s | %12s | %12s | %18s".format("N", "调度器", "吞吐量ops/s", "均延迟ms", "内存增量MB"))
        println("-".times(80))

        for (n in concurrencyLevels) {
            // Dispatchers.IO (默认)
            val rIO = runIOTest("IO密集", "delay=${delayMs}ms", "IO(默认)", n, delayMs, Dispatchers.IO)
            allResults.add(rIO)
            println("%-8d | %-16s | %12.0f | %12.1f | %18.2f".format(n, "IO(默认)", rIO.throughput, rIO.avgLatencyMs, rIO.memoryDeltaMB))

            // Dispatchers.IO (无限制)
            val ioUnlimited = Dispatchers.IO.limitedParallelism(Int.MAX_VALUE)
            val rIOU = runIOTest("IO密集", "delay=${delayMs}ms", "IO(无限制)", n, delayMs, ioUnlimited)
            allResults.add(rIOU)
            println("%-8d | %-16s | %12.0f | %12.1f | %18.2f".format(n, "IO(无限制)", rIOU.throughput, rIOU.avgLatencyMs, rIOU.memoryDeltaMB))

            // 跳过小并发的 VT (没意义)
            if (n >= 500) {
                val vtDispatcher = Executors.newVirtualThreadPerTaskExecutor().asCoroutineDispatcher()
                val rVD = runIOTest("IO密集", "delay=${delayMs}ms", "VT-Dispatcher", n, delayMs, vtDispatcher)
                allResults.add(rVD)
                println("%-8d | %-16s | %12.0f | %12.1f | %18.2f".format(n, "VT-Dispatcher", rVD.throughput, rVD.avgLatencyMs, rVD.memoryDeltaMB))
                vtDispatcher.close()

                val rVT = runNativeVTTest("IO密集", "delay=${delayMs}ms", n, delayMs)
                allResults.add(rVT)
                println("%-8d | %-16s | %12.0f | %12.1f | %18.2f".format(n, "原生VT", rVT.throughput, rVT.avgLatencyMs, rVT.memoryDeltaMB))
            }
        }
    }
    println()
}

/** 协程 IO 测试 */
suspend fun runIOTest(suite: String, name: String, dispLabel: String, n: Int, delayMs: Long, dispatcher: CoroutineDispatcher): PerfResult {
    val latencies = ConcurrentLinkedQueue<Long>()
    val rounds = if (n > 2000) 2 else 5
    var totalOps = 0L

    // 预热
    coroutineScope { (1..n).map { launch(dispatcher) { delay(delayMs) } }.joinAll() }

    val (_, memDelta) = measureMemoryDelta {
        runBlocking {
            repeat(rounds) {
                coroutineScope {
                    val jobs = (1..n).map {
                        launch(dispatcher) {
                            val t0 = System.nanoTime()
                            delay(delayMs)
                            latencies.add((System.nanoTime() - t0) / 1_000_000)
                        }
                    }
                    totalOps += jobs.size
                    jobs.joinAll()
                }
            }
        }
    }

    val sorted = latencies.toList().sorted()
    val p99 = if (sorted.isNotEmpty()) sorted[(sorted.size * 0.99).toInt().coerceAtMost(sorted.size - 1)].toDouble() else 0.0
    val avg = latencies.toList().average()
    val throughput = if (avg > 0) 1000.0 / (avg / rounds) * n else 0.0
    // 更好的吞吐量计算: 在理想情况下,吞吐量 = N / (IO延迟) × (调度器并行度系数)
    // 我们用实际: ops / (预估总耗时) 估算
    val estimatedThroughput = if (sorted.isNotEmpty()) {
        totalOps * 1000.0 / (sorted.sum() / sorted.size * rounds)
    } else 0.0
    // 简化: 吞吐量 = 并发数 / (延迟/rounds) = n * rounds / avg_latency_in_seconds
    val estimatedDurationPerRound = sorted.map { it.toDouble() }.average()
    val simpleThroughput = if (estimatedDurationPerRound > 0) n * 1000.0 / estimatedDurationPerRound else 0.0

    return PerfResult(suite, name, dispLabel, n, delayMs, totalOps, 0, simpleThroughput, avg, p99, memDelta)
}

/** 原生虚拟线程 IO 测试 */
fun runNativeVTTest(suite: String, name: String, n: Int, delayMs: Long): PerfResult {
    val latencies = ConcurrentLinkedQueue<Long>()
    val rounds = if (n > 2000) 2 else 5
    var totalOps = 0L

    val executor = Executors.newVirtualThreadPerTaskExecutor()
    // 预热
    (1..n).map { executor.submit { Thread.sleep(delayMs) } }.forEach { it.get() }

    val (_, memDelta) = measureMemoryDelta {
        repeat(rounds) {
            val futures = (1..n).map {
                executor.submit {
                    val t0 = System.nanoTime()
                    Thread.sleep(delayMs)
                    latencies.add((System.nanoTime() - t0) / 1_000_000)
                }
            }
            totalOps += futures.size
            futures.forEach { it.get() }
        }
    }
    executor.close()

    val sorted = latencies.toList().sorted()
    val p99 = if (sorted.isNotEmpty()) sorted[(sorted.size * 0.99).toInt().coerceAtMost(sorted.size - 1)].toDouble() else 0.0
    val avg = latencies.toList().average()
    val simpleThroughput = if (avg > 0) n * 1000.0 / avg else 0.0

    return PerfResult(suite, name, "原生VT", n, delayMs, totalOps, 0, simpleThroughput, avg, p99, memDelta)
}

// ============================================================
// 测试 3: 混合 CPU + IO (贴近真实业务)
// ============================================================

suspend fun benchmarkMixed() {
    println("=".times(60))
    println("测试 3: 混合 CPU+IO (模拟真实业务)")
    println("=".times(60))

    // 场景: 50% CPU计算 + 50% IO等待 (如: 处理请求 + 调外部服务 + 返回结果)
    data class MixedScenario(val label: String, val cpuMs: Long, val ioMs: Long, val concurrency: Int)

    val scenarios = listOf(
        MixedScenario("轻量API", 2, 5, 500),
        MixedScenario("普通业务", 10, 20, 1000),
        MixedScenario("数据处理+外部调用", 20, 100, 2000),
        MixedScenario("复杂计算+慢IO", 50, 500, 1000),
        MixedScenario("报表生成+批量IO", 100, 1000, 500),
    )

    println()
    println("%-20s | %5s/%5s | %6s | %-16s | %10s | %10s | %10s".format(
        "场景", "CPUms", "IOms", "N", "调度器", "吞吐量", "均延迟ms", "内存MB"
    ))
    println("-".times(100))

    for (scenario in scenarios) {
        val cpuMs = scenario.cpuMs
        val ioMs = scenario.ioMs
        val n = scenario.concurrency

        // IO (默认)
        val r1 = runMixedTest("混合负载", scenario.label, "IO(默认)", n, cpuMs, ioMs, Dispatchers.IO)
        allResults.add(r1)
        println("%-20s | %5d/%5d | %6d | %-16s | %10.0f | %10.1f | %10.2f".format(
            scenario.label, cpuMs, ioMs, n, "IO(默认)", r1.throughput, r1.avgLatencyMs, r1.memoryDeltaMB
        ))

        // IO (无限制)
        val ioUnlimited = Dispatchers.IO.limitedParallelism(Int.MAX_VALUE)
        val r2 = runMixedTest("混合负载", scenario.label, "IO(无限制)", n, cpuMs, ioMs, ioUnlimited)
        allResults.add(r2)
        println("%-20s | %5d/%5d | %6d | %-16s | %10.0f | %10.1f | %10.2f".format(
            scenario.label, cpuMs, ioMs, n, "IO(无限制)", r2.throughput, r2.avgLatencyMs, r2.memoryDeltaMB
        ))

        // VT-Dispatcher
        val vtDispatcher = Executors.newVirtualThreadPerTaskExecutor().asCoroutineDispatcher()
        val r3 = runMixedTest("混合负载", scenario.label, "VT-Dispatcher", n, cpuMs, ioMs, vtDispatcher)
        allResults.add(r3)
        println("%-20s | %5d/%5d | %6d | %-16s | %10.0f | %10.1f | %10.2f".format(
            scenario.label, cpuMs, ioMs, n, "VT-Dispatcher", r3.throughput, r3.avgLatencyMs, r3.memoryDeltaMB
        ))
        vtDispatcher.close()

        // 原生 VT
        val r4 = runNativeVTMixedTest("混合负载", scenario.label, n, cpuMs, ioMs)
        allResults.add(r4)
        println("%-20s | %5d/%5d | %6d | %-16s | %10.0f | %10.1f | %10.2f".format(
            scenario.label, cpuMs, ioMs, n, "原生VT", r4.throughput, r4.avgLatencyMs, r4.memoryDeltaMB
        ))
    }
    println()
}

/** 协程混合负载测试: CPU计算 + delay(IO) 交替 */
suspend fun runMixedTest(suite: String, name: String, dispLabel: String, n: Int, cpuMs: Long, ioMs: Long, dispatcher: CoroutineDispatcher): PerfResult {
    val latencies = ConcurrentLinkedQueue<Long>()
    val rounds = if (n > 1000) 3 else 5
    var totalOps = 0L

    // 预热
    coroutineScope {
        (1..n).map {
            launch(dispatcher) {
                cpuWork(cpuMs / 2)
                delay(ioMs / 2)
            }
        }.joinAll()
    }

    val (_, memDelta) = measureMemoryDelta {
        runBlocking {
            repeat(rounds) {
                coroutineScope {
                    val jobs = (1..n).map {
                        launch(dispatcher) {
                            val t0 = System.nanoTime()
                            // 混合工作: CPU计算 → IO等待 → CPU计算
                            cpuWork(cpuMs)
                            delay(ioMs)
                            cpuWork(cpuMs / 3)
                            latencies.add((System.nanoTime() - t0) / 1_000_000)
                        }
                    }
                    totalOps += jobs.size
                    jobs.joinAll()
                }
            }
        }
    }

    val sorted = latencies.toList().sorted()
    val p99 = if (sorted.isNotEmpty()) sorted[(sorted.size * 0.99).toInt().coerceAtMost(sorted.size - 1)].toDouble() else 0.0
    val avg = latencies.toList().average()
    val throughput = if (avg > 0) n * 1000.0 / avg else 0.0

    return PerfResult(suite, name, dispLabel, n, cpuMs + ioMs, totalOps, 0, throughput, avg, p99, memDelta)
}

/** 原生虚拟线程混合负载测试 */
fun runNativeVTMixedTest(suite: String, name: String, n: Int, cpuMs: Long, ioMs: Long): PerfResult {
    val latencies = ConcurrentLinkedQueue<Long>()
    val rounds = if (n > 1000) 3 else 5
    var totalOps = 0L

    val executor = Executors.newVirtualThreadPerTaskExecutor()
    // 预热
    (1..n).map {
        executor.submit {
            cpuWork(cpuMs / 2)
            Thread.sleep(ioMs / 2)
        }
    }.forEach { it.get() }

    val (_, memDelta) = measureMemoryDelta {
        repeat(rounds) {
            val futures = (1..n).map {
                executor.submit {
                    val t0 = System.nanoTime()
                    cpuWork(cpuMs)
                    Thread.sleep(ioMs)
                    cpuWork(cpuMs / 3)
                    latencies.add((System.nanoTime() - t0) / 1_000_000)
                }
            }
            totalOps += futures.size
            futures.forEach { it.get() }
        }
    }
    executor.close()

    val sorted = latencies.toList().sorted()
    val p99 = if (sorted.isNotEmpty()) sorted[(sorted.size * 0.99).toInt().coerceAtMost(sorted.size - 1)].toDouble() else 0.0
    val avg = latencies.toList().average()
    val throughput = if (avg > 0) n * 1000.0 / avg else 0.0

    return PerfResult(suite, name, "原生VT", n, cpuMs + ioMs, totalOps, 0, throughput, avg, p99, memDelta)
}

// ============================================================
// 测试 4: 内存开销对比
// ============================================================

suspend fun benchmarkMemoryOverhead() {
    println("=".times(60))
    println("测试 4: 内存开销对比 (大量并发任务)")
    println("=".times(60))

    val levels = listOf(100, 1000, 5000, 10000)

    println("%-8s | %-16s | %18s | %18s".format("任务数", "调度器", "内存增量MB", "单任务估算KB"))
    println("-".times(70))

    for (count in levels) {
        // 协程 IO
        val (_, coMem) = measureMemoryDelta {
            runBlocking {
                coroutineScope {
                    (1..count).map { launch(Dispatchers.IO) { delay(1) } }.joinAll()
                }
            }
        }
        println("%-8d | %-16s | %18.2f | %18.2f".format(count, "IO协程", coMem, coMem * 1024 / count))

        // 协程 Default
        val (_, defMem) = measureMemoryDelta {
            runBlocking {
                coroutineScope {
                    (1..count).map { launch(Dispatchers.Default) { delay(1) } }.joinAll()
                }
            }
        }
        println("%-8d | %-16s | %18.2f | %18.2f".format(count, "Default协程", defMem, defMem * 1024 / count))

        // 虚拟线程 (原生)
        val (_, vtMem) = measureMemoryDelta {
            val executor = Executors.newVirtualThreadPerTaskExecutor()
            val futures = (1..count).map { executor.submit { Thread.sleep(1) } }
            futures.forEach { it.get() }
            executor.close()
        }
        println("%-8d | %-16s | %18.2f | %18.2f".format(count, "虚拟线程", vtMem, vtMem * 1024 / count))

        // 平台线程 (少量,因为开销大)
        if (count <= 1000) {
            val (_, ptMem) = measureMemoryDelta {
                val executor = Executors.newCachedThreadPool()
                val futures = (1..count).map { executor.submit { Thread.sleep(1) } }
                futures.forEach { it.get() }
                executor.shutdown()
            }
            println("%-8d | %-16s | %18.2f | %18.2f".format(count, "平台线程池", ptMem, ptMem * 1024 / count))
        } else {
            println("%-8d | %-16s | %18s | %18s".format(count, "平台线程池", "(跳过-资源过大)", "-"))
        }
    }
    println()
}

// ============================================================
// 测试 5: 调度/创建开销
// ============================================================

fun benchmarkSchedulingOverhead() {
    println("=".times(60))
    println("测试 5: 任务创建与调度开销")
    println("=".times(60))

    val iterations = 10_000
    val warmup = 1000

    data class OverheadResult(val label: String, val totalNs: Long, val count: Int) {
        val avgNs: Double get() = totalNs.toDouble() / count
    }

    val results = mutableListOf<OverheadResult>()

    // 1. 协程 launch + join (最小开销)
    runBlocking {
        repeat(warmup) { launch(Dispatchers.Default) { }.join() }
        val t = measureNanoTime {
            repeat(iterations) { launch(Dispatchers.Default) { }.join() }
        }
        results.add(OverheadResult("协程launch+join(Default)", t, iterations))
    }

    // 2. 协程 launch + delay(0) + join
    runBlocking {
        repeat(warmup / 10) { launch(Dispatchers.IO) { delay(0) }.join() }
        val t = measureNanoTime {
            repeat(iterations / 10) { launch(Dispatchers.IO) { delay(0) }.join() }
        }
        results.add(OverheadResult("协程delay(0)+IO调度", t, iterations / 10))
    }

    // 3. 原生虚拟线程 create+start+join
    repeat(warmup) { Thread.ofVirtual().start { }.join() }
    val vtCreate = measureNanoTime {
        repeat(iterations) { Thread.ofVirtual().start { }.join() }
    }
    results.add(OverheadResult("虚拟线程create+join", vtCreate, iterations))

    // 4. 虚拟线程 Executor submit+get
    val vtExecutor = Executors.newVirtualThreadPerTaskExecutor()
    repeat(warmup) { vtExecutor.submit { }.get() }
    val vtSubmit = measureNanoTime {
        repeat(iterations) { vtExecutor.submit { }.get() }
    }
    results.add(OverheadResult("虚拟线程submit+get", vtSubmit, iterations))
    vtExecutor.close()

    // 5. 平台线程 create+start+join
    repeat(warmup / 100) { val t = Thread { }; t.start(); t.join() }
    val ptCreate = measureNanoTime {
        repeat(iterations / 100) { val t = Thread { }; t.start(); t.join() }
    }
    results.add(OverheadResult("平台线程create+join", ptCreate, iterations / 100))

    // 6. 平台线程池 submit+get
    val ptExecutor = Executors.newCachedThreadPool()
    repeat(warmup / 10) { ptExecutor.submit { }.get() }
    val ptSubmit = measureNanoTime {
        repeat(iterations / 10) { ptExecutor.submit { }.get() }
    }
    results.add(OverheadResult("平台线程池submit+get", ptSubmit, iterations / 10))
    ptExecutor.shutdown()

    println("%-30s | %10s | %12s".format("操作", "平均延迟", "相对比"))
    println("-".times(60))
    val baseline = results.first().avgNs
    for (r in results) {
        val ratio = r.avgNs / baseline
        val unit = if (r.avgNs > 10_000) "μs" else "ns"
        val display = if (r.avgNs > 10_000) r.avgNs / 1000 else r.avgNs
        println("%-30s | %8.1f %-3s | %10.1fx".format(r.label, display, unit, ratio))
    }
    println()
}

// ============================================================
// 测试 6: 线程池并发限制影响分析
// ============================================================

suspend fun benchmarkThreadPoolLimits() {
    println("=".times(60))
    println("测试 6: Dispatchers.IO 线程池限制影响分析")
    println("=".times(60))
    println("  说明: Dispatchers.IO 默认并行度 = max(64, CPU核数)")
    println("  本测试对比不同并行度限制下的表现")
    println()

    val ioDelays = listOf(5L, 50L, 200L) // ms
    val concurrency = 5000

    val limits = listOf(64, 128, 256, 512, 1024, Int.MAX_VALUE)

    println("%-6s | %-12s | %12s | %12s | %12s".format("IOms", "并行度限制", "吞吐量ops/s", "均延迟ms", "P99延迟ms"))
    println("-".times(70))

    for (delayMs in ioDelays) {
        for (limit in limits) {
            val dispatcher = Dispatchers.IO.limitedParallelism(limit)
            val label = if (limit == Int.MAX_VALUE) "无限制" else "$limit"

            val latencies = ConcurrentLinkedQueue<Long>()
            // 预热
            coroutineScope { (1..concurrency).map { launch(dispatcher) { delay(delayMs) } }.joinAll() }

            coroutineScope {
                val jobs = (1..concurrency).map {
                    launch(dispatcher) {
                        val t0 = System.nanoTime()
                        delay(delayMs)
                        latencies.add((System.nanoTime() - t0) / 1_000_000)
                    }
                }
                jobs.joinAll()
            }

            val sorted = latencies.toList().sorted()
            val p99 = if (sorted.isNotEmpty()) sorted[(sorted.size * 0.99).toInt().coerceAtMost(sorted.size - 1)].toDouble() else 0.0
            val avg = latencies.toList().average()
            val throughput = if (avg > 0) concurrency * 1000.0 / avg else 0.0

            println("%-6d | %-12s | %12.0f | %12.1f | %12.1f".format(delayMs, label, throughput, avg, p99))
        }
        println()
    }
    println()
}

// ============================================================
// 生成测试报告
// ============================================================

fun generateReport() {
    val sep = "=".times(60)
    println(sep)
    println("测试报告")
    println(sep)

    // === 按测试套汇总 ===
    val suites = allResults.groupBy { it.testSuite }
    for ((suite, results) in suites) {
        println("\n## $suite")
        println()

        val byDispatcher = results.groupBy { it.dispatcher }
        val dispatchers = byDispatcher.keys.sorted()

        // 表头
        print("%-20s".format("测试"))
        for (d in dispatchers) {
            print(" | %12s".format(d.take(12)))
        }
        println()
        print("-".times(20))
        for (d in dispatchers) print(" | ${"-".times(12)}")
        println()

        val byTest = results.groupBy { "${it.concurrency}@${it.workMs}ms" to it.testName }
        for ((key, group) in byTest) {
            print("%-20s".format(group.first().testName.take(20)))
            for (d in dispatchers) {
                val r = group.find { it.dispatcher == d }
                if (r != null) {
                    print(" | %12.0f".format(r.throughput))
                } else {
                    print(" | %12s".format("-"))
                }
            }
            println()
        }
    }

    // === 综合排名 ===
    println("\n## 综合排名 (按场景-吞吐量)")
    println()
    val ranked = allResults.sortedByDescending { it.throughput }
    println("%-4s | %-20s | %-16s | %8s | %8s | %10s | %10s".format(
        "排名", "场景", "调度器", "并发", "工作ms", "吞吐量", "延迟ms"
    ))
    println("-".times(85))
    ranked.take(30).forEachIndexed { idx, r ->
        println("%-4d | %-20s | %-16s | %8d | %8d | %10.0f | %10.1f".format(
            idx + 1, r.testName.take(20), r.dispatcher, r.concurrency, r.workMs, r.throughput, r.avgLatencyMs
        ))
    }

    // === 调度器全局对比 ===
    println("\n## 调度器全局对比 (所有场景的调和均值)")
    println()
    val globalByDispatcher = allResults.groupBy { it.dispatcher }.mapValues { (_, list) ->
        val valid = list.filter { it.throughput > 0 }
        if (valid.isNotEmpty()) valid.map { it.throughput }.average() else 0.0
    }
    globalByDispatcher.entries.sortedByDescending { it.value }.forEach { (disp, avg) ->
        println("  $disp: 平均吞吐量 ${"%.0f".format(avg)} ops/s")
    }
}

// ============================================================
// 主入口
// ============================================================

fun main() = runBlocking {
    val cpuCores = Runtime.getRuntime().availableProcessors()
    val javaVersion = System.getProperty("java.version")

    println("╔══════════════════════════════════════════════════════════════╗")
    println("║  虚拟线程 / 协程 / 平台线程 全面性能对比基准测试               ║")
    println("║  JDK: $javaVersion  |  CPU: ${cpuCores}核  |  OS: ${System.getProperty("os.name")} ║")
    println("╚══════════════════════════════════════════════════════════════╝")
    println()
    println("测试调度器:")
    println("  - IO(默认):     Dispatchers.IO (max(64, CPU核)并行)")
    println("  - IO(无限制):   Dispatchers.IO.limitedParallelism(MAX)")
    println("  - Default:      Dispatchers.Default (CPU核数个线程)")
    println("  - VT-Dispatcher: Dispatchers.Virtual (虚拟线程调度器)")
    println("  - 原生VT:        Thread.ofVirtual() (非协程)")
    println("  - 平台线程:      传统 Thread / CachedThreadPool")
    println()

    try {
        benchmarkSchedulingOverhead()
        benchmarkMemoryOverhead()
        benchmarkThreadPoolLimits()
        benchmarkCpuIntensive()
        benchmarkIOIntensive()
        benchmarkMixed()

        // 生成测试报告
        generateReport()

    } catch (e: Exception) {
        println("测试异常: ${e.message}")
        e.printStackTrace()
    }
}
  • 工具方法
kotlin 复制代码
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asCoroutineDispatcher
import java.util.concurrent.Executors

/**
 * 虚拟线程与协程工具类
 * 提供虚拟线程创建、平台线程创建、以及虚拟线程调度器扩展
 *
 * @author : zimo
 * @date : 2026/06/24
 */

/**
 * 使用虚拟线程执行回调
 * 注意: 虚拟线程在阻塞时自动卸载载体线程,适合长IO高并发场景
 */
fun virtual(callback: () -> Unit): Unit {
    Thread.ofVirtual().start(callback)
}

/**
 * 使用平台线程执行回调
 * 注意: 平台线程阻塞时会占用内核资源,不适合大量阻塞场景
 */
fun thread(callback: () -> Unit): Unit {
    Thread(callback).start()
}

/**
 * 虚拟线程调度器扩展属性
 * 将虚拟线程 Executor 转换为协程调度器,实现协程体系下的虚拟线程调度
 */
val Dispatchers.Virtual get() = Executors.newVirtualThreadPerTaskExecutor().asCoroutineDispatcher()