Skip to content

Instantly share code, notes, and snippets.

@EmNudge

EmNudge/hello.c Secret

Last active January 11, 2026 18:12
Show Gist options
  • Select an option

  • Save EmNudge/f6c94d84c68cd678d3c6f06474441b80 to your computer and use it in GitHub Desktop.

Select an option

Save EmNudge/f6c94d84c68cd678d3c6f06474441b80 to your computer and use it in GitHub Desktop.
Wasm file comparisons
// Compiler: emcc 4.0.23 (Emscripten)
//
// Build commands and resulting WASM sizes:
//
// Default (debug):
// emcc hello.c -o hello.wasm
// Size: 15 KB
//
// Release (optimized):
// emcc -O3 hello.c -o hello.wasm
// Size: 2.0 KB
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
// Compiler: tinygo 0.33.0 (go 1.23.0, LLVM 18.1.2)
//
// Build commands and resulting WASM sizes:
//
// Default (debug):
// tinygo build -target wasi -o hello.wasm hello.go
// Size: 470 KB
//
// Release (optimized):
// tinygo build -target wasi -opt=2 -o hello.wasm hello.go
// Size: 446 KB
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
// Compiler: rustc 1.92.0 (ded5c06cf 2025-12-08)
//
// Build commands and resulting WASM sizes:
//
// Default (debug):
// rustc --target wasm32-wasip1 hello.rs -o hello.wasm
// Size: 1.8 MB
//
// Release (optimized):
// rustc --target wasm32-wasip1 -C opt-level=3 hello.rs -o hello.wasm
// Size: 1.8 MB
fn main() {
println!("Hello, World!");
}
// Compiler: zig 0.12.0-dev.2700+d12c8db64
//
// Build commands and resulting WASM sizes:
//
// Default (debug):
// zig build-exe hello.zig -target wasm32-wasi -femit-bin=hello.wasm
// Size: 601 KB
//
// Release (optimized):
// zig build-exe hello.zig -target wasm32-wasi -OReleaseFast -femit-bin=hello.wasm
// Size: 12 KB
const std = @import("std");
pub fn main() void {
std.debug.print("Hello, World!\n", .{});
}
// Compiler: zig cc (clang 17.0.6 via zig 0.12.0-dev.2700+d12c8db64)
//
// Build commands and resulting WASM sizes:
//
// Minimal (no libc, direct WASI calls, using zig cc):
// zig cc --target=wasm32-wasi -O3 -nostdlib \
// -Wl,--no-entry -Wl,--export=_start \
// hello_minimal.c -o hello_minimal.wasm
// Size: 1.3 KB (includes debug metadata)
//
// Minimal + stripped:
// zig cc --target=wasm32-wasi -O3 -nostdlib \
// -Wl,--no-entry -Wl,--export=_start -Wl,--strip-all \
// hello_minimal.c -o hello_minimal.wasm
// Size: 254 B
//
// Compared to standard hello.c (emscripten):
// Debug: 15 KB -> 254 B (60x smaller)
// Release: 2.0 KB -> 254 B (8x smaller)
__attribute__((import_module("wasi_snapshot_preview1"), import_name("fd_write")))
int fd_write(int fd, const void *iovs, int iovs_len, int *nwritten);
typedef struct {
const char *buf;
int len;
} iovec_t;
void _start() {
const char msg[] = "Hello, World!\n";
iovec_t iov = { msg, sizeof(msg) - 1 };
int nwritten;
fd_write(1, &iov, 1, &nwritten);
}
// Compiler: tinygo 0.33.0 (go 1.23.0, LLVM 18.1.2)
//
// Build commands and resulting WASM sizes:
//
// Minimal (direct WASI calls, optimized, no debug):
// tinygo build -target wasi -opt=2 -no-debug -o hello_minimal.wasm hello_minimal.go
// Size: 17 KB
//
// Compared to standard hello.go:
// Debug: 470 KB -> 17 KB (28x smaller)
// Release: 446 KB -> 17 KB (26x smaller)
package main
import "unsafe"
//go:wasmimport wasi_snapshot_preview1 fd_write
func fd_write(fd int32, iovs unsafe.Pointer, iovsLen int32, nwritten unsafe.Pointer) int32
type iovec struct {
buf *byte
len uint32
}
func main() {
msg := "Hello, World!\n"
msgBytes := unsafe.StringData(msg)
iov := iovec{buf: msgBytes, len: uint32(len(msg))}
var nwritten int32
fd_write(1, unsafe.Pointer(&iov), 1, unsafe.Pointer(&nwritten))
}
// Compiler: rustc 1.92.0 (ded5c06cf 2025-12-08)
//
// Build commands and resulting WASM sizes:
//
// Minimal (no_std, direct WASI calls, LTO, size-optimized):
// rustc --target wasm32-unknown-unknown -C opt-level=s -C lto=yes \
// -C link-arg=--export=_start hello_minimal.rs -o hello_minimal.wasm
// Size: 605 B
//
// Compared to standard hello.rs:
// Debug: 1.8 MB -> 605 B (3000x smaller)
// Release: 1.8 MB -> 605 B (3000x smaller)
#![no_std]
#![no_main]
use core::panic::PanicInfo;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
#[link(wasm_import_module = "wasi_snapshot_preview1")]
extern "C" {
fn fd_write(fd: u32, iovs: *const Iovec, iovs_len: u32, nwritten: *mut u32) -> u32;
}
#[repr(C)]
struct Iovec {
buf: *const u8,
len: u32,
}
#[no_mangle]
pub extern "C" fn _start() {
let msg = b"Hello, World!\n";
let iov = Iovec {
buf: msg.as_ptr(),
len: msg.len() as u32,
};
let mut nwritten: u32 = 0;
unsafe {
fd_write(1, &iov, 1, &mut nwritten);
}
}
// Compiler: zig 0.12.0-dev.2700+d12c8db64
//
// Build commands and resulting WASM sizes:
//
// Minimal (direct WASI calls, size-optimized):
// zig build-exe hello_minimal.zig -target wasm32-wasi -OReleaseSmall \
// -femit-bin=hello_minimal.wasm
// Size: 199 B
//
// Compared to standard hello.zig:
// Debug: 601 KB -> 199 B (3000x smaller)
// Release: 12 KB -> 199 B (60x smaller)
extern "wasi_snapshot_preview1" fn fd_write(fd: i32, iovs: [*]const Ciovec, iovs_len: usize, nwritten: *usize) i32;
const Ciovec = extern struct {
base: [*]const u8,
len: usize,
};
pub export fn _start() void {
const msg = "Hello, World!\n";
var nwritten: usize = 0;
_ = fd_write(1, &[_]Ciovec{.{ .base = msg.ptr, .len = msg.len }}, 1, &nwritten);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment