code wiki / (root) / sketch_linear_counter.nx

sketch_linear_counter.nx

buildroot/runtime/sketch_linear_counter.nx

7647 B223 linesdepth 4pulls 5 transitivereach 2 importersview sourcekind sketch/demotopic sketch
docsdependenciesstructsconstsfunctions

about

sketch_linear_counter.nx -- Linear Counting (Whang-Vander Zanden 1990). Pre-HLL cardinality primitive. Simpler than HLL; sometimes more accurate at small cardinalities (n < m/10). Used by Google's BigQuery as the small-cardinality fallback. ALGORITHM: bitmap of m bits, all zero initially. add(x): set bit at index = hash(x) % m estimate(n) = -m * ln(zeros / m) where zeros = count of unset bits at query time. Derivation: if items are hashed uniformly to m buckets and we observe `zeros` empty buckets, then by occupancy theory the true count n satisfies E[zeros] = m * (1 - 1/m)^n ≈ m * e^(-n/m) inverting: n ≈ -m * ln(zeros / m). CAPABILITIES (vs HLL): - LC: accurate at n < m (uses bitmap directly) - HLL: accurate at n >> m (uses harmonic mean of register maxima) Together: BigQuery + others run LC for n < threshold, HLL for n >=. INTEGER-FIXED-POINT IMPLEMENTATION: ln(zeros/m) approximated via tabulated values keyed on (zeros*100)/m (percentage of empty bits, in [0, 100]). Table values are -ln(p/100) * 1_000_000 in PPM (negative of -log so positive). MEMORY: m bits = m/8 bytes. For m=8192: 1 KB. LOSSLESS-LANGUAGE DISCIPLINE: nx_lc_query returns ApproxI64 with NX_ENV_REL_STDDEV. Standard error per Whang 1990: sqrt(e^t - t - 1) / t where t = n/m (load). Tabulated by load class.

dependencies 3 imports · 2 importers

syscalls.nx murmur3.nx sketch_types.nx sketch_linear_counter.nx sketch_lc_vs_hll_small_n_bench.nx sketch_linear_counter_test.nx

imports: syscalls.nxmurmur3.nxsketch_types.nx

imported by: sketch_lc_vs_hll_small_n_bench.nxsketch_linear_counter_test.nx

structs

42struct LinearCounter {

consts

39const NX_LC_MIN_BITS: i64 = 64
40const NX_LC_MAX_BITS: i64 = 16777216 // 2 MB cap

functions

51func nx_lc_is_pow2(n: i64) -> i64 {
called by 1: nx_lc_alloc
57func nx_lc_alloc(m_bits: i64, seed: i64) -> *LinearCounter {
called by 3: mainnx_lc_mergemain calls 1: nx_lc_is_pow2
77func nx_lc_set_bit(lc: *LinearCounter, bit_idx: i64) -> i64 {
called by 1: nx_lc_add
84func nx_lc_get_bit(lc: *LinearCounter, bit_idx: i64) -> i64 {
92func nx_lc_add(lc: *LinearCounter, key: *u8, len: i64) -> i64 {
called by 2: mainmain calls 2: murmur3_32nx_lc_set_bit
104func nx_lc_popcount_byte(b: i64) -> i64 {
called by 1: nx_lc_zeros
114func nx_lc_zeros(lc: *LinearCounter) -> i64 {
142func nx_lc_neg_ln_ppm(zeros_x_100: i64, m: i64) -> i64 {
called by 1: nx_lc_estimate
167func nx_lc_estimate(lc: *LinearCounter) -> i64 {
181func nx_lc_stddev_rel_ppb(load_pct: i64) -> i64 {
called by 1: nx_lc_query
193func nx_lc_query(lc: *LinearCounter) -> *ApproxI64 {
208func nx_lc_merge(a: *LinearCounter, b: *LinearCounter) -> *LinearCounter {
called by 1: main calls 1: nx_lc_alloc
221func nx_lc_memory_bytes(lc: *LinearCounter) -> i64 {