NetWasm 0.10.0 Enables C# to Beat Rust on WebAssembly Size

C# now beats Rust on WebAssembly size

Takeaway: NetWasm 0.10.0 produces a 47,895‑byte standalone WASI component for a simple print(42) program, which is 10.7 % smaller than the Rust 1.95.0 size‑profile binary (53,635 B) and 19.5 % smaller after Brotli‑11 compression.

Raw and compressed size comparison

Result: NetWasm’s Compact GC build is 47,895 B raw and 13,660 B compressed; Rust’s size‑profile build is 53,635 B raw and 16,963 B compressed.

Toolchain Raw Wasm Brotli‑11 Role
NetWasm, Compact GC 47,895 B 13,660 B WASI Preview 2 component with built‑in precise GC
Rust 1.95.0, size profile 53,635 B 16,963 B Matched WASI Preview 2 component
Kotlin 2.4.0 wasmWasi 79,293 B 22,655 B Preview 1; host supplies GC
TinyGo 0.39.0 93,043 B 26,931 B Preview 1; custom GC

Both binaries are produced with default release settings and no manual post‑processing. NetWasm’s compiler pipeline always runs Binaryen’s wasm-opt, while Rust’s build does not include an equivalent step because the component format complicates re‑encoding.

Methodology notes

  • The Rust build uses opt-level = "z", LTO, a single codegen unit, panic = "abort", and stripped symbols.
  • NetWasm’s size includes its runtime and precise Compact garbage collector.
  • The benchmark reflects a single print(42) workload, not a language‑wide size ranking.
  • Both toolchains can shrink further with no_std (Rust) or a zero‑library approach (C#), but those configurations sacrifice most of the standard library and, for C#, the garbage collector.

Exact sources, versions, settings and reproduction steps

Performance advantage over Mono AOT

Takeaway: NetWasm’s allocation and GC benchmarks run 26‑40 % faster than Mono AOT compiled from the same C# source and executed under Wasmtime.

Workload NetWasm (Compact GC) Mono AOT (.NET 10) Speed improvement
Full transaction 43.9 ms 59.1 ms 26 % faster
Retained object graph 8.8 ms 14.2 ms 38 % faster
UI churn 17.6 ms 29.1 ms 40 % faster

Measurements are medians of 15 runs on an Apple M1 Max using Wasmtime 49.0.3. NetWasm also outperforms desktop .NET 11 JIT on the first two workloads, though the JIT is slightly faster on UI churn. Native AOT remains the fastest overall.

Versions, settings, desktop figures and reproduction

Feature set beyond a hello‑world

Takeaway: NetWasm supports full‑featured C# 15 applications, JavaScript interop, static .a archives, WIT bindings, expression trees, HTTP/JSON/XML, reflection‑free DI, and both wasm32 and wasm64 targets.

  • Build, run, publish, and test via the standard dotnet CLI.
  • Export typed JavaScript modules for browsers or Web Workers.
  • Link native .a archives with generated C ABI marshalling.
  • Generate C# bindings from WIT and produce standalone WASI Preview 2 components.
  • Use LINQ, HTTP, JSON, XML, Regex, and DI libraries packaged for NetWasm.
  • Dual‑target libraries for netwasm0.1 and desktop .NET.

The profile is intentionally smaller than desktop .NET, yet it already supports complete applications, portable libraries, test projects, and browser modules.

Quick start guide

Takeaway: Installing the .NET 10+ SDK and the NetWasm templates lets you compile and run a self‑contained C# program that prints 42 in under a minute.

# Install .NET SDK 10.0.303+ (or .NET 11 for C# 15)
# Install NetWasm templates

dotnet new install NetWasm.Templates

dotnet new netwasm-app -n HelloNetWasm
cd HelloNetWasm

dotnet restore

dotnet run               # prints 42

dotnet publish -c Release -o publish/local

# Run with a WASI Preview 2 host (e.g., Wasmtime)
wasmtime run publish/local/HelloNetWasm.wasm

The published component is self‑contained with respect to .NET; only a WASI host is required at runtime. "Build with .NET; run without .NET."

How NetWasm stays small

Takeaway: NetWasm achieves its size by compiling reachable CIL directly to Wasm, performing closed‑world analysis, and linking only the runtime features actually used.

  • Closed‑world compilation: Unreachable code, imports, and runtime features are omitted.
  • Reduced platform contract: Only portable APIs are provided; desktop runtime assumptions are excluded.
  • No broad reflection: Features like dynamic, runtime assembly loading, and general type inspection are omitted.
  • Generated boundaries: JSON, DI, WIT, and native interop use static contracts rather than runtime discovery.
  • Optional data: Timezone data, rich stack traces, and diagnostics are included only when explicitly enabled.
  • Single collector: Only the selected GC strategy is linked into the final binary.

This is a purpose‑built .NET platform, not a stripped‑down desktop runtime forced into a Wasm sandbox.

Allocation strategies and size trade‑offs

Takeaway: NetWasm offers three garbage‑collector strategies, each with distinct size and performance characteristics.

Strategy Best fit Raw component Brotli‑11
Compact (default) Small apps, low‑churn UI 47,895 B 13,660 B
NetWasmGC Higher allocation throughput 50,855 B 15,053 B
Bump Bounded, one‑shot jobs 43,864 B 11,959 B

Select a strategy by adding <NetWasmGarbageCollector> to the project file; omitting the property defaults to Compact.

Library compatibility model

Takeaway: NetWasm libraries are multi‑targeted to netwasm0.1 and a desktop framework (e.g., net10.0), enabling the same source to run on both Wasm and traditional .NET.

# Create a library that targets both platforms

dotnet new netwasm-lib -n MyLibrary

The generated NuGet package contains assets for both runtimes, but only libraries that fit the NetWasm profile can be consumed without modification.

Comparison with conventional Blazor WebAssembly

Takeaway: A Blazor WebAssembly AOT build that prints 42 occupies ~12 MiB (.NET 10) or ~15 MiB (.NET 11), making NetWasm roughly 256‑319 × larger.

Build Uncompressed Wasm Size factor vs 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×

The Blazor figures include the native runtime and required assemblies; NetWasm includes its runtime, GC, and WASI component. This comparison is a console component versus an empty‑UI Blazor app, not a full‑framework equivalence.

Exact Blazor versions, settings and reproduction

Community feedback highlights

"It's a bit of an unfair comparison, the benchmarked C# code here is also post‑processed which none of the other targets are. … Rust can go much, much lower." – @sondr3

"There's something about C# that just feels so right for me for backends… good tooling, modern language features, great debugging experience." – @CharlieDigital

"Worth pointing out this is not open source, if that is an important consideration when you're comparing the two (it is to me)." – @Scaled

"This is a bit of an unfair comparison, since WASM ships with a garbage collector that C# can use while Rust does all the memory management itself, of course it will be bigger." – @weinzierl (later corrected after reading the post)

"Not very hard... Rust wasm binaries are ginormous. If you care about WASM performance, size and memory usage, Zig is a much better candidate." – @jedisct1

These comments illustrate the main points of contention: the fairness of the benchmark, open‑source licensing, and the relevance of garbage‑collector overhead.

Current status and roadmap

Takeaway: NetWasm is pre‑1.0; the 0.10.0 release introduces rebuilt garbage collectors and size‑focused optimizations. Future work targets broader WASI Preview 2 coverage, optional globalization data, full expression‑tree support, a stable netwasm1.0 contract, and production diagnostics.

  • 0.10.0 highlights: No collector header on objects, single‑allocation arrays, removal of the Boehm collector, faster allocation‑heavy programs, reduced startup time for timezone data.
  • Road to 1.0: Expand WASI Preview 2 API surface, enable more .NET Standard 2.1 libraries, stabilize platform and runtime contracts, improve incremental development loops.

The project remains open to contributions; issues and feature requests are welcomed.

Licensing model

Takeaway: NetWasm’s core libraries are MIT‑licensed; the compiler and tooling use the NetWasm Community License 1.0, which is free for individuals, education, open‑source work, and small organizations (≤250 employees, ≤$10 M revenue). Commercial use beyond these limits requires a separate agreement.

  • Runtime collectors (Compact, NetWasmGC) are Apache‑2.0.
  • No activation, telemetry, or technical enforcement is built into generated applications.

Packages and ecosystem

  • Compiler & SDK: NetWasm.Compiler.Tasks, NetWasm.Sdk, NetWasm.Templates
  • Runtime: NetWasm.Runtime.Wasm32, NetWasm.Runtime.Wasm64
  • Hosting: NetWasm.Hosting, NetWasm.Hosting.Build
  • Library ports: NetWasm.Libraries suite, TUnit-NetWasm

All packages are available on NuGet and documented in the repository.


Sources