<!-- nornir:class: benchmarks -->
# skade — bench worklist (loop-until-times-down)
> Prompt item: *"skade — gå igenom källkod, hitta tidigare analyzer, skapa bencher, loopa tills du får ner tiderna."* skade = the embedded single-file Iceberg engine (default warehouse impl). This is the triage of what's already benched vs the un-benched core hot paths + concrete zero-copy candidates. **No heavy runs here** — heavy benches run on the quiet bench box (Loki/Odin) per bench policy; bench data → `/home`.
## What's ALREADY benched (the `bench/` crate — nornir-Bencher harness, migrated OFF criterion)
- **catalog throughput** — `catalog-bench embedded|nessie|polaris`, `RedbCatalog` vs competitor backends (Nessie/Polaris/RustFS-S3/Iceberg-REST); emits nornir Bencher JSON.
- **encode/write pipeline** — `pipeline.rs` (zstd codec), OSM/TPC-H synthetic workloads (`data.rs`).
- **tpch_warehouse (scalable)** — first scalable-dimension bench (see `.nornir/scalable-bench-design.md`).
- **containers** — `bench-containers` bin brings competitor backends up/down in Rust (zero-shell).
- **throughput** — `bench/src/skade_throughput.rs` = the `skade.*` benchers (this is where new micro-benches belong).
Harness rule: **NOT criterion** (deliberately removed). New benches implement `nornir::bench::api::Bencher` (`id()`+`run()`) and emit BenchRun JSON, added to `bench/src/skade_throughput.rs`.
## Un-benched CORE hot paths (skade `src/`) — GAPS to add
| Hot path | File | Why it matters | Bench to add |
|---|---|---|---|
| commit-key build | `catalog.rs::table_key`/`commit_table_key` | allocates a `String` (`format!("{cat}\x1f{ns}\x1f{table}")`) **per commit + per subscribe-filter** | `skade.catalog_key` micro-bench (in-proc, no containers) |
| cache lookups | `meta_cache.rs` · `table_cache.rs` · `pointer_cache.rs` | the read hot path; verify no per-lookup alloc/clone | `skade.cache_lookup` (hit/miss) |
| snapshot append / commit-log write | `store.rs` | the WRITE critical path (durable append) | `skade.snapshot_append` |
| atomic pointer swap | `atomic.rs` | single-file commit critical section | `skade.atomic_swap` |
| static index build/lookup | `static_index.rs` | scan/lookup path | `skade.static_index` |
## Zero-copy / algo candidates (the loop levers)
0. **create/register key dedup** — ✅ DONE. `create_table` and `register_table` each
rebuilt `table_key(name, ident)` **three times** (redb insert · commit-log record ·
L1 write-through). Now built once per op and reused by reference: **3 allocs → 1** on
the table-create/register commit path. Trivially byte-identical (same builder), covered
by `tests/catalog.rs` (`table_create_load_list_drop`, register/rename). The optimistic
commit hot path (`persist_commit`) was already single-key.
1. **commit-key zero-copy** — ✅ DONE (`table_key`) and EXTENDED to the whole builder family (`namespace_key`, `namespace_prop_key`, `namespace_prop_prefix`, `table_prefix`, `catalog_prefix`) via zero-alloc helpers `ns_path_len`/`push_ns_path`: 2 allocs → 1 each, byte-identical (guard `ns_keys_are_byte_identical_to_legacy_format`). Light bench: ~8–10× (see `catalog-key-zerocopy.md`). ✅ Harness bencher landed: `bench`-feature `pub` hook `skade_katalog::bench_keys::*` (gated, zero-cost in normal/published builds) lets the nornir-Bencher harness reach these `pub(crate)` fns — bencher `skade_catalog_key_build` in `bench/examples/nornir-bench.rs` (batched, `black_box`-pinned, `BENCH_CATALOG_KEY_ITERS` for the Loki/Odin sweep). The in-crate `#[ignore]` micro-test stays as the local sanity check.
1b. **spatial radius O(k·n)→O(k)** — ✅ DONE. `GeoIndex::query_radius` re-found every
candidate's coords with `entries.iter().find(|e| e.id == id)` (an O(n) linear scan
*per candidate*, so a k-hit radius was O(k·n)); `query_bbox` had already thrown those
coords away. Extracted a shared `for_bbox(min,max, |&Entry|)` covering-cell walk that
hands each matching `Entry` (with its `lat`/`lon`) straight to the caller — `query_bbox`
collects `e.id`, `query_radius` computes haversine from the coords in hand. Byte-identical
for unique ids (guard `tests/spatial.rs::radius_matches_brute_force_after_zero_copy_rewrite`
compares id+bit-exact distance vs a brute-force scan across 4 query points). Light bencher
landed: `skade_spatial_radius` in `bench/examples/nornir-bench.rs` (`BENCH_SPATIAL_POINTS`
/ `BENCH_SPATIAL_QUERIES` for the Loki/Odin sweep) + timing guard
`tests/spatial.rs::light_radius_timing_is_sublinear`.
2. **cache alloc audit** — confirm `meta_cache`/`table_cache`/`pointer_cache` hits don't clone the value; hand back `Arc`/`&` on the hit path.
3. **snapshot-append batching** — coalesce commit-log writes; measure single vs batched append.
4. **parquet window read** — `ParquetWindows` streaming reader is already all-core (gatling); bench a zero-copy/mmap window path.
## Next action (concrete)
Add `skade.catalog_key` + `skade.cache_lookup` (pure in-proc, no containers/S3) to `bench/src/skade_throughput.rs`, get a **light** local baseline, then run the heavy sweep on the bench box and loop the zero-copy commit-key change until the number drops (keep only if faster **and** byte-identical output).
_Not added here as runnable code: a new criterion bench would reintroduce the removed pattern, and the catalog hot paths need the async+iceberg+redb setup that already lives in the `bench/` crate — so the micro-benches belong in `skade_throughput.rs`, specced above, not a throwaway example._