跳到正文
Hacker News· sumnole·· 9 小时前AI 评分46

NetWasm 让 C# 在 Wasm 体积上胜过 Rust

C# now beats Rust on WASM size

AI 导读

独立 .NET WebAssembly 编译器 NetWasm 在相同 print-42 工作负载下产出 47,895 字节的 WASI Preview 2 组件,比 Rust 1.95.0 开启体积优化后的 53,635 字节小 10.7%,经 Brotli-11 压缩后小 19.5%。

正文 · AI 翻译

面向 WebAssembly 和 WASI 的独立 .NET。
编写 C#。部署小巧、独立的 Wasm。

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

Star NetWasm on GitHub Publication License: Community + MIT

将 C# 编译为 WebAssembly · 在浏览器中试用 · 安装并构建 · 查看支持的 API · 阅读文档

C# 在 Wasm 体积上现已胜过 Rust

NetWasm + 精确 GC:47,895 字节
Rust 1.95.0 面向体积的配置:53,635 字节

对于相同的 print-42 工作负载,NetWasm 的结果包含运行时和 精确的 Compact 垃圾回收器。两个产物都是独立的 WASI Preview 2 组件,均按开发者开箱即得的方式构建:各自 工具链的标准库和常规构建,未手工添加任何工具。

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

Wasm footprint landscape with raw and Brotli-11 sizes. NetWasm with precise GC is 47,895 raw and 13,660 compressed bytes; Rust is 53,635 and 16,963; Kotlin/Wasm is 79,293 and 22,655; TinyGo is 93,043 and 26,931. NetWasm and Rust are the matched WASI Preview 2 comparison; Kotlin and TinyGo are contextual Preview 1 references.

工具链 原始 Wasm Brotli 11 对比角色
NetWasm,Compact GC 47,895 B 13,660 B WASI P2 组件;包含精确 GC
Rust 1.95.0,体积配置 53,635 B 16,963 B 匹配的 WASI P2 组件参照
Kotlin 2.4.0 wasmWasi 79,293 B 22,655 B P1 上下文;GC 由宿主引擎提供
TinyGo 0.39.0 93,043 B 26,931 B P1 上下文;配置了精确 GC

Rust 是重点对比对象,因为它使用相同的工作负载和输出 形式。Kotlin/Wasm 和 TinyGo 展示了更广泛的体积格局,但它们的 目标和运行时边界不同。这是实测的工作负载/配置 结果,而非语言层面的体积排名。

NetWasm 的数字是模板应用程序的默认 Release 发布结果。Rust 的 数字则在普通 cargo build 基础上加入了 Cargo 文档记载的体积设置(opt-level = "z"、LTO、一个 codegen 单元、panic = "abort" 以及剥离符号)。 Binaryen 的 wasm-opt 在这里不是后处理步骤:它是 NetWasm 的 编译器流水线的一部分,在每次常规构建中都会运行,无需安装或 启用任何东西。Rust 的 wasm32-wasip2 输出是一个组件,因此对其应用 wasm-opt 意味着提取核心模块、优化它,再手工重新编码该组件。

两种语言都能做到更小,但那属于另一类程序:Rust 使用 no_std,或 C# 使用 bflat 的 zero library 之类的最小库,各自 放弃大部分标准库,对 C# 而言还要放弃垃圾回收器。

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

比 Mono AOT 更快

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

工作负载 NetWasm,NetWasmGC Mono AOT,.NET 10 NetWasm 时间
完整事务 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 次的中位数,每次运行 都与桌面 .NET 的结果进行了核对。Mono AOT 是 Microsoft 的 wasi-wasm 目标在其默认 Release 配置下的结果。NetWasmGC 在前两个工作负载上耗时也少于 桌面 .NET 11 的 JIT(49.9 和 9.7 ms);JIT 在 UI 频繁变更上更快(8.6 ms),而桌面 NativeAOT 在三项上都是最快的。

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

不止于 Hello World

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

C# 15 · JavaScript 模块与 Web Worker · 静态 .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 或更高版本, 然后运行:

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:

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 平台,而不是把桌面 .NET 塞进 Wasm 沙箱。 正是这一边界使原生 Wasm 大小的 C# 成为可能。

JSON 和可选特性如何影响大小

选择你的分配策略

Compact 是默认选项:一种大小优先、精确、非移动的标记-清除 收集器。它跟踪编译器发布的根和引用布局,而不进行 保守扫描。其名称指的是实现大小,而不是移动式堆压缩。

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

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

<PropertyGroup>
  <NetWasmGarbageCollector>NetWasm</NetWasmGarbageCollector>
</PropertyGroup>

未设置该属性时选择 Compact。各库保持收集器中立;四种后端均打包为 wasm32 和 wasm64。有关权衡和诊断信息,请参阅选择垃圾收集器。

库应向上兼容

为较小的 NetWasm 配置文件编写的库也应能在桌面 .NET 上使用,而不是被困在仅限 Wasm 的生态系统中。

dotnet new netwasm-lib -n MyLibrary

该模板面向 netwasm0.1 和 net10.0,为两个平台生成 NuGet 资产。这是显式的多目标设定,并不承诺任意桌面包都能反向工作。

NetWasm.Libraries 提供移植的框架和扩展包。TUnit-NetWasm 通过通用 VSTest 桥接提供常规的 dotnet test 集成。

这与传统 .NET Wasm 相比如何?

对于一个打印 42 的空 UI Blazor WebAssembly 应用程序,启用 AOT、托管裁剪、IL 剥离、固定区域性和以大小为重点的本机链接:

AOT Wasm size comparison, lower is better: NetWasm with Compact GC 46.8 KiB, Blazor .NET 10 11.71 MiB, Blazor .NET 11 RC1 14.58 MiB. NetWasm is about 319 times smaller than the .NET 11 build by exact bytes.

AOT 构建 未压缩 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 总计包括其本机运行时和所需的 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 · NetWasm.Compiler.Tasks · NetWasm.Hosting · NetWasm.Hosting.Build · NetWasm.Ref · NetWasm.Runtime.Pack · NetWasm.Runtime.Wasm32 · NetWasm.Runtime.Wasm64 · NetWasm.Sdk · NetWasm.Templates · NetWasm.Testing.VSTest · NetWasm.Toolchain · NetWasm.Wit.Bindings.Tool

移植库包 · TUnit 包

许可证

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

社区许可证对符合条件的个人、教育用途、开源工作、评估用途,以及员工少于 250 人且年收入低于 10,000,000 美元的组织免费。匹配的 GitHub Sponsors 层级授予有限的内部商业使用;OEM 和商业产品使用需要单独协议。生成的应用程序可以使用其作者选择的许可证。没有登录、激活、遥测或技术强制执行。

许可指南 · 第三方声明 · 商业许可:zionsatidev@gmail.com

带来一个工作负载

从 Console.WriteLine(42) 开始,然后尝试部署大小、沙箱化或可移植性至关重要的最小真实工作负载。如果缺少某些内容,请提交 issue。具体的工作负载是决定 1.0 支持边界的方式。

来源:Hacker News · github.com