Go vs Nim 性能实测(三):6 种 GC 模式全景对比

系列文章 (一)6 项常规算法对决 · (二)大数运算与 GMP 四端 · (三)6 种 GC 模式全景对比(本篇)

前两篇讲了常规算法与大数运算,这一篇是系列最后一块拼图:内存管理。Nim 最大的特色之一就是 GC 可选——同一份代码可以用 6 种不同的内存管理策略编译。加上 Go 的 GC,正好做一场全景对比。

测试设计

同一份 Nim 基准代码分别以 --mm:refc / markAndSweep / arc / orc / boehm / atomicArc 编译(--mm:go 本机缺 libgo.dll 不可运行、--mm:none 无 GC 不可比),与 Go 1.26.7 自带 GC 对照。7 项负载覆盖小/大对象分配、分配-释放交替、二叉树、循环引用、字符串构建、seq 增长;7 个实现校验和完全一致。

GC 基准对比 GC 基准对比

基准Gorefcmark&sweeparcorcboehmatomicArc
allocSmall3M(300 万小对象)3.071.659.8113.2124.081.5113.4
allocBig1500(1500 个 1 MiB)97.533.4132.618.518.3475.518.8
churn4M(400 万分配-释放)108.8147.185.2194.0200.964.9196.8
treeBuild200(200 棵满二叉树)521.4709.1564.7632.8688.0301.1647.7
cycleRefs300(300 个循环链表)14.215.612.612.2*46.48.816.6
strBuild20k(2 万次字符串拼接)117.339.5237.231.632.23311.732.7
seqGrowth10M(1000 万次 push)102.6100.799.0189.7190.6120.2193.0
7 项合计965111711911192130043641219

* arc 不回收循环引用,cycleRefs 的"快"是以内存泄漏为代价;orc 有循环收集器(比 arc 慢 3.8×),追踪式全部正常回收。

读这张表的关键:没有绝对赢家

7 项负载的"最快"分布在 5 个实现上:

  • 追踪式(refc / m&s / boehm / Go):分配便宜、回收周期性;引用计数(arc/orc/atomicArc):每对象即时增减引用并释放,短生命周期对象反而更贵。
  • 高频小对象分配:Go 3.0 ms 遥遥领先(比 arc 快约 38×)。在 Nim 里避免用 arc/orc 做小对象流水。
  • 大对象 / 字符串构建:arc/orc/atomicArc 最快(18 / 32 ms);boehm 是灾难(475 / 3312 ms,字符串拼接被慢到离谱)。
  • 指针结构(树、循环、交替释放):boehm 三项全场第一(301 / 8.8 / 64.9 ms),Go 次之。
  • seq 增长:refc / m&s / Go ≈ 99~103 ms;arc 系约 190 ms(扩容需搬移并维护引用计数)。

按负载选型的一句话总结:高频小对象 → Go/refc;大块内存与字符串流 → arc/orc;大量指针结构 → boehm/Go;循环引用 → Go/orc/refc(arc 泄漏勿用);seq 频繁增长 → m&s/Go;综合均衡 → orc(Nim 2.x 默认)

系列总结与选型建议

把三篇五轮测试放一起,可以给出相当务实的结论:

场景推荐理由
常规计算密集算法Nim / Go 皆可6 项合计仅差 3.5%,按生态选
高频小对象分配Go比 Nim 最快方案还快一个数量级
大数乘法/模幂(自己写)Go math/big深度优化碾压生态库;Nim 需自研算法级优化才能接近
大数模幂(可引 GMP)Nim+GMP FFI / CFFI 开销≈0,GMP powm 快 58%
除法/开方密集(π 类)GMP比 math/big 快 7.8~11.3×,开方段 91×
高性能 FFI 调用Nim dynlibcgo 在百万次小调用场景慢 45%
默认 GC 选择Nim 用 orc;高频小对象换 refc无全局最优

一句话总结:常规负载 Go 与 Nim 打平;一旦进入大数、FFI 调用模式、GC 形态这些"深水区",差距就不是语言本身的锅,而是运行库与算法选择的锅——Go 的 math/big 和 GC 打磨得深,Nim 的 ARC/FFI 和 C 后端也有自己的主场。

局限说明

  • 单机单测:i9-11950H + Windows 10,其他平台/版本结果可能不同。
  • 单线程对比,未测多核、并发与内存峰值。
  • GC 负载结果与 Nim 版本强相关(Nim 2.x 各 –mm 仍在演进),升级大版本后建议重测。
  • 测试工程位于本机 benchmark/nimgolang,复现脚本见第一篇。