nx_sketch_cpc.nx
buildroot/runtime/nx_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 · 1 importers
imports: nx_syscalls.nxnx_murmur3.nxnx_bits.nxnx_sketch_hash_map.nxnx_sketch_types.nx
imported by: nx_bench_hll_vs_cpc.nx
structs
| 60 | struct Cpc |
consts
| 50 | const NX_MAGIC_1000000: i64 = 1000000 |
| 51 | const NX_MAGIC_1000000000: i64 = 1000000000 |
| 52 | const NX_MAGIC_682700000: i64 = 682700000 |
| 54 | const NX_CPC_MIN_LG_K: i64 = 4 |
| 55 | const NX_CPC_MAX_LG_K: i64 = 14 |
| 56 | const NX_CPC_WINDOW: i64 = 32 // rows per column (bits of rho) |
| 57 | const NX_CPC_SEED_HI: i64 = 0x9747B28C |
| 58 | const NX_CPC_SEED_LO: i64 = 0x36185EC0 |
functions
| 72 | func nx_cpc_clz32(x: i64) -> i64 |
| 81 | func nx_cpc_alloc(lg_k: i64, hashmap_cap: i64, seed: i64) -> *Cpc |
| 104 | func nx_cpc_coupon(c: *Cpc, key: *u8, len: i64) -> i64 |
| 120 | func nx_cpc_hip_add(c: *Cpc, n_before: i64) -> i64 called by 1: nx_cpc_add |
| 130 | func nx_cpc_add(c: *Cpc, key: *u8, len: i64) -> i64 |
| 144 | func nx_cpc_estimate(c: *Cpc) -> i64 |
| 152 | func nx_cpc_isqrt(x: i64) -> i64 called by 1: nx_cpc_stddev_rel_ppb |
| 169 | func nx_cpc_stddev_rel_ppb(c: *Cpc) -> i64 |
| 175 | func nx_cpc_query(c: *Cpc) -> *ApproxI64 |
| 186 | func nx_cpc_n_coupons(c: *Cpc) -> i64 calls 1: nx_hmap_size |
| 190 | func nx_cpc_big_m(c: *Cpc) -> i64 |
| 194 | func nx_cpc_memory_bytes(c: *Cpc) -> i64 |