NXS · Nyxis
Browser benchmark
Workflow outcomes first — time to interactive, filter latency, stream responsiveness, and browser memory — then raw throughput. The same fixtures used in Node and Go benches, run in your browser over fetch().
What we measure first
- Time to interactiveOpen or stream until the first row is usable — sections 1–5 below.
- Browser memory
performance.memorydelta after parse vsNxsReader— section 17. - Filter & scan latencyPredicate and aggregate passes — sections 12–16.
- Raw throughputWarm random access and WAL reference charts — sections 8–11 and 19.
.nxb plus records_<N>_columnar.nxb for aggregate comparisons. Use size presets or upload matching fixtures. pre-parsed on JSON/CSV bars — full parse happened before the timer. lazy decode on NXS row bars — reads from .nxb bytes inside the timed region. Purple columnar bars use records_<N>_columnar.nxb (colSumF64).
1. Open file — parse the entire structure
JSON and CSV eagerly parse every byte; NXS reads only the header + tail-index.
2. Cold first field — bytes already in memory
Parse/open then read one field at record n/2. Purple prefetch bar: prefetch_viewport(0, min(49,n−1)) then cursor(k) (Workload F1 pattern, in-memory).
3. Cold pipeline — fetch + parse + one field
Models the page-load path after fetch(): open or parse, then first field at n/2. Includes the same prefetch_viewport bar as §2.
4. Cold pipeline — open + sum score
No pre-parsed state: parse entire file then reduce (realistic analytics cold start).
5. Stream — time to first record
Chunked NxsStreamReader vs JSON.parse until first row is usable.
6. Open + iterate all records
End-to-end: open the format, then walk every record and read username once. Includes open + prefetch_viewport(0,49) + scan (virtual-scroll warm path).
7. JSON raw scan vs parse + loop
Substring scan for "username": lengths without JSON.parse vs full parse + loop vs NXS scan.
8. Iterate only — warm structures · JSON/CSV pre-parsed
Data already parsed/opened; walk every record and read username. Includes a reader with prefetch_viewport(0, min(49,n−1)) held warm across iterations.
9. Random-access read — one field from record k · JSON/CSV pre-parsed
JSON/CSV: property access on a pre-parsed array. NXS: decode from row .nxb — record vs cursor vs buildFieldIndex (optional WASM build).
10. Random-access read — multiple fields from record k · JSON/CSV pre-parsed
Four fields per random record. NXS seekWarm(k) amortises the rank cache when reading several fields on the same row.
11. Scattered access — strided indices · JSON/CSV pre-parsed
~500 reads at evenly spaced record indices (simulates non-sequential UI paging).
12. Full scan — four fields per record
Open + linear pass reading username, age, balance, active on every row. NXS uses zero-alloc cursor.scan (not seekWarm per row).
13. Filter — count where score > 80 · JSON/CSV pre-parsed
Predicate over all records: JSON loop vs NXS cursor.seek + getF64BySlot. Row layout walks the bitmask per field — use columnar layout for filter-heavy analytics.
14. Aggregate — sum of 'score' (warm) · JSON/CSV pre-parsed
Pre-parsed JSON loop, raw CSV column scan, row sumF64 (WASM when loaded), and columnar colSumF64 when the columnar fixture is present.
15. Indexed sum vs column reducer · row vs columnar layout
Row: buildFieldIndex("score") + loop vs sumF64. Columnar: dense colSumF64 (SIMD path in native drivers).
16. Columnar aggregate vs JSON · §7.4 / analytics headline
Sum score at the same record count: JSON warm (pre-parsed) vs JSON cold (parse each iteration) vs NXS columnar cold, mistaken prefetch (new reader each time), and persistent reader with prefetch_column once. Requires records_<N>_columnar.nxb and matching JSON.
NXS cold (new reader each iteration) beats pre-parsed warm JSON by ~10% without any warmup. NXS warm (persistent reader + prefetch_column) beats pre-parsed warm JSON by ~45%. The prefetch_column + sum (new reader) bar shows the cost of calling prefetch without retaining the reader — avoid that pattern.
17. Memory — heap growth (Chrome)
Indicative performance.memory delta after parse vs NxsReader (not file size on disk).
18. Workers — parallel sum score
Main-thread sumF64 vs four module workers each summing a chunk (buffer copy per worker).
19. WAL span ingestion — append · recover · seal · roundtrip
Rust cargo run --release --bin bench on Apple M-series (tmpfs I/O for append). Reference charts below; values match BENCHMARK.md. Append is amortised append-batch (encode + write all spans per run).
Timings (ns / span, lower is better)
File size vs JSON NDJSON baseline
Honest tradeoffs
Nyxis is not a drop-in replacement for your entire data stack. Published comparisons acknowledge where other formats win.
- Apache Arrow remains superior for dense analytical scans — use NXS
columnarlayout or the Arrow bridge for hybrid pipelines. - FlatBuffers / Cap'n Proto may cold-open faster on tiny warm-cache files — see Workload B.
- Parquet remains excellent warehouse storage — NXS targets transport, browser UIs, and streaming ingest.
- JSON (warm) can tie NXS on simple field access when the entire document is already parsed — bars label pre-parsed explicitly.
Reproducibility
Re-run the same workloads locally or in CI.
- Public fixtures —
nyxis/site/bench/fixtures/records_*.nxb(+ matching.json/.csv) - Browser harness — this page (
site/web/src/demos/bench-page.js) - Native benches —
cargo run --release --bin benchin nyxis - Methodology & hardware — BENCHMARK.md (Apple Silicon dev hosts, Linux x86_64, EPYC AVX-512 rows)
- Generate fixtures —
make -C nyxis bench-fixtures(see repoMakefile)
19. WAL encoder — cross-language comparison
Per-span append throughput across all ten implementations, measured at 10,000 spans on Apple M-series (arm64). NXS encodes directly to the binary format; JSON baseline uses each language's standard library serialiser. JS numbers are from the live benchmark above.