Skip to content

top-k: heap = m·k per deployment; heap a runtime knob - #182

Merged
zzylol merged 4 commits into
mainfrom
topk-heap-mk
Oct 7, 2026
Merged

zzylol merged 4 commits into
mainfrom
topk-heap-mk

Conversation

@zzylol

@zzylol zzylol commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on #180.

Why

A merged top-k answer loses keys: each window's heap keeps only its own top k, so a key in the global top k can be missing from every window's heap. #179 reads merge curves for that, but the synthetic workload's top-k instances hold 1.8e9 to 1.7e11 items, far past any measurable merge curve. The heap was also tied to the graded k by a compile-time constant (CMS_HEAP_TOP_K = 32).

Approved design: a deployment that merges m windows keeps a heap of m·k, and such a merged answer reads as one sketch.

What

sketch-bench

  • heap= is a runtime config param on cms-heap (CmsHeapParams) and on a new countsketch-heap vocabulary (CsHeapParams). The default and minimum is TOPK_K = 32. asap_sketchlib's CMSHeap::new/CSHeap::new already take the capacity, so no upstream change is needed.
  • The answered and graded k stays TOPK_K: a larger heap answers its heaviest k (top_k).
  • Memory counts heap × HHItem. UnivMon and the CLI's TopkGT use TOPK_K.

rqe-optimizer

  • Heap per deployment: has_heap, heap_capacity, TOPK_K. A heap top-k deployment answering from m merged windows keeps m·k (m = 1 gives k).
  • Candidates: one heap per distinct m among a window's RAQEs. A RAQE is eligible only where heap ≥ (L/x)·k.
  • Costs: from the cost table's heap rows, linear in the heap between and past the measured sizes (at_heap). With only one measured size, the smallest row that holds the heap serves at its own size and cost.
  • Accuracy: with heap ≥ m·k, a merged answer reads as unmerged: the plain curve, then rqe-optimizer: fall back to each sketch's guarantee where the study measured nothing #180's guarantee fallback with one window. This assumes the merged heaps still hold the true top k; it isn't a guarantee.
    • SaturationCurves::load requires merge curves only for KLL.
    • Curves match configs ignoring heap; they're measured at a heap of k.
    • check_cost_table skips heaps other than k (cost-only rows).
  • TOPK_ENTRIES (query output) = TOPK_K.

study_saturation.py

  • --phase optimizer-cost measures top-k at TOPK_HEAPS = (32, 128, 512, 2048).
  • Fix: --resume over a narrower grid (--families, --points-from) used to rewrite saturation.csv with only the current points, while their curves were kept, so SaturationCurves::load failed. It now keeps the other points' rows. This hit the rqe-optimizer: one study for sketch cost and accuracy (#174) #178 rerun on clnode109.

Decisions

  • The param is named heap. top_k is still refused by name: k is not a knob.
  • Interpolation is linear per op (memory, insert, merge, query) and extrapolates linearly from the two nearest measured sizes. Accuracy fields come from the smallest heap's row.
  • Accuracy curves stay at heap = k, the default grid configs.

Verification

  • cargo test: sketch-bench (90, including heap below k refused, a larger heap answers its heaviest k, and heap memory), aqpbm-core, aqpbm-cli, rqe-optimizer (72, including interpolation and extrapolation, the single-row fallback, m·k candidates and eligibility, heap ≥ m·k reading the plain curve, load without top-k merge curves, and heap rows in check_cost_table).
  • python3 -m unittest test_study_saturation (30): heap rows in optimizer-cost, and the --resume summary fix, which fails on the old script.
  • cargo clippy --all-targets -D warnings and fmt are clean. approxbench sketchbench --config "rows=3 cols=1024 heap=128" runs for both variants; heap=8 is refused.

Experiments it needs

  • The top-k cost table at heaps 32/128/512/2048: study_saturation.py --phase optimizer-cost, timed, alone on the machine.
  • No top-k merge curves. Top-k accuracy curves at heap = k are unchanged.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FhcWJExZcmVqS6r6rEtjis

…ows on --resume

sketch-bench:
- The top-k heap's capacity is a runtime param (`heap=` on cms-heap and the
  new countsketch-heap vocabulary, default and minimum TOPK_K = 32). The
  answered and graded k stays TOPK_K: a larger heap answers its heaviest k.
  Memory counts heap x HHItem. UnivMon and the CLI's TopkGT read TOPK_K.

rqe-optimizer:
- A heap top-k deployment answering from m merged windows keeps m·k.
  Candidates carry one heap per distinct m among a window's RAQEs, and a RAQE
  is eligible only where the heap holds its m·k.
- Costs come from the cost table's heap rows, linear in the heap between and
  past the measured sizes. One measured size serves any heap up to it.
- With heap >= m·k a merged answer reads as one sketch (plain curve, then the
  guarantee fallback), so top-k needs no merge curves; KLL still does. Curves
  match configs ignoring `heap`. check_cost_table skips heaps other than k.

study_saturation:
- --phase optimizer-cost measures top-k at heaps 32/128/512/2048
  (TOPK_HEAPS).
- --resume over a narrower grid keeps the saturation.csv rows of the other
  points, as the curve files keep theirs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhcWJExZcmVqS6r6rEtjis
@zzylol
zzylol changed the base branch from theory-bounds to main October 7, 2026 19:18
zzylol and others added 3 commits October 7, 2026 19:26
… flag single-heap pricing

From the first review of #182:
- AutoSketch never merges, so its search space holds heap-k rows only;
  larger heaps aren't an axis it tunes.
- Every heap row priced from a config carries the heap-k row's accuracy,
  measured hit or interpolated, since the curves are measured at k.
- check_cost_table flags a heap top-k config priced at one heap only: no
  merged candidate could be built from it.
