sketch_cpc.nx
buildroot/runtime/sketch_cpc.nx
about
sketch_cpc.nx -- CPC sparse-mode cardinality sketch (Lang 2017 / FM85).
"Back to the Future: An Even More Nearly Optimal Cardinality Estimation
Algorithm" -- arXiv:1708.06839. CPC is the headline cardinality stomp
over HLL: same memory, tighter rel-stddev via the HIP estimator.
THIS V1 SHIPS THE SPARSE MODE + HIP ESTIMATOR:
- sparse mode: store coupons in a hash set until capacity reached
- HIP estimator: kappa accumulates 1/theta on every distinct coupon
DENSE MODE (pinned + sliding) queued for v2. Sparse mode alone is
sufficient for cardinalities up to ~K coupons (typical K=4096 -> exact
for n < ~3000, then accurate-via-HIP up to ~tens-of-thousands).
COUPON DERIVATION:
hash(key) -> 64-bit value
column = (hash >> 32) & (m - 1) (high bits choose register column)
row = clz32(hash & 0xFFFFFFFF) + 1 (low bits drive rho, capped at w)
coupon_id = column * w + row (unique pair encoding)
HIP ESTIMATOR (Cohen 2015, Lang 2017):
For each distinct coupon arrival i (0..n-1):
theta_i = (M - i) / M where M = m * w (total possible coupons)
kappa += 1 / theta_i = M / (M - i)
estimate = kappa
At i = 0 (first coupon): theta = 1.0; kappa += 1.
At i = M-1: theta = 1/M; kappa += M.
COMPLEMENTS the cardinality family:
- HLL/LC/KMV/Theta: classical estimators, lots of variance
- CPC (this): HIP-based, near-optimal variance via online accounting
COMPOSES against sketch_hash_map for sparse coupon storage.
LOSSLESS-LANGUAGE DISCIPLINE: estimate has rel_stddev approx
1/sqrt(M) at confidence 0.6827. For m=128, w=32 (M=4096): ~1.56% rel.
For HLL_8 lg_k=7 (m=128): ~9.2% rel. CPC stomp ~6x improvement.
dependencies 5 imports · 2 importers
imports: syscalls.nxmurmur3.nxnx_bits.nxsketch_hash_map.nxsketch_types.nx
imported by: bench_hll_vs_cpc.nxsketch_cpc_test.nx
structs
| 51 | struct Cpc { |
consts
| 45 | const NX_CPC_MIN_LG_K: i64 = 4 |
| 46 | const NX_CPC_MAX_LG_K: i64 = 14 |
| 47 | const NX_CPC_WINDOW: i64 = 32 // rows per column (bits of rho) |
| 48 | const NX_CPC_SEED_HI: i64 = 0x9747B28C |
| 49 | const NX_CPC_SEED_LO: i64 = 0x36185EC0 |
functions
| 64 | func nx_cpc_clz32(x: i64) -> i64 { |
| 73 | func nx_cpc_alloc(lg_k: i64, hashmap_cap: i64, seed: i64) -> *Cpc { |
| 96 | func nx_cpc_coupon(c: *Cpc, key: *u8, len: i64) -> i64 { |
| 112 | func nx_cpc_hip_add(c: *Cpc, n_before: i64) -> i64 {
called by 1: nx_cpc_add |
| 122 | func nx_cpc_add(c: *Cpc, key: *u8, len: i64) -> i64 { |
| 136 | func nx_cpc_estimate(c: *Cpc) -> i64 { |
| 144 | func nx_cpc_isqrt(x: i64) -> i64 {
called by 1: nx_cpc_stddev_rel_ppb |
| 161 | func nx_cpc_stddev_rel_ppb(c: *Cpc) -> i64 { |
| 167 | func nx_cpc_query(c: *Cpc) -> *ApproxI64 { |
| 178 | func nx_cpc_n_coupons(c: *Cpc) -> i64 { |
| 182 | func nx_cpc_big_m(c: *Cpc) -> i64 {
called by 1: main |
| 186 | func nx_cpc_memory_bytes(c: *Cpc) -> i64 { |