C# 在 WASM 体积上已超越 Rust

Hacker News Top 工具

摘要

NetWasm 是一个独立的编译器、CoreLib、运行时和 SDK,可将 C# 和 .NET CIL 直接编译为 WebAssembly,生成的二进制文件比体积优化后的 Rust 更小(47,895 字节对 53,635 字节),同时在 GC 基准测试中优于 Mono AOT。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/10/21 09:18

zion-sati/NetWasm 来源:https://github.com/zion-sati/NetWasm

NetWasm

面向 WebAssembly 和 WASI 的独立 .NET。

用 C# 编写,部署小巧的独立 Wasm。NetWasm 是一个独立的编译器、CoreLib、运行时和 SDK,可将 C# 和 .NET CIL 直接编译为 WebAssembly。它不会将桌面 CLR、Mono、dotnet.js 或应用程序 DLL 带入生产环境。

在 GitHub 上为 NetWasm 点 Star(https://github.com/zion-sati/NetWasm)

发布(https://github.com/zion-sati/NetWasm/actions/workflows/release.yml)

许可证:Community + MIT

将 C# 编译为 WebAssembly(https://www.netwasm.com/compile-csharp-to-webassembly/) · 在浏览器中试用(https://playground.netwasm.com/) · 安装并构建 · 查看支持的 API · 阅读文档

C# 在 Wasm 体积上已超越 Rust

NetWasm + 精确 GC:47,895 字节

Rust 1.95.0 体积优化配置:53,635 字节

在相同的打印 42 工作负载下,NetWasm 的结果包含了运行时和精确的 Compact 垃圾收集器。两个产物均为采用体积优化设置构建的独立 WASI Preview 2 组件。

原始体积小 10.7%。Brotli-11 压缩后小 19.5%。

Wasm 体积对比图,包含原始和 Brotli-11 尺寸。NetWasm(精确 GC)原始 47,895 字节,压缩后 13,660 字节;Rust 为 53,635 和 16,963;Kotlin/Wasm 为 79,293 和 22,655;TinyGo 为 93,043 和 26,931。NetWasm 与 Rust 为同一 WASI Preview 2 的对比;Kotlin 和 TinyGo 为 Preview 1 的参考上下文。

工具链原始 WasmBrotli 11对比角色
NetWasm,Compact GC47,895 B13,660 BWASI P2 组件;含精确 GC
Rust 1.95.0,体积配置53,635 B16,963 B同条件 WASI P2 组件参考
Kotlin 2.4.0 wasmWasi79,293 B22,655 BP1 上下文;GC 由宿主引擎提供
TinyGo 0.39.093,043 B26,931 BP1 上下文;已配置精确 GC

Rust 是主要对比对象,因为它使用相同的工作负载和输出形式。Kotlin/Wasm 和 TinyGo 展示了更广泛的体积格局,但它们的编译目标和运行时边界有所不同。这是针对特定工作负载/配置的实测结果,而非全语言范围的体积排名。

精确来源、版本、设置与复现步骤

比 Mono AOT 更快

NetWasm 的分配和垃圾收集基准测试运行速度比 Mono AOT 更快,两者由相同的 C# 源码编译,并由相同的 Wasmtime 运行:

工作负载NetWasm,NetWasmGCMono AOT,.NET 10NetWasm 耗时
完整事务43.9 ms59.1 ms少 26%
持有对象图8.8 ms14.2 ms少 38%
UI churn17.6 ms29.1 ms少 40%

以上数据为 Apple M1 Max 上 Wasmtime 49.0.3 下 15 次运行的中位数,每次运行均与桌面 .NET 的结果进行了核对。Mono AOT 指 Microsoft 的 wasi-wasm 目标,默认 Release 配置。NetWasmGC 在前两个工作负载上也比桌面 .NET 11 的 JIT 耗时更少(49.9 和 9.7 ms);JIT 在 UI churn 上更快(8.6 ms),而桌面 NativeAOT 在三项上均为最快。

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

不止 Hello World

46.7 KiB 的程序只是基线,而非边界。

C# 15 · JavaScript 模块和 Web Workers · 静态 .a 互操作 · WIT/WASI P2 · 表达式树 · HTTP/JSON/XML/Regex · 无反射 DI · TUnit/VSTest · wasm64 · 四种分配策略

目前 NetWasm 可以:

  • 通过常规 dotnet CLI 构建、运行、发布和测试项目;
  • 将 C# 导出发布为带类型的 JavaScript 模块,可在页面中或 Web Worker 中运行;
  • 链接兼容的 Wasm .a 归档,并生成受限的 C ABI 编组;
  • 从 WIT 生成 C# 绑定 并产出独立的 WASI P2 组件;
  • 无需通用运行时反射即可构造和检查表达式树;
  • 使用独立打包的 LINQ、HTTP、JSON、XML、Regex、哈希和依赖注入库;
  • 为 netwasm0.1 和桌面 .NET 双目标打包库。

该配置刻意比桌面 .NET 更小。它已经支持完整应用、可移植库、测试项目和浏览器模块。

试一试

安装 .NET SDK 10.0.303 或更高版本(https://dotnet.microsoft.com/download/dotnet/10.0),然后运行:

dotnet new install NetWasm.Templates
dotnet new netwasm-app -n HelloNetWasm
cd HelloNetWasm
dotnet restore
dotnet run
dotnet publish -c Release -o publish/local

使用兼容的 WASI Preview 2 宿主(如 Wasmtime(https://wasmtime.dev/))运行已发布的组件:

wasmtime run publish/local/HelloNetWasm.wasm
42

已发布的组件在 .NET 方面是自包含的,但仍需要一个 WebAssembly 宿主。

用 .NET 构建;无需 .NET 运行。

使用 C# 15 的项目将选择 .NET 11 SDK;较早的语言版本可以继续使用 .NET 10。SDK 通过 NuGet 还原其固定版本的 Node、LLD 和 Binaryen 工具,因此普通应用无需额外安装 Emscripten、Git 或 Python。

详见完整快速入门,了解宿主、还原、Windows、浏览器和双目标相关细节。

它如何保持小巧

Roslyn 产出 CIL。NetWasm 将可达程序直接编译为 Wasm,特化其泛型,并只链接它所需的运行时支持。链接后的 Wasm 组件就是程序本身。

  • 封闭世界编译。 不可达的代码、导入和运行时特性不会进入产物。
  • 更小的平台契约。 NetWasm 支持可移植 API,而非复刻每一个桌面运行时假设。
  • 没有通用运行时反射。 运行时类型名称、动态程序集加载和 dynamic 关键字不在该配置范围内。
  • 生成式边界。 JSON、DI、WIT 和原生互操作使用已知契约,而非运行时发现。
  • 可选数据和诊断。 时区数据、丰富堆栈跟踪及其他部署数据仅在被选中时才包含。
  • 单一收集器。 只链接所选的分配策略。

这是一个全新的 .NET 平台,而非被压缩进 Wasm 沙箱的桌面 .NET。正是这一边界使得原生 Wasm 体积级别的 C# 成为可能。

JSON 和可选特性如何影响体积

选择你的分配策略

Compact 是默认项:一个体积优先、精确、非移动的标记-清除收集器。它追踪编译器发布的根和引用布局,无需保守扫描。其名称指的是实现体积,而非移动堆压缩。

策略适用场景原始组件Brotli 11
Compact(默认)小型应用和低 churn UI47,895 B13,660 B
NetWasmGC更高分配吞吐量50,855 B15,053 B
Bump有界的一次性任务43,864 B11,959 B

在可执行项目的 .csproj 中选择策略:

<NetWasmGC>Bump</NetWasmGC>

不设置该属性即选择 Compact。库保持收集器无关;所有四种后端均已为 wasm32 和 wasm64 打包。

详见选择垃圾收集器了解权衡与诊断。

库应向上游走

为更小的 NetWasm 配置编写的库,也应当在桌面 .NET 上有用,而不是被困在仅限 Wasm 的生态中。

dotnet new netwasm-lib -n MyLibrary

该模板同时以 netwasm0.1 和 net10.0 为目标,为两个平台生成 NuGet 资产。这是显式的多目标,而非承诺任意桌面包可以反向使用。

NetWasm.Libraries(https://github.com/zion-sati/NetWasm.Libraries)提供已移植的框架和扩展包。

TUnit-NetWasm(https://github.com/zion-sati/TUnit-NetWasm)通过通用 VSTest 桥接提供常规的 dotnet test 集成。

与传统 .NET Wasm 相比如何?

针对一个打印 42 的空 UI Blazor WebAssembly 应用,启用 AOT、托管裁剪、IL 剥离、不变全球化和体积优先的原生链接后:

AOT Wasm 体积对比,越低越好:NetWasm(Compact GC)46.8 KiB,Blazor .NET 10 为 11.71 MiB,Blazor .NET 11 RC1 为 14.58 MiB。按精确字节计算,NetWasm 比 .NET 11 构建小约 319 倍。

AOT 构建未压缩 Wasm相对 NetWasm
NetWasm,Compact GC47,895 B1×
Blazor WebAssembly,.NET 1012,276,684 B256×
Blazor WebAssembly,.NET 11 RC115,289,241 B319×

Blazor 总量包含其原生运行时和必需的 Webcil 程序集;NetWasm 包含其运行时、垃圾收集器和 WASI 组件。这是一个控制台组件与空 UI 应用的对比,并非等价框架的比较,也不是对所有 .NET 工作负载的结论。

精确 Blazor 版本、设置与复现

当前边界

NetWasm 处于 1.0 之前阶段。其支持的配置今天即可使用,并有已发布的资格验证覆盖,但源码、包、目标配置和 ABI 契约可能在次要版本之间发生变化。常规的可移植输出是 wasm32 WASI Preview 2 组件。原始 wasm32、原始 wasm64 和 JavaScript 宿主集成是高级替代方案;wasm64 组件仍受上游工具链限制。

运行时反射、dynamic、运行时程序集加载和托管线程不在配置范围内。通用的 System.IO.File/Directory/FileStream、原始套接字和子进程 API 尚未实现。

现有的 NuGet 包仅在其可达代码和 API 依赖符合支持配置时才能工作。

支持的 API 与限制 · 文档索引

通往 1.0 之路

NetWasm 1.0 意味着一个有用、文档完善且稳定的小型平台。它不意味着“桌面 .NET,只不过在 Wasm 里“。主要待完成主题:

  • 更广泛的 WASI Preview 2 覆盖;
  • 全球化模式和可选文化数据;
  • 表达式树的完善;
  • 更快的增量开发循环;
  • 稳定的 netwasm1.0 平台和运行时契约;
  • 生产诊断和更广泛的宿主资格验证。

NetWasm 0.10.0 新特性: 重建的垃圾收集器。对象不再携带收集器头,数组为单次分配,NetWasmGC 通过推进指针进行分配,Compact 拥有为体积和内存而设计的全新核心。分配密集型程序运行更快且内存占用大幅降低,附带时区数据的应用启动时间缩短为原来的几分之一。Boehm 收集器已被移除。

发行说明

进行中: 与现有 .NET Standard 2.1 库二进制和 NuGet 资产的更广泛兼容。目标是在可达代码符合 NetWasm 配置时无需修改即可使用这些库,同时不将未使用的不支持路径带入应用。这不会把 NetWasm 变成桌面 .NET。可达的通用运行时反射、dynamic、运行时程序集加载、托管线程、不受支持的宿主 API 和命名文化全球化仍在初始兼容切片之外,会在编译时失败。

阅读完整的 1.0 路线图

软件包

编译器、SDK、运行时和托管包

NetWasm.Compiler.Browser(https://www.nuget.org/packages/NetWasm.Compiler.Browser) · NetWasm.Compiler.Tasks(https://www.nuget.org/packages/NetWasm.Compiler.Tasks) · NetWasm.Hosting(https://www.nuget.org/packages/NetWasm.Hosting) · NetWasm.Hosting.Build(https://www.nuget.org/packages/NetWasm.Hosting.Build) · NetWasm.Ref(https://www.nuget.org/packages/NetWasm.Ref) · NetWasm.Runtime.Pack(https://www.nuget.org/packages/NetWasm.Runtime.Pack) · NetWasm.Runtime.Wasm32(https://www.nuget.org/packages/NetWasm.Runtime.Wasm32) · NetWasm.Runtime.Wasm64(https://www.nuget.org/packages/NetWasm.Runtime.Wasm64) · NetWasm.Sdk(https://www.nuget.org/packages/NetWasm.Sdk) · NetWasm.Templates(https://www.nuget.org/packages/NetWasm.Templates) · NetWasm.Testing.VSTest(https://www.nuget.org/packages/NetWasm.Testing.VSTest) · NetWasm.Toolchain(https://www.nuget.org/packages/NetWasm.Toolchain) · NetWasm.Wit.Bindings.Tool(https://www.nuget.org/packages/NetWasm.Wit.Bindings.Tool)

已移植的库包(https://github.com/zion-sati/NetWasm.Libraries#packages) · TUnit 包(https://github.com/zion-sati/TUnit-NetWasm#netwasm-packages)

许可证

CoreLib、运行时库、模板和生成的辅助代码采用 MIT 许可,须保留上游声明。编译器和开发者工具链使用 NetWasm Community License 1.0。Compact 和 NetWasmGC 收集器采用 Apache-2.0。

Community License 对符合条件的个人、教育、开源工作、评估用途免费,对员工少于 250 人且年收入低于 1,000 万美元的组织免费。匹配的 GitHub Sponsors 等级(https://github.com/sponsors/zion-sati)授予有限的内部商业使用权;OEM 和商业产品使用需要单独协议。生成的应用可以使用其作者选择的许可证。不存在登录、激活、遥测或技术强制手段。

许可证指南 · 第三方声明 · 商业许可:

带上你的工作负载

从 Console.WriteLine(42) 开始,然后尝试最小的真实工作负载——那些部署体积、沙箱化或可移植性真正重要的场景。

如果缺少什么,请提交 issue(https://github.com/zion-sati/NetWasm/issues)。具体的工作负载是决定 1.0 支持边界的依据。

相似文章

weblings:在 WASM 内部将 Rust 编译为 WASM

Lobsters Hottest

Weblings 是一个编译为 WebAssembly 的 Rust 编译器工具链,通过带有 playground 和 Rustlings 练习的网页 UI,支持在浏览器中直接编译和执行 Rust 代码。

2026 年 WebAssembly 运行时性能

Lobsters Hottest

本文使用 libsodium 加密库对多种 WebAssembly 运行时(WAVM、WasmEdge、WAMR、wasm2c、Wasmer、Wasmtime、Wazero、Node、Bun)进行了性能基准测试,比较了 2024、2025 和 2026 年的版本。结果显示,WAVM、WasmEdge(AOT)、WAMR(AOT)、wasm2c、Wasmer 和 Wasmtime 在 CPU 密集型加密任务中实现了接近原生的性能,而 wide_arithmetic 指令对加密代码有益。

我们的Rust到Zig重写进展如何

Lobsters Hottest

Roc编译器团队已将他们30万行Rust代码库重写为Zig,经过18个月实现了功能对等,生成了更小的WebAssembly二进制文件并提高了性能。

Wasmi 2.0 - 工程实现最快的Wasm解释器

Lobsters Hottest

Wasmi 2.0,一款WebAssembly解释器,已发布,带来了显著的性能提升,运行速度比1.0版本快约2.2倍,还新增了减小二进制大小和稳定燃料计量等功能。