Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 6 additions & 36 deletions sketch-bench/src/wrappers/hydra_kll/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,39 +11,9 @@ pub(crate) use super::hydra_shared::{
check_grid, grid_overhead_bytes, label_columns, labels, new_hydra, query, update,
};

// Level count and decay of an `asap_sketchlib::KLL`. Both are private constants
// in the library, reproduced here because a cell's footprint is a function of
// them and the library exposes no accessor for its own capacity.
const KLL_MAX_LEVELS: usize = 61;

const KLL_CAPACITY_DECAY: f64 = 2.0 / 3.0;

/// The `m` the library's `init_kll` passes, its minimum level capacity, and the
/// floor it silently raises a smaller `k` to. Same cell as the `kll` rows, so
/// the bound is theirs: see [`crate::wrappers::kll::LIB_K_MIN`].
const KLL_MIN_LEVEL: usize = crate::wrappers::kll::LIB_K_MIN as usize;

/// The library clamps `k` to this before sizing, so a larger `k` buys nothing.
const KLL_MAX_CACHEABLE_K: usize = crate::wrappers::kll::LIB_K_MAX as usize;

/// Retained slots one KLL cell allocates at construction — `KLL::init` boxes a
/// slice of this length once and never grows it. A line-for-line copy of the
/// library's private `compute_max_capacity` (unchanged from 0.2.2 to 0.3.0), so
/// a claim about the pinned version, not a bound.
fn kll_cell_slots(k: u32) -> usize {
// `init_internal` normalises before sizing: `m` floors `k`, and `k` is
// capped. Reproduced so an out-of-range `k` reports the footprint the
// library actually allocates.
let m = KLL_MIN_LEVEL;
let k = (k as usize).max(m).min(KLL_MAX_CACHEABLE_K) as f64;
let mut total = 0usize;
let mut scale = 1.0f64;
for _ in 0..KLL_MAX_LEVELS {
total += (k * scale).ceil().max(m as f64) as usize;
scale *= KLL_CAPACITY_DECAY;
}
total
}
pub(crate) use crate::wrappers::kll::{kll_lib_slots as kll_cell_slots, KLL_LIB_MAX_LEVELS};
#[cfg(test)]
use crate::wrappers::kll::{KLL_LIB_MAX_CACHEABLE_K, KLL_LIB_MIN_LEVEL};

