The engine is fast. Here's the proof.
Every number below was measured minutes ago, against a real 19,638-timetable dataset, in both a native release build and the exact compiled WASM binary this app ships. Nothing here is rounded up, extrapolated, or vibes-based.
Results
| Stage | Native (release) | WASM (browser) |
|---|---|---|
| Generation — all 19,638 results | 25.7ms | 36.4ms |
| Re-rank — previous approach | 362.1ms | 454.5ms |
| Re-rank — current (shipped) | 1.93ms | 4.0ms |
| Session preference (soft rank) | 2.36ms | 3.8ms |
| Speedup, naive → shipped | 188× | 114× |
WASM trails native, as any browser sandbox will — and it doesn't matter. 4.0ms is a rounding error inside a single 16ms animation frame.
vs. the old solver
Before Lattice, there was a plain-TypeScript prototype solver. Same real dataset, same 19,638 correct results — it isn't wrong, it's just not built for speed the way this is.
| Tool | Generate + rank, same 19,638-result dataset |
|---|---|
| Old solver — solve only (no worker) | 395ms |
| Old solver — full pipeline (worker + serialize + parse) | 575ms |
| Lattice (Rust → WASM, real browser) | 41.3ms |
| Lattice (native release build) | 27.7ms |
| Lattice's speedup, full pipeline vs. full pipeline | ~14× |
Both measured as a cold first call in the same headless Chrome, no warmup — identical input, identical output count. The old solver runs on a Web Worker, so its real cost includes serializing all 19,638 results to JSON and parsing them back on the main thread — that round-trip alone is ~220ms, more than five times Lattice's entire generate-and-rank pipeline.
How this was measured
- One real VIT course selection. 19,638 valid timetables. Identical input, every run.
- Measured twice: a native release-mode Rust binary, and the actual compiled
.wasmbinary in a real, headless Chrome — not a simulation of either. - The "old approach" isn't a strawman — it's the exact previous implementation, kept in the codebase as a correctness reference, run against the same data.
- No cherry-picked runs. No warm-cache-only numbers. This is cold, one-shot, reproducible.
- The old-solver comparison runs its actual, unmodified source against this same dataset, bundled and loaded in the same real browser — not reimplemented, not paraphrased.
What this means
- Every optimizer click, every session filter, every re-rank: done before you notice it happened.
- No server. Not a fast server — none at all. The engine runs on your device.
- And the whole app is 96 KB — see Efficiency.
- Two orders of magnitude isn't a marketing multiplier — it's the same dataset, the same assertions, run twice, one line apart in a diff.
Correctness
- Zero double-bookings. No subject ever collides with another, in any of 19,638 results, every run.
- Deterministic. Same input, same output. No hidden randomness to hide behind.
- No duplicates. Every result in a run is genuinely distinct from every other.
- 86 automated tests hold these guarantees — including one that sorts all 19,638 results with the slow, obviously-correct reference implementation and asserts byte-for-byte agreement with the fast path. Not "close enough." Identical.