xref: /netbsd-src/external/mpl/bind/dist/lib/isc/histo.c (revision c2d18a95984d618d94f261361000142d37ac53d9)
1*c2d18a95Schristos /*	$NetBSD: histo.c,v 1.3 2025/01/27 02:16:05 christos Exp $	*/
29689912eSchristos 
39689912eSchristos /*
49689912eSchristos  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
59689912eSchristos  *
69689912eSchristos  * SPDX-License-Identifier: MPL-2.0
79689912eSchristos  *
89689912eSchristos  * This Source Code Form is subject to the terms of the Mozilla Public
99689912eSchristos  * License, v. 2.0. If a copy of the MPL was not distributed with this
109689912eSchristos  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
119689912eSchristos  *
129689912eSchristos  * See the COPYRIGHT file distributed with this work for additional
139689912eSchristos  * information regarding copyright ownership.
149689912eSchristos  */
159689912eSchristos 
169689912eSchristos #include <assert.h>
179689912eSchristos #include <errno.h>
189689912eSchristos #include <math.h>
199689912eSchristos #include <stdbool.h>
209689912eSchristos #include <stdint.h>
219689912eSchristos #include <stdio.h>
229689912eSchristos #include <stdlib.h>
239689912eSchristos #include <string.h>
249689912eSchristos 
259689912eSchristos #include <isc/atomic.h>
269689912eSchristos #include <isc/histo.h>
279689912eSchristos #include <isc/magic.h>
289689912eSchristos #include <isc/mem.h>
299689912eSchristos #include <isc/tid.h>
309689912eSchristos 
319689912eSchristos #define HISTO_MAGIC	    ISC_MAGIC('H', 's', 't', 'o')
329689912eSchristos #define HISTO_VALID(p)	    ISC_MAGIC_VALID(p, HISTO_MAGIC)
339689912eSchristos #define HISTOMULTI_MAGIC    ISC_MAGIC('H', 'g', 'M', 't')
349689912eSchristos #define HISTOMULTI_VALID(p) ISC_MAGIC_VALID(p, HISTOMULTI_MAGIC)
359689912eSchristos 
369689912eSchristos /*
379689912eSchristos  * Natural logarithms of 2 and 10 for converting precisions between
389689912eSchristos  * binary and decimal significant figures
399689912eSchristos  */
409689912eSchristos #define LN_2  0.693147180559945309
419689912eSchristos #define LN_10 2.302585092994045684
429689912eSchristos 
439689912eSchristos /*
449689912eSchristos  * The chunks array has a static size for simplicity, fixed as the
459689912eSchristos  * number of bits in a value. That means we waste a little extra space
469689912eSchristos  * that could be saved by omitting the exponents that are covered by
479689912eSchristos  * `sigbits`. The following macros calculate (at run time) the exact
489689912eSchristos  * number of buckets when we need to do accurate bounds checks.
499689912eSchristos  *
509689912eSchristos  * For a discussion of the floating point terminology, see the
519689912eSchristos  * commmentary on `value_to_key()` below.
529689912eSchristos  *
539689912eSchristos  * We often use the variable names `c` for chunk and `b` for bucket.
549689912eSchristos  */
559689912eSchristos #define CHUNKS 64
569689912eSchristos 
579689912eSchristos #define DENORMALS(hg) ((hg)->sigbits - 1)
589689912eSchristos #define MANTISSAS(hg) (1 << (hg)->sigbits)
599689912eSchristos #define EXPONENTS(hg) (CHUNKS - DENORMALS(hg))
609689912eSchristos #define BUCKETS(hg)   (EXPONENTS(hg) * MANTISSAS(hg))
619689912eSchristos 
629689912eSchristos #define MAXCHUNK(hg)  EXPONENTS(hg)
639689912eSchristos #define CHUNKSIZE(hg) MANTISSAS(hg)
649689912eSchristos 
65*c2d18a95Schristos #ifdef _LP64
669689912eSchristos typedef atomic_uint_fast64_t hg_bucket_t;
67*c2d18a95Schristos #else
68*c2d18a95Schristos typedef atomic_uint_fast32_t hg_bucket_t;
69*c2d18a95Schristos #endif
709689912eSchristos typedef atomic_ptr(hg_bucket_t) hg_chunk_t;
719689912eSchristos 
729689912eSchristos struct isc_histo {
739689912eSchristos 	uint magic;
749689912eSchristos 	uint sigbits;
759689912eSchristos 	isc_mem_t *mctx;
769689912eSchristos 	hg_chunk_t chunk[CHUNKS];
779689912eSchristos };
789689912eSchristos 
799689912eSchristos struct isc_histomulti {
809689912eSchristos 	uint magic;
819689912eSchristos 	uint size;
829689912eSchristos 	isc_histo_t *hg[];
839689912eSchristos };
849689912eSchristos 
859689912eSchristos /**********************************************************************/
869689912eSchristos 
879689912eSchristos void
889689912eSchristos isc_histo_create(isc_mem_t *mctx, uint sigbits, isc_histo_t **hgp) {
899689912eSchristos 	REQUIRE(sigbits >= ISC_HISTO_MINBITS);
909689912eSchristos 	REQUIRE(sigbits <= ISC_HISTO_MAXBITS);
919689912eSchristos 	REQUIRE(hgp != NULL);
929689912eSchristos 	REQUIRE(*hgp == NULL);
939689912eSchristos 
949689912eSchristos 	isc_histo_t *hg = isc_mem_get(mctx, sizeof(*hg));
959689912eSchristos 	*hg = (isc_histo_t){
969689912eSchristos 		.magic = HISTO_MAGIC,
979689912eSchristos 		.sigbits = sigbits,
989689912eSchristos 	};
999689912eSchristos 	isc_mem_attach(mctx, &hg->mctx);
1009689912eSchristos 
1019689912eSchristos 	*hgp = hg;
1029689912eSchristos }
1039689912eSchristos 
1049689912eSchristos void
1059689912eSchristos isc_histo_destroy(isc_histo_t **hgp) {
1069689912eSchristos 	REQUIRE(hgp != NULL);
1079689912eSchristos 	REQUIRE(HISTO_VALID(*hgp));
1089689912eSchristos 
1099689912eSchristos 	isc_histo_t *hg = *hgp;
1109689912eSchristos 	*hgp = NULL;
1119689912eSchristos 
1129689912eSchristos 	for (uint c = 0; c < CHUNKS; c++) {
1139689912eSchristos 		if (hg->chunk[c] != NULL) {
1149689912eSchristos 			isc_mem_cput(hg->mctx, hg->chunk[c], CHUNKSIZE(hg),
1159689912eSchristos 				     sizeof(hg_bucket_t));
1169689912eSchristos 		}
1179689912eSchristos 	}
1189689912eSchristos 	isc_mem_putanddetach(&hg->mctx, hg, sizeof(*hg));
1199689912eSchristos }
1209689912eSchristos 
1219689912eSchristos /**********************************************************************/
1229689912eSchristos 
1239689912eSchristos uint
1249689912eSchristos isc_histo_sigbits(isc_histo_t *hg) {
1259689912eSchristos 	REQUIRE(HISTO_VALID(hg));
1269689912eSchristos 	return hg->sigbits;
1279689912eSchristos }
1289689912eSchristos 
1299689912eSchristos /*
1309689912eSchristos  * use precomputed logs and builtins to avoid linking with libm
1319689912eSchristos  */
1329689912eSchristos 
1339689912eSchristos uint
1349689912eSchristos isc_histo_bits_to_digits(uint bits) {
1359689912eSchristos 	REQUIRE(bits >= ISC_HISTO_MINBITS);
1369689912eSchristos 	REQUIRE(bits <= ISC_HISTO_MAXBITS);
1379689912eSchristos 	return floor(1.0 - (1.0 - bits) * LN_2 / LN_10);
1389689912eSchristos }
1399689912eSchristos 
1409689912eSchristos uint
1419689912eSchristos isc_histo_digits_to_bits(uint digits) {
1429689912eSchristos 	REQUIRE(digits >= ISC_HISTO_MINDIGITS);
1439689912eSchristos 	REQUIRE(digits <= ISC_HISTO_MAXDIGITS);
1449689912eSchristos 	return ceil(1.0 - (1.0 - digits) * LN_10 / LN_2);
1459689912eSchristos }
1469689912eSchristos 
1479689912eSchristos /**********************************************************************/
1489689912eSchristos 
1499689912eSchristos /*
1509689912eSchristos  * The way we map buckets to keys is what gives the histogram a
1519689912eSchristos  * consistent relative error across the whole range of `uint64_t`.
1529689912eSchristos  * The mapping is log-linear: a chunk key is the logarithm of part
1539689912eSchristos  * of the value (in other words, chunks are spaced exponentially);
1549689912eSchristos  * and a bucket within a chunk is a linear function of another part
1559689912eSchristos  * of the value.
1569689912eSchristos  *
1579689912eSchristos  * This log-linear spacing is similar to the size classes used by
1589689912eSchristos  * jemalloc. It is also the way floating point numbers work: the
1599689912eSchristos  * exponent is the log part, and the mantissa is the linear part.
1609689912eSchristos  *
1619689912eSchristos  * So, a chunk number is the log (base 2) of a `uint64_t`, which is
1629689912eSchristos  * between 0 and 63, which is why there are up to 64 chunks. In
1639689912eSchristos  * floating point terms the chunk number is the exponent. The
1649689912eSchristos  * histogram's number of significant bits is the size of the
1659689912eSchristos  * mantissa, which indexes buckets within each chunk.
1669689912eSchristos  *
1679689912eSchristos  * A fast way to get the logarithm of a positive integer is CLZ,
1689689912eSchristos  * count leading zeroes.
1699689912eSchristos  *
1709689912eSchristos  * Chunk zero is special. Chunk 1 covers values between `CHUNKSIZE`
1719689912eSchristos  * and `CHUNKSIZE * 2 - 1`, where `CHUNKSIZE == exponent << sigbits
1729689912eSchristos  * == 1 << sigbits`. Each chunk has CHUNKSIZE buckets, so chunk 1 has
1739689912eSchristos  * one value per bucket. There are CHUNKSIZE values before chunk 1
1749689912eSchristos  * which map to chunk 0, so it also has one value per bucket. (Hence
1759689912eSchristos  * the first two chunks have one value per bucket.) The values in
1769689912eSchristos  * chunk 0 correspond to denormal nubers in floating point terms.
1779689912eSchristos  * They are also the values where `63 - sigbits - clz` would be less
1789689912eSchristos  * than one if denormals were not handled specially.
1799689912eSchristos  *
1809689912eSchristos  * This branchless conversion is due to Paul Khuong: see bin_down_of() in
1819689912eSchristos  * https://pvk.ca/Blog/2015/06/27/linear-log-bucketing-fast-versatile-simple/
1829689912eSchristos  *
1839689912eSchristos  * This function is in the `isc_histo_inc()` fast path.
1849689912eSchristos  */
1859689912eSchristos static inline uint
1869689912eSchristos value_to_key(const isc_histo_t *hg, uint64_t value) {
1879689912eSchristos 	/* ensure that denormal numbers are all in chunk zero */
1889689912eSchristos 	uint64_t chunked = value | CHUNKSIZE(hg);
1899689912eSchristos 	int clz = __builtin_clzll((unsigned long long)(chunked));
1909689912eSchristos 	/* actually 1 less than the exponent except for denormals */
1919689912eSchristos 	uint exponent = 63 - hg->sigbits - clz;
1929689912eSchristos 	/* mantissa has leading bit set except for denormals */
1939689912eSchristos 	uint mantissa = value >> exponent;
1949689912eSchristos 	/* leading bit of mantissa adds one to exponent */
1959689912eSchristos 	return (exponent << hg->sigbits) + mantissa;
1969689912eSchristos }
1979689912eSchristos 
1989689912eSchristos /*
1999689912eSchristos  * Inverse functions of `value_to_key()`, to get the minimum and
2009689912eSchristos  * maximum values that map to a particular key.
2019689912eSchristos  *
2029689912eSchristos  * We must not cause undefined behaviour by hitting integer limits,
2039689912eSchristos  * which is a risk when we aim to cover the entire range of `uint64_t`.
2049689912eSchristos  *
2059689912eSchristos  * The maximum value in the last bucket is UINT64_MAX, which
2069689912eSchristos  * `key_to_maxval()` gets by deliberately subtracting `0 - 1`,
2079689912eSchristos  * undeflowing a `uint64_t`. That is OK when unsigned.
2089689912eSchristos  *
2099689912eSchristos  * We must take care not to shift too much in `key_to_minval()`.
2109689912eSchristos  * The largest key passed by `key_to_maxval()` is `BUCKETS(hg)`, so
2119689912eSchristos  *	`exponent == EXPONENTS(hg) - 1 == 64 - sigbits`
2129689912eSchristos  * which is always less than 64, so the size of the shift is OK.
2139689912eSchristos  *
2149689912eSchristos  * The `mantissa` in this edge case is just `chunksize`, which when
2159689912eSchristos  * shifted becomes `1 << 64` which overflows `uint64_t` Again this is
2169689912eSchristos  * OK when unsigned, so the return value is zero.
2179689912eSchristos  */
2189689912eSchristos 
2199689912eSchristos static inline uint64_t
2209689912eSchristos key_to_minval(const isc_histo_t *hg, uint key) {
2219689912eSchristos 	uint chunksize = CHUNKSIZE(hg);
2229689912eSchristos 	uint exponent = (key / chunksize) - 1;
2239689912eSchristos 	uint64_t mantissa = (key % chunksize) + chunksize;
2249689912eSchristos 	return key < chunksize ? key : mantissa << exponent;
2259689912eSchristos }
2269689912eSchristos 
2279689912eSchristos static inline uint64_t
2289689912eSchristos key_to_maxval(const isc_histo_t *hg, uint key) {
2299689912eSchristos 	return key_to_minval(hg, key + 1) - 1;
2309689912eSchristos }
2319689912eSchristos 
2329689912eSchristos /**********************************************************************/
2339689912eSchristos 
2349689912eSchristos static hg_bucket_t *
2359689912eSchristos key_to_new_bucket(isc_histo_t *hg, uint key) {
2369689912eSchristos 	/* slow path */
2379689912eSchristos 	uint chunksize = CHUNKSIZE(hg);
2389689912eSchristos 	uint chunk = key / chunksize;
2399689912eSchristos 	uint bucket = key % chunksize;
2409689912eSchristos 	hg_bucket_t *old_cp = NULL;
2419689912eSchristos 	hg_bucket_t *new_cp = isc_mem_cget(hg->mctx, CHUNKSIZE(hg),
2429689912eSchristos 					   sizeof(hg_bucket_t));
2439689912eSchristos 	hg_chunk_t *cpp = &hg->chunk[chunk];
2449689912eSchristos 	if (atomic_compare_exchange_strong_acq_rel(cpp, &old_cp, new_cp)) {
2459689912eSchristos 		return &new_cp[bucket];
2469689912eSchristos 	} else {
2479689912eSchristos 		/* lost the race, so use the winner's chunk */
2489689912eSchristos 		isc_mem_cput(hg->mctx, new_cp, CHUNKSIZE(hg),
2499689912eSchristos 			     sizeof(hg_bucket_t));
2509689912eSchristos 		return &old_cp[bucket];
2519689912eSchristos 	}
2529689912eSchristos }
2539689912eSchristos 
2549689912eSchristos static hg_bucket_t *
2559689912eSchristos get_chunk(const isc_histo_t *hg, uint chunk) {
2569689912eSchristos 	return atomic_load_acquire(&hg->chunk[chunk]);
2579689912eSchristos }
2589689912eSchristos 
2599689912eSchristos static inline hg_bucket_t *
2609689912eSchristos key_to_bucket(const isc_histo_t *hg, uint key) {
2619689912eSchristos 	/* fast path */
2629689912eSchristos 	uint chunksize = CHUNKSIZE(hg);
2639689912eSchristos 	uint chunk = key / chunksize;
2649689912eSchristos 	uint bucket = key % chunksize;
2659689912eSchristos 	hg_bucket_t *cp = get_chunk(hg, chunk);
2669689912eSchristos 	return cp == NULL ? NULL : &cp[bucket];
2679689912eSchristos }
2689689912eSchristos 
2699689912eSchristos static inline uint64_t
2709689912eSchristos bucket_count(const hg_bucket_t *bp) {
2719689912eSchristos 	return bp == NULL ? 0 : atomic_load_relaxed(bp);
2729689912eSchristos }
2739689912eSchristos 
2749689912eSchristos static inline uint64_t
2759689912eSchristos get_key_count(const isc_histo_t *hg, uint key) {
2769689912eSchristos 	return bucket_count(key_to_bucket(hg, key));
2779689912eSchristos }
2789689912eSchristos 
2799689912eSchristos static inline void
2809689912eSchristos add_key_count(isc_histo_t *hg, uint key, uint64_t inc) {
2819689912eSchristos 	/* fast path */
2829689912eSchristos 	if (inc > 0) {
2839689912eSchristos 		hg_bucket_t *bp = key_to_bucket(hg, key);
2849689912eSchristos 		bp = bp != NULL ? bp : key_to_new_bucket(hg, key);
2859689912eSchristos 		atomic_fetch_add_relaxed(bp, inc);
2869689912eSchristos 	}
2879689912eSchristos }
2889689912eSchristos 
2899689912eSchristos /**********************************************************************/
2909689912eSchristos 
2919689912eSchristos void
2929689912eSchristos isc_histo_add(isc_histo_t *hg, uint64_t value, uint64_t inc) {
2939689912eSchristos 	REQUIRE(HISTO_VALID(hg));
2949689912eSchristos 	add_key_count(hg, value_to_key(hg, value), inc);
2959689912eSchristos }
2969689912eSchristos 
2979689912eSchristos void
2989689912eSchristos isc_histo_inc(isc_histo_t *hg, uint64_t value) {
2999689912eSchristos 	isc_histo_add(hg, value, 1);
3009689912eSchristos }
3019689912eSchristos 
3029689912eSchristos void
3039689912eSchristos isc_histo_put(isc_histo_t *hg, uint64_t min, uint64_t max, uint64_t count) {
3049689912eSchristos 	REQUIRE(HISTO_VALID(hg));
3059689912eSchristos 
3069689912eSchristos 	uint kmin = value_to_key(hg, min);
3079689912eSchristos 	uint kmax = value_to_key(hg, max);
3089689912eSchristos 
3099689912eSchristos 	for (uint key = kmin; key <= kmax; key++) {
3109689912eSchristos 		uint64_t mid = ISC_MIN(max, key_to_maxval(hg, key));
3119689912eSchristos 		double in_bucket = mid - min + 1;
3129689912eSchristos 		double remaining = max - min + 1;
3139689912eSchristos 		uint64_t inc = ceil(count * in_bucket / remaining);
3149689912eSchristos 		add_key_count(hg, key, inc);
3159689912eSchristos 		count -= inc;
3169689912eSchristos 		min = mid + 1;
3179689912eSchristos 	}
3189689912eSchristos }
3199689912eSchristos 
3209689912eSchristos isc_result_t
3219689912eSchristos isc_histo_get(const isc_histo_t *hg, uint key, uint64_t *minp, uint64_t *maxp,
3229689912eSchristos 	      uint64_t *countp) {
3239689912eSchristos 	REQUIRE(HISTO_VALID(hg));
3249689912eSchristos 
3259689912eSchristos 	if (key < BUCKETS(hg)) {
3269689912eSchristos 		SET_IF_NOT_NULL(minp, key_to_minval(hg, key));
3279689912eSchristos 		SET_IF_NOT_NULL(maxp, key_to_maxval(hg, key));
3289689912eSchristos 		SET_IF_NOT_NULL(countp, get_key_count(hg, key));
3299689912eSchristos 		return ISC_R_SUCCESS;
3309689912eSchristos 	} else {
3319689912eSchristos 		return ISC_R_RANGE;
3329689912eSchristos 	}
3339689912eSchristos }
3349689912eSchristos 
3359689912eSchristos void
3369689912eSchristos isc_histo_next(const isc_histo_t *hg, uint *keyp) {
3379689912eSchristos 	REQUIRE(HISTO_VALID(hg));
3389689912eSchristos 	REQUIRE(keyp != NULL);
3399689912eSchristos 
3409689912eSchristos 	uint chunksize = CHUNKSIZE(hg);
3419689912eSchristos 	uint buckets = BUCKETS(hg);
3429689912eSchristos 	uint key = *keyp;
3439689912eSchristos 
3449689912eSchristos 	key++;
3459689912eSchristos 	while (key < buckets && key % chunksize == 0 &&
3469689912eSchristos 	       key_to_bucket(hg, key) == NULL)
3479689912eSchristos 	{
3489689912eSchristos 		key += chunksize;
3499689912eSchristos 	}
3509689912eSchristos 	*keyp = key;
3519689912eSchristos }
3529689912eSchristos 
3539689912eSchristos void
3549689912eSchristos isc_histo_merge(isc_histo_t **targetp, const isc_histo_t *source) {
3559689912eSchristos 	REQUIRE(HISTO_VALID(source));
3569689912eSchristos 	REQUIRE(targetp != NULL);
3579689912eSchristos 
3589689912eSchristos 	if (*targetp != NULL) {
3599689912eSchristos 		REQUIRE(HISTO_VALID(*targetp));
3609689912eSchristos 	} else {
3619689912eSchristos 		isc_histo_create(source->mctx, source->sigbits, targetp);
3629689912eSchristos 	}
3639689912eSchristos 
3649689912eSchristos 	uint64_t min, max, count;
3659689912eSchristos 	for (uint key = 0;
3669689912eSchristos 	     isc_histo_get(source, key, &min, &max, &count) == ISC_R_SUCCESS;
3679689912eSchristos 	     isc_histo_next(source, &key))
3689689912eSchristos 	{
3699689912eSchristos 		isc_histo_put(*targetp, min, max, count);
3709689912eSchristos 	}
3719689912eSchristos }
3729689912eSchristos 
3739689912eSchristos /**********************************************************************/
3749689912eSchristos 
3759689912eSchristos void
3769689912eSchristos isc_histomulti_create(isc_mem_t *mctx, uint sigbits, isc_histomulti_t **hmp) {
3779689912eSchristos 	REQUIRE(hmp != NULL);
3789689912eSchristos 	REQUIRE(*hmp == NULL);
3799689912eSchristos 
3809689912eSchristos 	uint size = isc_tid_count();
3819689912eSchristos 	INSIST(size > 0);
3829689912eSchristos 
3839689912eSchristos 	isc_histomulti_t *hm = isc_mem_cget(mctx, 1,
3849689912eSchristos 					    STRUCT_FLEX_SIZE(hm, hg, size));
3859689912eSchristos 	*hm = (isc_histomulti_t){
3869689912eSchristos 		.magic = HISTOMULTI_MAGIC,
3879689912eSchristos 		.size = size,
3889689912eSchristos 	};
3899689912eSchristos 
3909689912eSchristos 	for (uint i = 0; i < hm->size; i++) {
3919689912eSchristos 		isc_histo_create(mctx, sigbits, &hm->hg[i]);
3929689912eSchristos 	}
3939689912eSchristos 
3949689912eSchristos 	*hmp = hm;
3959689912eSchristos }
3969689912eSchristos 
3979689912eSchristos void
3989689912eSchristos isc_histomulti_destroy(isc_histomulti_t **hmp) {
3999689912eSchristos 	REQUIRE(hmp != NULL);
4009689912eSchristos 	REQUIRE(HISTOMULTI_VALID(*hmp));
4019689912eSchristos 
4029689912eSchristos 	isc_histomulti_t *hm = *hmp;
4039689912eSchristos 	isc_mem_t *mctx = hm->hg[0]->mctx;
4049689912eSchristos 	*hmp = NULL;
4059689912eSchristos 
4069689912eSchristos 	for (uint i = 0; i < hm->size; i++) {
4079689912eSchristos 		isc_histo_destroy(&hm->hg[i]);
4089689912eSchristos 	}
4099689912eSchristos 
4109689912eSchristos 	isc_mem_put(mctx, hm, STRUCT_FLEX_SIZE(hm, hg, hm->size));
4119689912eSchristos }
4129689912eSchristos 
4139689912eSchristos void
4149689912eSchristos isc_histomulti_merge(isc_histo_t **hgp, const isc_histomulti_t *hm) {
4159689912eSchristos 	REQUIRE(HISTOMULTI_VALID(hm));
4169689912eSchristos 
4179689912eSchristos 	for (uint i = 0; i < hm->size; i++) {
4189689912eSchristos 		isc_histo_merge(hgp, hm->hg[i]);
4199689912eSchristos 	}
4209689912eSchristos }
4219689912eSchristos 
4229689912eSchristos void
4239689912eSchristos isc_histomulti_add(isc_histomulti_t *hm, uint64_t value, uint64_t inc) {
4249689912eSchristos 	REQUIRE(HISTOMULTI_VALID(hm));
4259689912eSchristos 	isc_histo_t *hg = hm->hg[isc_tid()];
4269689912eSchristos 	add_key_count(hg, value_to_key(hg, value), inc);
4279689912eSchristos }
4289689912eSchristos 
4299689912eSchristos void
4309689912eSchristos isc_histomulti_inc(isc_histomulti_t *hm, uint64_t value) {
4319689912eSchristos 	isc_histomulti_add(hm, value, 1);
4329689912eSchristos }
4339689912eSchristos 
4349689912eSchristos /**********************************************************************/
4359689912eSchristos 
4369689912eSchristos /*
4379689912eSchristos  * https://fanf2.user.srcf.net/hermes/doc/antiforgery/stats.pdf
4389689912eSchristos  * equation 4 (incremental mean) and equation 44 (incremental variance)
4399689912eSchristos  */
4409689912eSchristos void
4419689912eSchristos isc_histo_moments(const isc_histo_t *hg, double *pm0, double *pm1,
4429689912eSchristos 		  double *pm2) {
4439689912eSchristos 	REQUIRE(HISTO_VALID(hg));
4449689912eSchristos 
4459689912eSchristos 	uint64_t pop = 0;
4469689912eSchristos 	double mean = 0.0;
4479689912eSchristos 	double sigma = 0.0;
4489689912eSchristos 
4499689912eSchristos 	uint64_t min, max, count;
4509689912eSchristos 	for (uint key = 0;
4519689912eSchristos 	     isc_histo_get(hg, key, &min, &max, &count) == ISC_R_SUCCESS;
4529689912eSchristos 	     isc_histo_next(hg, &key))
4539689912eSchristos 	{
4549689912eSchristos 		if (count == 0) { /* avoid division by zero */
4559689912eSchristos 			continue;
4569689912eSchristos 		}
4579689912eSchristos 		double value = min / 2.0 + max / 2.0;
4589689912eSchristos 		double delta = value - mean;
4599689912eSchristos 		pop += count;
4609689912eSchristos 		mean += count * delta / pop;
4619689912eSchristos 		sigma += count * delta * (value - mean);
4629689912eSchristos 	}
4639689912eSchristos 
4649689912eSchristos 	SET_IF_NOT_NULL(pm0, pop);
4659689912eSchristos 	SET_IF_NOT_NULL(pm1, mean);
4669689912eSchristos 	SET_IF_NOT_NULL(pm2, (pop > 0) ? sqrt(sigma / pop) : 0.0);
4679689912eSchristos }
4689689912eSchristos 
4699689912eSchristos /*
4709689912eSchristos  * Clamped linear interpolation
4719689912eSchristos  *
4729689912eSchristos  * `outrange` should be `((1 << n) - 1)` for some `n`; when `n` is larger
4739689912eSchristos  * than 53, `outrange` can get rounded up to a power of 2, so we clamp the
4749689912eSchristos  * result to keep within bounds (extra important when `max == UINT64_MAX`)
4759689912eSchristos  */
4769689912eSchristos static inline uint64_t
4779689912eSchristos lerp(uint64_t min, uint64_t max, uint64_t lo, uint64_t in, uint64_t hi) {
4789689912eSchristos 	double inrange = (double)(hi - lo);
4799689912eSchristos 	double inpart = (double)(in - lo);
4809689912eSchristos 	double outrange = (double)(max - min);
4819689912eSchristos 	double outpart = round(outrange * inpart / inrange);
4829689912eSchristos 	return min + ISC_MIN((uint64_t)outpart, max - min);
4839689912eSchristos }
4849689912eSchristos 
4859689912eSchristos /*
4869689912eSchristos  * There is non-zero space for the inner value, and it is inside the bounds
4879689912eSchristos  */
4889689912eSchristos static inline bool
4899689912eSchristos inside(uint64_t lo, uint64_t in, uint64_t hi) {
4909689912eSchristos 	return lo < hi && lo <= in && in <= hi;
4919689912eSchristos }
4929689912eSchristos 
4939689912eSchristos isc_result_t
4949689912eSchristos isc_histo_quantiles(const isc_histo_t *hg, uint size, const double *fraction,
4959689912eSchristos 		    uint64_t *value) {
4969689912eSchristos 	hg_bucket_t *chunk[CHUNKS];
4979689912eSchristos 	uint64_t total[CHUNKS];
4989689912eSchristos 	uint64_t rank[ISC_HISTO_MAXQUANTILES];
4999689912eSchristos 
5009689912eSchristos 	REQUIRE(HISTO_VALID(hg));
5019689912eSchristos 	REQUIRE(0 < size && size <= ISC_HISTO_MAXQUANTILES);
5029689912eSchristos 	REQUIRE(fraction != NULL);
5039689912eSchristos 	REQUIRE(value != NULL);
5049689912eSchristos 
5059689912eSchristos 	const uint maxchunk = MAXCHUNK(hg);
5069689912eSchristos 	const uint chunksize = CHUNKSIZE(hg);
5079689912eSchristos 
5089689912eSchristos 	/*
5099689912eSchristos 	 * Find out which chunks exist and what their totals are. We take a
5109689912eSchristos 	 * copy of the chunk pointers to reduce the need for atomic ops
5119689912eSchristos 	 * later on. Scan from low to high so that higher buckets are more
5129689912eSchristos 	 * likely to be in the CPU cache when we scan from high to low.
5139689912eSchristos 	 */
5149689912eSchristos 	uint64_t population = 0;
5159689912eSchristos 	for (uint c = 0; c < maxchunk; c++) {
5169689912eSchristos 		chunk[c] = get_chunk(hg, c);
5179689912eSchristos 		total[c] = 0;
5189689912eSchristos 		if (chunk[c] != NULL) {
5199689912eSchristos 			for (uint b = chunksize; b-- > 0;) {
5209689912eSchristos 				total[c] += bucket_count(&chunk[c][b]);
5219689912eSchristos 			}
5229689912eSchristos 			population += total[c];
5239689912eSchristos 		}
5249689912eSchristos 	}
5259689912eSchristos 
5269689912eSchristos 	/*
5279689912eSchristos 	 * Now we know the population, we can convert fractions to ranks.
5289689912eSchristos 	 * Also ensure they are within bounds and in decreasing order.
5299689912eSchristos 	 */
5309689912eSchristos 	for (uint i = 0; i < size; i++) {
5319689912eSchristos 		REQUIRE(0.0 <= fraction[i] && fraction[i] <= 1.0);
5329689912eSchristos 		REQUIRE(i == 0 || fraction[i - 1] > fraction[i]);
5339689912eSchristos 		rank[i] = round(fraction[i] * population);
5349689912eSchristos 	}
5359689912eSchristos 
5369689912eSchristos 	/*
5379689912eSchristos 	 * Scan chunks from high to low, keeping track of the bounds on
5389689912eSchristos 	 * each chunk's ranks. Each time we match `rank[i]`, move on to the
5399689912eSchristos 	 * next rank and continue the scan from the same place.
5409689912eSchristos 	 */
5419689912eSchristos 	uint i = 0;
5429689912eSchristos 	uint64_t chunk_lo = population;
5439689912eSchristos 	for (uint c = maxchunk; c-- > 0;) {
5449689912eSchristos 		uint64_t chunk_hi = chunk_lo;
5459689912eSchristos 		chunk_lo = chunk_hi - total[c];
5469689912eSchristos 
5479689912eSchristos 		/*
5489689912eSchristos 		 * Scan buckets backwards within this chunk, in a similar
5499689912eSchristos 		 * manner to the chunk scan. Skip all or part of the loop
5509689912eSchristos 		 * if the current rank is not in the chunk.
5519689912eSchristos 		 */
5529689912eSchristos 		uint64_t bucket_lo = chunk_hi;
5539689912eSchristos 		for (uint b = chunksize;
5549689912eSchristos 		     b-- > 0 && inside(chunk_lo, rank[i], chunk_hi);)
5559689912eSchristos 		{
5569689912eSchristos 			uint64_t bucket_hi = bucket_lo;
5579689912eSchristos 			bucket_lo = bucket_hi - bucket_count(&chunk[c][b]);
5589689912eSchristos 
5599689912eSchristos 			/*
5609689912eSchristos 			 * Convert all ranks that fall in this bucket.
5619689912eSchristos 			 */
5629689912eSchristos 			while (inside(bucket_lo, rank[i], bucket_hi)) {
5639689912eSchristos 				uint key = chunksize * c + b;
5649689912eSchristos 				value[i] = lerp(key_to_minval(hg, key),
5659689912eSchristos 						key_to_maxval(hg, key),
5669689912eSchristos 						bucket_lo, rank[i], bucket_hi);
5679689912eSchristos 				if (++i == size) {
5689689912eSchristos 					return ISC_R_SUCCESS;
5699689912eSchristos 				}
5709689912eSchristos 			}
5719689912eSchristos 		}
5729689912eSchristos 	}
5739689912eSchristos 
5749689912eSchristos 	return ISC_R_UNSET;
5759689912eSchristos }
5769689912eSchristos 
5779689912eSchristos /**********************************************************************/
578