#[cfg(test)]
mod tests {
Expand Down Expand Up @@ -125,11 +95,11 @@ mod tests {
assert_eq!(kll_cell_slots(200), 580 + 424);
// Monotone in k, and never below the floor times the level count.
assert!(kll_cell_slots(400) > kll_cell_slots(200));
assert_eq!(kll_cell_slots(1), KLL_MIN_LEVEL * KLL_MAX_LEVELS);
assert_eq!(kll_cell_slots(1), KLL_LIB_MIN_LEVEL * KLL_LIB_MAX_LEVELS);
// Past the library's clamp, a larger k buys no more slots.
assert_eq!(
kll_cell_slots(KLL_MAX_CACHEABLE_K as u32),
kll_cell_slots(KLL_MAX_CACHEABLE_K as u32 + 5_000)
kll_cell_slots(KLL_LIB_MAX_CACHEABLE_K as u32),
kll_cell_slots(KLL_LIB_MAX_CACHEABLE_K as u32 + 5_000)
);
}

Expand Down
6 changes: 4 additions & 2 deletions sketch-bench/src/wrappers/hydra_kll/sketchlib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,13 @@ impl HydraKll {

/// Retained slots per cell times the grid area, plus the level index every
/// cell carries. Analytic because the cell allocates once — see
/// `kll_cell_slots` in this crate's `hydra_kll` module for the per-cell count.
/// `kll_lib_slots` in this crate's `kll` module for the per-cell count. Unlike
/// the `kll-percall` lib rows (`kll_lib_bytes`), each cell's merge buffer of
/// `cell_k` items is not counted.
pub fn memory_hydra_kll(sketch: &HydraKll) -> usize {
let p = &sketch.params;
let per_cell = kll_cell_slots(p.cell_k) * std::mem::size_of::<f64>()
+ (KLL_MAX_LEVELS + 1) * std::mem::size_of::<usize>();
+ (KLL_LIB_MAX_LEVELS + 1) * std::mem::size_of::<usize>();
p.rows * p.cols * per_cell + grid_overhead_bytes(p.rows, p.cols)
}

Expand Down
75 changes: 72 additions & 3 deletions sketch-bench/src/wrappers/kll/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,65 @@ pub mod oxide;
pub mod polars;
pub mod sketchlib;

/// figure is `heap_bytes_net`, which the tracking allocator measures; this is
/// the derived upper bound beside it, and the two are meant to be compared.
/// One rule for all four rows, so the column answers one question.
/// A nominal footprint for the `oxide` rows, `4 · k` items: `sketch_oxide`'s
/// KLL grows its buffers as it fills, so there is no fixed allocation to
/// report. The `lib` rows report the allocation instead ([`kll_lib_bytes`]).
fn kll_footprint<T>(k: u32) -> usize {
(k as usize) * std::mem::size_of::<T>() * 4
}

// Level count and decay of an `asap_sketchlib::KLL`. Both are private constants
// in the library, reproduced here because its footprint is a function of them
// and the library exposes no accessor for its own capacity.
pub(crate) const KLL_LIB_MAX_LEVELS: usize = 61;

const KLL_LIB_CAPACITY_DECAY: f64 = 2.0 / 3.0;

/// The `m` the library's `init_kll` passes (`init(k, 8)`), its minimum level
/// capacity, and the floor it silently raises a smaller `k` to.
pub(crate) const KLL_LIB_MIN_LEVEL: usize = 8;

// The lib rows refuse a `k` below the library's floor rather than let it be
// raised silently, so the two must agree.
const _: () = assert!(LIB_K_MIN as usize == KLL_LIB_MIN_LEVEL);

/// The library clamps `k` to this before sizing, so a larger `k` buys nothing.
pub(crate) const KLL_LIB_MAX_CACHEABLE_K: usize = LIB_K_MAX as usize;

/// The `k` the library sizes with: `init_internal` floors it at `m` and caps it.
fn kll_lib_k(k: u32) -> usize {
(k as usize).clamp(KLL_LIB_MIN_LEVEL, KLL_LIB_MAX_CACHEABLE_K)
}

/// Retained slots an `asap_sketchlib::KLL` allocates at construction:
/// `KLL::init` boxes a slice of this length once and never grows it. A
/// line-for-line copy of the library's private `compute_max_capacity`
/// (unchanged from 0.2.2 to 0.3.0), so a claim about the pinned version, not a
/// bound.
pub(crate) fn kll_lib_slots(k: u32) -> usize {
let m = KLL_LIB_MIN_LEVEL;
let k = kll_lib_k(k) as f64;
let mut total = 0usize;
let mut scale = 1.0f64;
for _ in 0..KLL_LIB_MAX_LEVELS {
total += (k * scale).ceil().max(m as f64) as usize;
scale *= KLL_LIB_CAPACITY_DECAY;
}
total
}

/// Bytes an `asap_sketchlib::KLL<T>` allocates at construction, whatever it
/// holds later (`init_internal`): the retained-item slice, the level index,
/// and the merge buffer's capacity of `k` items. Independent of the item count,
/// so a window's sketch costs the same however many values it sees. The
/// sketch's own allocation only: the cdf row's prepared CDF table, built for
/// queries, is not counted (nor is the oxide cdf row's).
fn kll_lib_bytes<T>(k: u32) -> usize {
kll_lib_slots(k) * std::mem::size_of::<T>()
+ (KLL_LIB_MAX_LEVELS + 1) * std::mem::size_of::<usize>()
+ kll_lib_k(k) * std::mem::size_of::<T>()
}

/// The range `asap_sketchlib::KLL::init` keeps a `k` in. Below the floor it
/// raises `k` to `m`, above the ceiling it caps; both silently. Reproduced here
/// so the two `lib` rows refuse instead, which is the only way the `k` in the
Expand Down Expand Up @@ -48,3 +100,20 @@ fn query_cdf(table: &[(f64, f64)], phi: f64, min: f64, max: f64) -> f64 {
let idx = table.partition_point(|(_, cum)| *cum < phi);
table[idx.min(table.len() - 1)].0
}

#[cfg(test)]
mod tests {
use super::*;

/// The `lib` footprint is what `init_internal` allocates. At k = 269 that
/// is 9648 + 496 + 2152 = 12296 B, the figure `aqpbm-planeval` derives for
/// the same library independently.
#[test]
fn lib_footprint_is_the_library_allocation() {
assert_eq!(kll_lib_bytes::<f64>(269), 9648 + 496 + 2152);
// ceil(50 · (2/3)^i) is 50, 34, 23, 15, 10, then 56 levels at m = 8.
assert_eq!(kll_lib_slots(50), 132 + 56 * 8);
// Below the floor, the library sizes as k = m.
assert_eq!(kll_lib_bytes::<i64>(1), kll_lib_bytes::<i64>(LIB_K_MIN));
}
}
4 changes: 2 additions & 2 deletions sketch-bench/src/wrappers/kll/sketchlib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub fn memory_kll_lib_per_call<T>(sketch: &KllLibPerCall<T>) -> usize
where
T: asap_sketchlib::common::numerical::NumericalValue + QuantileValue,
{
kll_footprint::<T>(sketch.k)
kll_lib_bytes::<T>(sketch.k)
}

pub struct KllLibCdf<T: asap_sketchlib::common::numerical::NumericalValue = i64> {
Expand All @@ -73,7 +73,7 @@ pub fn memory_kll_lib_cdf<T>(sketch: &KllLibCdf<T>) -> usize
where
T: asap_sketchlib::common::numerical::NumericalValue + QuantileValue,
{
kll_footprint::<T>(sketch.k)
kll_lib_bytes::<T>(sketch.k)
}

pub fn insert_kll_lib_per_call<T>(
Expand Down
Loading