NXS · Nyxis · Demo
WAL / span ingestion
Live comparison: NXS generic encoder, fast-path, sealed, WASM, and JSON NDJSON for OTel-style spans.
WASM unavailable — WASM bench will be skipped
NXS WAL
—
click Run benchmark
NXS Fast
—
click Run benchmark
NXS Sealed
—
click Run benchmark
NXS WASM
—
click Run benchmark
JSON NDJSON
—
click Run benchmark
What each encoder does
Five strategies, same output format — same binary bytes on disk.
| Encoder | Strategy | Why it matters |
|---|---|---|
| NXS WAL | One NxsWriter per span. Each call to finish() allocates a chunk array, merges it, and wraps the record in a full preamble + schema + tail-index. | Matches a real WAL append where each span is an independent object written to a rolling log. Slow because of per-span allocations and BigInt arithmetic. |
| NXS Fast | A single pre-allocated 128-byte Uint8Array is reused for every span. Fields are written with DataView.setUint32 (two calls per i64 — no BigInt). Strings are pre-encoded to Uint8Array once at startup. Only variable offsets are patched per span. | Shows the theoretical ceiling of NXS binary encoding in JS: eliminate GC pressure and BigInt. This is what a production WAL writer would look like — fixed schema, typed views, no dynamic allocation in the hot loop. |
| NXS Sealed | All spans written into one shared NxsWriter, then finish() called once. Produces a single .nxb file with one schema header, one preamble, and one tail-index covering all records. | The batch/seal path used after WAL rotation. finish() triggers a single large _materialize() merge — fast for the tail-index write but re-encodes every span with BigInt, so throughput is similar to WAL. |
| NXS WASM | A WasmSpanWriter fills a 72-byte input struct in WASM memory via DataView, then calls encode_span(outPtr, fieldsPtr) — a freestanding C function compiled to WASM. Strings are written directly into WASM memory with TextEncoder.encodeInto(). Zero JS allocations per span. | Native WASM struct packing with no JS BigInt, no GC pressure, and no per-call memory allocation. The output bytes are a zero-copy view into WASM linear memory. Comparable to NXS Fast — WASM call overhead is the main cost. |
| JSON NDJSON | One JSON.stringify per span, newline-delimited. IDs are serialised as decimal strings (BigInt cannot be JSON-serialised natively). | V8's JSON.stringify is implemented in C++ and highly optimised — it wins on throughput for small objects. But it produces ~2× more bytes because every field name is repeated as a UTF-8 string in every record. |
Throughput — spans / second (higher is better)
How many spans each format encodes per second in this browser tab.
NXS WAL
—
NXS Fast
—
NXS Sealed
—
NXS WASM
—
JSON NDJSON
—
Output size (smaller is better)
Total bytes produced for all N spans. NXS omits field names and stores numbers in 8-byte binary; JSON repeats every key as a string.
NXS WAL
—
NXS Fast
—
NXS Sealed
—
NXS WASM
—
JSON NDJSON
—
Detail
Per-span cost and aggregate metrics for the current run.
| Format | ns / span | spans / sec | total time | output size | bytes / span | vs JSON size |
|---|---|---|---|---|---|---|
| Run the benchmark to see results. | ||||||
NXS WAL encodes each span independently (append path); NXS Sealed writes all spans then calls
finish() once (batch path). NXS WASM calls a native C function compiled to WebAssembly. JSON uses JSON.stringify per span.Cross-language WAL comparison
NXS WAL append throughput vs each language's standard JSON serialiser — measured at 10,000 spans, Apple M-series (arm64). JS bars update live after each run above.
NXS WAL ns/span (lower is better)
C
run above first
JSON ns/span (lower is better)
C
run above first
Reference numbers from
bench_wal.c, bench_wal_test.go, bench_wal.py, ruby/bench_wal.rb, and cargo run --release --bin bench. Rust has no stdlib JSON encoder — only NXS append is shown. JS numbers update live from this browser tab when you run the benchmark.