bigywater.tech
Rust · 126

Monomorphization 生成的代码

“泛型会生成多份代码”具体是什么样?每份有什么不同?什么情况下会被合并?体积到底涨多少?这篇让 rustc 列出单态化实例,读汇编,再量目标文件大小。

9 个
这个小例子里的单态化实例总数
4 份
largest 的实例数(每个类型一份)
11,488 对 11,208
16 种类型的泛型版与 &dyn 版目标文件字节数

实例列表

实测 · 本机执行输出
  MONO_ITEM fn largest::<char> @@ u126a.2d40a2df0f3cf73d-cgu.0[External]
  MONO_ITEM fn largest::<f64> @@ u126a.2d40a2df0f3cf73d-cgu.0[External]
  MONO_ITEM fn largest::<i32> @@ u126a.2d40a2df0f3cf73d-cgu.0[External]
  MONO_ITEM fn largest::<u8> @@ u126a.2d40a2df0f3cf73d-cgu.0[External]
  MONO_ITEM fn std::cmp::impls::<impl std::cmp::PartialOrd for char>::gt @@ u126a.2d40a2df0f3cf73d-cgu.0[Internal]
  MONO_ITEM fn std::cmp::impls::<impl std::cmp::PartialOrd for f64>::gt @@ u126a.2d40a2df0f3cf73d-cgu.0[Internal]
  MONO_ITEM fn std::cmp::impls::<impl std::cmp::PartialOrd for i32>::gt @@ u126a.2d40a2df0f3cf73d-cgu.0[Internal]
  MONO_ITEM fn std::cmp::impls::<impl std::cmp::PartialOrd for u8>::gt @@ u126a.2d40a2df0f3cf73d-cgu.0[Internal]
  MONO_ITEM fn use_all @@ u126a.2d40a2df0f3cf73d-cgu.0[External]
  共 9 个单态化实例:largest 被实例化了 4 次(i32、f64、u8、char 各一份)

单态化的基础见泛型单态化;输出使用 RUSTC_BOOTSTRAP=1 -Z print-mono-items。

每个实例的机器码

实测 · 本机执行输出
  largest::<f64>(3 条指令):fcmp d0, d1 ; fcsel d0, d0, d1, gt ; ret
  largest::<i32>(3 条指令):cmp w0, w1 ; csel w0, w0, w1, gt ; ret
  largest::<u32>(3 条指令):cmp w0, w1 ; csel w0, w0, w1, hi ; ret
  汇编里 largest 的符号:3 个(i32、f64、u32 各一份;u32 与 i32 比较指令不同,所以没有被合并)

相同代码被合并

实测 · 本机执行输出
  汇编里别名(_x = _y):[('__RINvCs5c6pjRSpYZC_5u126c8add_pairmEB2_', '__RINvCs5c6pjRSpYZC_5u126c8add_pairlEB2_'), ('_add_u32', '_add_i32')]
  实际定义的函数标签:['__RINvCs5c6pjRSpYZC_5u126c8add_pairfEB2_', '__RINvCs5c6pjRSpYZC_5u126c8add_pairlEB2_', '_add_f32', '_add_i32']

函数合并是 LLVM 的优化,在 -O 下发生。

代码体积

实测 · 本机执行输出
     实例数        泛型版 .o 字节     &dyn 版 .o 字节
       1            1,552            2,032
       4            3,768            4,104
      16           11,488           11,208

静态与动态分发的耗时对比见静态分发与动态分发性能对比。

分步演示

单态化生成的代码
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动。汇编为 aarch64
    • 没有验证:share-generics 在 debug 下跨 crate 复用实例、更大的泛型函数体(膨胀会更明显)、codegen-units 对合并的影响、x86_64 上的指令选择、-C link-dead-code 与 GC sections 对最终二进制的影响
    京ICP备2026067502号-1京公网安备11010502063487号