- heap reads any JSON number (128 or 128.0).
- --resume keeps other points' saturation.csv rows in the accuracy phase
  only, tolerating older column sets.
- UnivMon's top-k cut reuses top_k.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhcWJExZcmVqS6r6rEtjis
…needed

Round-2 review of #182:
- at_heap panics, naming the optimizer-cost rerun, when a config is priced
  at one heap smaller than the heap a candidate needs; it no longer drops
  the candidate.
- heap_rows_by_shape panics on two rows at one heap (no heap and heap=k);
  AutoSketch's grid runs the same check.
- heap_needed(lookback, window) and Deployment::heap_needed give m · k for
  is_eligible, the per-window candidate heaps and SaturationCurves::accuracy.
- top_k selects the heaviest k with select_nth_unstable and sorts only
  those; the query cost is benchmarked.
- CountSketchParams no longer owns countsketch-heap-* variants (pinned in
  each_algorithm_owns_its_variants_and_no_neighbours).
- load_needs_no_merge_curves_for_heap_topk no longer splits another test
  from its doc comment; config_params' doc is one comment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhcWJExZcmVqS6r6rEtjis
…; CmsParams leaves cms-heap

- --phase optimizer-cost rewrites each sketch row's accuracy as the mean
  over seeds 1..--seeds of one accuracy run at COST_N: the run the curves
  make at that N. One seed-42 run against a three-seed mean failed
  check_cost_table on seed variance alone (top-k rows=3 cols=256, KLL
  k=200). Top-k heaps above k take the heap-k mean; exact rows keep 0.
- CmsParams no longer claims cms-heap-* variants, as CountSketchParams no
  longer claims countsketch-heap-*.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhcWJExZcmVqS6r6rEtjis
@zzylol
zzylol merged commit a2e0455 into main Oct 7, 2026
2 checks passed
zzylol added a commit that referenced this pull request Oct 7, 2026
… top-k heap memory per 0.3.0 (#183)

* deps: asap_sketchlib 0.3.0 (capacity-independent top-k heap)

asap_sketchlib 0.2.2's HHHeap rebuilt its key index after every accepted
update, so CMSHeap/CSHeap inserts cost O(k) in the heap capacity; 0.3.0
(asap_sketchlib#107) patches the index through each sift. With #182
sizing top-k heaps to m·k, the 0.2.2 cost grew with every merged window.

- Pin asap_sketchlib 0.3.0 (aqpbm-planeval already used it).
- Hydra: 0.3.0 fixes a grid's key columns at construction (with_schema)
  and queries positionally. hydra_shared's new_hydra/update/query/merge
  set the schema from the first record (one column per ';'-joined label),
  constrain the probe's leading label columns (the scored subpopulations
  group by column 0) and skip shards that saw no record. A probe now
  matches its column only, not an equal value in another label column.
- DDSketch::merge now returns a Result; the wrappers expect it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhcWJExZcmVqS6r6rEtjis

* hydra: the label-column count fixes the schema at construction

From the first review of #183:
- build_hydra_* take the stream's label-column count (its first record's,
  label_columns) and new_hydra builds the schema once. The placeholder
  schema, the rebuild on the first update and the clone-on-empty merge
  are gone; grids built from one ParamSet and stream merge as before.
- A record keeps an empty label in its own column (no dropped segments
  shifting the rest). A record or probe of another width is refused,
  naming the variant and both widths, instead of being resized.
- A zero label-column count is a BuildError.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhcWJExZcmVqS6r6rEtjis

* top-k memory counts 0.3's heap index; one asap_sketchlib pin; Hydra width checked at build

From the second review of #183:
- aqpbm_core::hh_heap_bytes(capacity, item_bytes): what 0.3's HHHeap
  holds per resident, the HHItem, its u64 digest (slots) and its share of
  the position index (hashbrown buckets at 7/8 load, power of two, each a
  u64 digest, a SmallVec<[usize; 2]> and a control byte). cms_heap,
  cs_heap, UnivMon and aqpbm-planeval use it in place of heap x HHItem.
- aqpbm-planeval takes the workspace asap_sketchlib; stale 0.2.2 notes
  fixed (planeval Cargo.toml, hydra_kll).
- build_hydra_* check every record's label width and return a BuildError
  naming the variant and both widths; the in-closure panic is a backstop.
- polars_shared says its baseline keeps 0.2.2's any-column subset keys.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhcWJExZcmVqS6r6rEtjis

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
zzylol added a commit that referenced this pull request Oct 7, 2026
… in the planning flow

From review on #181:
- skew_summary.csv's kind, weight and range columns, and how ranges map onto
  data_shape (keyed by grouping: the worst case over its RAQEs' ranges).
- A 'Fanning out over machines' section and scripts/merge_study_parts.py,
  which concatenates per-machine run directories, refusing a point in two
  parts.
- Brought up to date: top-k needs no merge curves (heap m·k, #182), the
  guarantee fallback is merged (#180), main is through #183.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhcWJExZcmVqS6r6rEtjis
zzylol added a commit that referenced this pull request Oct 11, 2026
…181)

* docs: end-to-end planning flow from traces to an rqe-optimizer plan

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: kind/weight/range and data_shape, fanning out, and #180/#182/#183 in the planning flow

From review on #181:
- skew_summary.csv's kind, weight and range columns, and how ranges map onto
  data_shape (keyed by grouping: the worst case over its RAQEs' ranges).
- A 'Fanning out over machines' section and scripts/merge_study_parts.py,
  which concatenates per-machine run directories, refusing a point in two
  parts.
- Brought up to date: top-k needs no merge curves (heap m·k, #182), the
  guarantee fallback is merged (#180), main is through #183.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhcWJExZcmVqS6r6rEtjis

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant