NetWasm 0.10.0 使 C# 在 WebAssembly 体积上超越 Rust

C# 现在在 WebAssembly 体积上超越 Rust

要点: NetWasm 0.10.0 为简单的 print(42) 程序生成了 47,895 字节的独立 WASI 组件,比 Rust 1.95.0 的 size-profile 二进制文件(53,635 字节)小 10.7%,在 Brotli-11 压缩后小 19.5%。

原始和压缩体积对比

结果: NetWasm 的 Compact GC 构建原始体积为 47,895 字节,压缩后为 13,660 字节;Rust 的 size-profile 构建原始体积为 53,635 字节,压缩后为 16,963 字节。

工具链 原始 Wasm Brotli-11 角色
NetWasm, Compact GC 47,895 B 13,660 B 内置精确 GC 的 WASI Preview 2 组件
Rust 1.95.0, size profile 53,635 B 16,963 B 匹配的 WASI Preview 2 组件
Kotlin 2.4.0 wasmWasi 79,293 B 22,655 B Preview 1;宿主提供 GC
TinyGo 0.39.0 93,043 B 26,931 B Preview 1;自定义 GC

两个二进制文件均使用默认发布设置生成,且没有手动后处理。NetWasm 的编译器流水线始终运行 Binaryen 的 wasm-opt,而 Rust 的构建不包含等效步骤,因为组件格式使重新编码变得复杂。

方法论说明

  • Rust 构建使用 opt-level = "z"、LTO、单一 codegen 单元、panic = "abort" 和剥离符号。
  • NetWasm 的体积包括其运行时和精确的 Compact 垃圾收集器。
  • 基准测试反映的是单个 print(42) 工作负载,而非语言级别的体积排名。
  • 两种工具链都可以通过 no_std(Rust)或零库方法(C#)进一步缩小,但这些配置会牺牲大部分标准库,对于 C# 还会牺牲垃圾收集器。

确切的来源、版本、设置和复现步骤

相对于 Mono AOT 的性能优势

要点: NetWasm 的分配和 GC 基准测试比从相同 C# 源码编译并在 Wasmtime 下执行的 Mono AOT 快 26-40%。

工作负载 NetWasm (Compact GC) Mono AOT (.NET 10) 速度提升
完整事务 43.9 ms 59.1 ms 快 26%
保留对象图 8.8 ms 14.2 ms 快 38%
UI 频繁更新 17.6 ms 29.1 ms 快 40%

测量结果是 Apple M1 Max 上使用 Wasmtime 49.0.3 运行 15 次的中位数。NetWasm 在前两个工作负载上也优于桌面 .NET 11 JIT,尽管 JIT 在 UI 频繁更新上略快。Native AOT 整体上仍然最快。

版本、设置、桌面数据和复现

超越 hello-world 的功能集

要点: NetWasm 支持功能完整的 C# 15 应用程序、JavaScript 互操作、静态 .a 归档、WIT 绑定、表达式树、HTTP/JSON/XML、无反射 DI,以及 wasm32 和 wasm64 目标。

  • 通过标准 dotnet CLI 构建、运行、发布和测试。
  • 为浏览器或 Web Workers 导出类型化的 JavaScript 模块。
  • 链接带有生成的 C ABI 封送处理的本地 .a 归档。
  • 从 WIT 生成 C# 绑定,并生成独立的 WASI Preview 2 组件。
  • 使用为 NetWasm 打包的 LINQ、HTTP、JSON、XML、Regex 和 DI 库。
  • 为 netwasm0.1 和桌面 .NET 提供双目标库。

该配置文件有意比桌面 .NET 小,但它已经支持完整的应用程序、可移植库、测试项目和浏览器模块。

快速入门指南

要点: 安装 .NET 10+ SDK 和 NetWasm 模板,即可在一分钟内编译并运行一个打印 42 的自包含 C# 程序。

# 安装 .NET SDK 10.0.303+(或 .NET 11 以支持 C# 15)
# 安装 NetWasm 模板

dotnet new install NetWasm.Templates

dotnet new netwasm-app -n HelloNetWasm
cd HelloNetWasm

dotnet restore

dotnet run               # 打印 42

dotnet publish -c Release -o publish/local

# 使用 WASI Preview 2 宿主(例如 Wasmtime)运行
wasmtime run publish/local/HelloNetWasm.wasm

发布的组件相对于 .NET 是自包含的;运行时只需要一个 WASI 宿主。“使用 .NET 构建;无需 .NET 运行。”

NetWasm 如何保持小巧

要点: NetWasm 通过将可达的 CIL 直接编译为 Wasm、执行闭世界分析,并仅链接实际使用的运行时功能来实现其体积优势。

  • 闭世界编译: 省略不可达代码、导入和运行时功能。
  • 精简平台契约: 仅提供可移植 API;排除桌面运行时假设。
  • 无广泛反射: 省略 dynamic、运行时程序集加载和通用类型检查等功能。
  • 生成的边界: JSON、DI、WIT 和本地互操作使用静态契约而非运行时发现。
  • 可选数据: 时区数据、丰富的堆栈跟踪和诊断仅在显式启用时包含。
  • 单一收集器: 仅将选定的 GC 策略链接到最终二进制文件中。

这是一个专门构建的 .NET 平台,而不是被强行塞入 Wasm 沙箱的简化桌面运行时。

分配策略和体积权衡

要点: NetWasm 提供三种垃圾收集器策略,每种策略具有不同的体积和性能特征。

策略 最佳适用场景 原始组件 Brotli-11
Compact(默认) 小型应用、低频繁更新 UI 47,895 B 13,660 B
NetWasmGC 更高的分配吞吐量 50,855 B 15,053 B
Bump 有界、一次性任务 43,864 B 11,959 B

通过在项目文件中添加 <NetWasmGarbageCollector> 来选择策略;省略该属性则默认为 Compact。

库兼容性模型

要点: NetWasm 库多目标定位到 netwasm0.1 和桌面框架(例如 net10.0),使同一源代码可以在 Wasm 和传统 .NET 上运行。

# 创建一个同时针对两个平台的库

dotnet new netwasm-lib -n MyLibrary

生成的 NuGet 包包含两个运行时的资产,但只有符合 NetWasm 配置文件的库才能无需修改即可使用。

与传统 Blazor WebAssembly 的比较

要点: 打印 42 的 Blazor WebAssembly AOT 构建占用约 12 MiB(.NET 10)或约 15 MiB(.NET 11),使 NetWasm 大约大 256-319 倍。

构建 未压缩 Wasm 相对于 NetWasm 的体积因子
NetWasm, Compact GC 47,895 B 1×
Blazor WebAssembly, .NET 10 12,276,684 B 256×
Blazor WebAssembly, .NET 11 RC1 15,289,241 B 319×

Blazor 的数字包括本地运行时和必需的程序集;NetWasm 包括其运行时、GC 和 WASI 组件。此比较是控制台组件与空 UI 的 Blazor 应用,而非完整框架的等效比较。

确切的 Blazor 版本、设置和复现

社区反馈亮点

“这有点不公平的比较,这里基准测试的 C# 代码也经过了后处理,而其他目标都没有。…… Rust 可以做得更小得多。” – @sondr3

“对我来说,C# 在后端方面就是感觉非常合适……良好的工具、现代语言特性、出色的调试体验。” – @CharlieDigital

“值得指出的是,这不是开源的,如果你在比较两者时这很重要的话(对我来说很重要)。” – @Scaled

“这有点不公平的比较,因为 WASM 自带垃圾收集器,C# 可以使用,而 Rust 自己管理所有内存,当然会更大。” – @weinzierl(后来阅读帖子后更正)

“不难…… Rust wasm 二进制文件巨大。如果你关心 WASM 性能、体积和内存使用,Zig 是更好的候选。” – @jedisct1

这些评论说明了主要的争议点:基准测试的公平性、开源许可,以及垃圾收集器开销的相关性。

当前状态和路线图

要点: NetWasm 处于 1.0 之前;0.10.0 版本引入了重建的垃圾收集器和面向体积的优化。未来的工作目标是更广泛的 WASI Preview 2 覆盖、可选的全球化数据、完整的表达式树支持、稳定的 netwasm1.0 契约和生产级诊断。

  • 0.10.0 亮点: 对象上无收集器头、单分配数组、移除 Boehm 收集器、更快的分配密集型程序、减少时区数据的启动时间。
  • 通往 1.0 之路: 扩展 WASI Preview 2 API 表面、启用更多 .NET Standard 2.1 库、稳定平台和运行时契约、改进增量开发循环。

该项目仍然欢迎贡献;问题和功能请求均受欢迎。

许可模式

要点: NetWasm 的核心库采用 MIT 许可;编译器和工具使用 NetWasm Community License 1.0,该许可对个人、教育、开源工作和小型组织(≤250 名员工,≤1000 万美元收入)免费。超出这些限制的商业使用需要单独协议。

  • 运行时收集器(Compact、NetWasmGC)采用 Apache-2.0。
  • 生成的应用程序中没有内置激活、遥测或技术强制。

包和生态系统

  • 编译器和 SDK:NetWasm.Compiler.Tasks、NetWasm.Sdk、NetWasm.Templates
  • 运行时:NetWasm.Runtime.Wasm32、NetWasm.Runtime.Wasm64
  • 托管:NetWasm.Hosting、NetWasm.Hosting.Build
  • 库移植:NetWasm.Libraries 套件、TUnit-NetWasm

所有包均可从 NuGet 获取,并在仓库中有文档说明。


Sources