KB / infrastructure / rust-wasm-migration

Rust/WASM Migration

7 Rust crates compiled to WASM; markdown 79x faster, others parity or slower due to JS↔WASM marshaling cost.

Updated 2026-05-01
rustwasmperformancebenchmark
raw .md

Rust/WASM Migration

Phase 1 and 2 of the Rust/WASM migration completed 2026-05-01. Seven Rust crates were built as cdylib targets and compiled to WASM, replacing TypeScript implementations in @duyet/libs.

Benchmark results

Module TS mean WASM mean Speedup Verdict
markdown-to-html 6.3 ms 0.08 ms 79x Use WASM
diff-text 0.11 ms 0.11 ms ~1x Parity
string-utils 0.14 ms 0.14 ms ~1x Parity
exif-parse 0.001 ms 0.001 ms ~1x Parity
csv-parse 0.08 ms 0.16 ms 0.5x WASM slower
normalizers 0.01 ms 0.04 ms 0.23x WASM slower
dedup 0.01 ms 0.04 ms 0.29x WASM slower

Why some modules are slower

For sub-100 µs operations, JS↔WASM string marshaling (30–40 µs per call) exceeds the actual computation time. The boundary cost dominates. Only markdown-to-html — which takes ~6 ms in pure TypeScript — has enough computation to justify the marshal cost.

Rule of thumb: don't migrate a TypeScript function to WASM unless its TS mean exceeds ~1 ms.

Completed work units

  • WU-01: Rust/WASM toolchain foundation
  • WU-02–08: All 7 modules (markdown, csv, normalizers, exif, diff, utils, dedup)
  • WU-09: Benchmark harness with real WASM imports (not mocked)

Remaining work units

  • WU-10: CI benchmark workflow (not yet wired)
  • WU-19: Remove superseded TS dependencies (unified/remark/rehype, exifreader, diff-match-patch)

Important notes

  • All pkg/ directories are gitignored — WASM must be built from source in CI
  • scripts/wasm-build.ts filters crates by crate-type = ["cdylib"] — keep this filter
  • Normalizers in apps/llm-timeline/lib/normalizers.ts already call WASM via initSync
  • Cloudflare Workers WASM boundary cost may differ from Bun runtime — benchmark separately if Workers usage is added
  • Keep slower WASM crates: batch APIs may benefit future use cases

Integration

WASM artifacts land in packages/wasm/pkg/. The @duyet/libs package re-exports the bindings. Apps import via @duyet/libs/markdownToHtml (and similar) — they don't import WASM directly.