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
dotnetCLI. - Export typed JavaScript modules for browsers or Web Workers.
- Link native
.aarchives 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.1and 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.Librariessuite,TUnit-NetWasm
All packages are available on NuGet and documented in the repository.