From 31e8e96c92ea89655137083e71176c01ae8dc445 Mon Sep 17 00:00:00 2001 From: Hannu Krosing Date: Tue, 25 Aug 2026 22:07:35 +0000 Subject: [PATCH v2 1/2] Extract pgbench low-level functions into src/common Move uniform, Gaussian, Exponential, Zipfian, and Poisson random distributions, 64-bit MurmurHash2 and FNV-1a hash functions, and domain permutation from src/bin/pgbench/pgbench.c into a shared library module in src/common/pgbench_funcs.c and src/include/common/pgbench_funcs.h. Update pgbench to include common/pgbench_funcs.h and use the shared implementations, removing duplicate code. --- src/bin/pgbench/pgbench.c | 342 ++--------------------------- src/common/Makefile | 1 + src/common/meson.build | 1 + src/common/pgbench_funcs.c | 320 +++++++++++++++++++++++++++ src/include/common/pgbench_funcs.h | 48 ++++ 5 files changed, 386 insertions(+), 326 deletions(-) create mode 100644 src/common/pgbench_funcs.c create mode 100644 src/include/common/pgbench_funcs.h diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c index 5862758427f..f38575bdccb 100644 --- a/src/bin/pgbench/pgbench.c +++ b/src/bin/pgbench/pgbench.c @@ -57,6 +57,7 @@ #include "common/int.h" #include "common/logging.h" #include "common/pg_prng.h" +#include "common/pgbench_funcs.h" #include "common/string.h" #include "common/username.h" #include "fe_utils/cancel.h" @@ -78,15 +79,6 @@ #define ERRCODE_T_R_DEADLOCK_DETECTED "40P01" #define ERRCODE_UNDEFINED_TABLE "42P01" -/* - * Hashing constants - */ -#define FNV_PRIME UINT64CONST(0x100000001b3) -#define FNV_OFFSET_BASIS UINT64CONST(0xcbf29ce484222325) -#define MM2_MUL UINT64CONST(0xc6a4a7935bd1e995) -#define MM2_MUL_TIMES_8 UINT64CONST(0x35253c9ade8f4ca8) -#define MM2_ROT 47 - /* * Multi-platform socket set implementations */ @@ -166,11 +158,6 @@ typedef struct socket_set #define LOG_STEP_SECONDS 5 /* seconds between log messages */ #define DEFAULT_NXACTS 10 /* default nxacts */ -#define MIN_GAUSSIAN_PARAM 2.0 /* minimum parameter for gauss */ - -#define MIN_ZIPFIAN_PARAM 1.001 /* minimum parameter for zipfian */ -#define MAX_ZIPFIAN_PARAM 1000.0 /* maximum parameter for zipfian */ - static int nxacts = 0; /* number of transactions per client */ static int duration = 0; /* duration in seconds */ static int64 end_time = 0; /* when to stop in micro seconds, under -T */ @@ -1081,303 +1068,6 @@ initRandomState(pg_prng_state *state) pg_prng_seed(state, pg_prng_uint64(&base_random_sequence)); } - -/* - * random number generator: uniform distribution from min to max inclusive. - * - * Although the limits are expressed as int64, you can't generate the full - * int64 range in one call, because the difference of the limits mustn't - * overflow int64. This is not checked. - */ -static int64 -getrand(pg_prng_state *state, int64 min, int64 max) -{ - return min + (int64) pg_prng_uint64_range(state, 0, max - min); -} - -/* - * random number generator: exponential distribution from min to max inclusive. - * the parameter is so that the density of probability for the last cut-off max - * value is exp(-parameter). - */ -static int64 -getExponentialRand(pg_prng_state *state, int64 min, int64 max, - double parameter) -{ - double cut, - uniform, - rand; - - /* abort if wrong parameter, but must really be checked beforehand */ - Assert(parameter > 0.0); - cut = exp(-parameter); - /* pg_prng_double value in [0, 1), uniform in (0, 1] */ - uniform = 1.0 - pg_prng_double(state); - - /* - * inner expression in (cut, 1] (if parameter > 0), rand in [0, 1) - */ - Assert((1.0 - cut) != 0.0); - rand = -log(cut + (1.0 - cut) * uniform) / parameter; - /* return int64 random number within between min and max */ - return min + (int64) ((max - min + 1) * rand); -} - -/* random number generator: gaussian distribution from min to max inclusive */ -static int64 -getGaussianRand(pg_prng_state *state, int64 min, int64 max, - double parameter) -{ - double stdev; - double rand; - - /* abort if parameter is too low, but must really be checked beforehand */ - Assert(parameter >= MIN_GAUSSIAN_PARAM); - - /* - * Get normally-distributed random number in the range -parameter <= stdev - * < parameter. - * - * This loop is executed until the number is in the expected range. - * - * As the minimum parameter is 2.0, the probability of looping is low: - * sqrt(-2 ln(r)) <= 2 => r >= e^{-2} ~ 0.135, then when taking the - * average sinus multiplier as 2/pi, we have a 8.6% looping probability in - * the worst case. For a parameter value of 5.0, the looping probability - * is about e^{-5} * 2 / pi ~ 0.43%. - */ - do - { - stdev = pg_prng_double_normal(state); - } - while (stdev < -parameter || stdev >= parameter); - - /* stdev is in [-parameter, parameter), normalization to [0,1) */ - rand = (stdev + parameter) / (parameter * 2.0); - - /* return int64 random number within between min and max */ - return min + (int64) ((max - min + 1) * rand); -} - -/* - * random number generator: generate a value, such that the series of values - * will approximate a Poisson distribution centered on the given value. - * - * Individual results are rounded to integers, though the center value need - * not be one. - */ -static int64 -getPoissonRand(pg_prng_state *state, double center) -{ - /* - * Use inverse transform sampling to generate a value > 0, such that the - * expected (i.e. average) value is the given argument. - */ - double uniform; - - /* pg_prng_double value in [0, 1), uniform in (0, 1] */ - uniform = 1.0 - pg_prng_double(state); - - return (int64) (-log(uniform) * center + 0.5); -} - -/* - * Computing zipfian using rejection method, based on - * "Non-Uniform Random Variate Generation", - * Luc Devroye, p. 550-551, Springer 1986. - * - * This works for s > 1.0, but may perform badly for s very close to 1.0. - */ -static int64 -computeIterativeZipfian(pg_prng_state *state, int64 n, double s) -{ - double b = pow(2.0, s - 1.0); - double x, - t, - u, - v; - - /* Ensure n is sane */ - if (n <= 1) - return 1; - - while (true) - { - /* random variates */ - u = pg_prng_double(state); - v = pg_prng_double(state); - - x = floor(pow(u, -1.0 / (s - 1.0))); - - t = pow(1.0 + 1.0 / x, s - 1.0); - /* reject if too large or out of bound */ - if (v * x * (t - 1.0) / (b - 1.0) <= t / b && x <= n) - break; - } - return (int64) x; -} - -/* random number generator: zipfian distribution from min to max inclusive */ -static int64 -getZipfianRand(pg_prng_state *state, int64 min, int64 max, double s) -{ - int64 n = max - min + 1; - - /* abort if parameter is invalid */ - Assert(MIN_ZIPFIAN_PARAM <= s && s <= MAX_ZIPFIAN_PARAM); - - return min - 1 + computeIterativeZipfian(state, n, s); -} - -/* - * FNV-1a hash function - */ -static int64 -getHashFnv1a(int64 val, uint64 seed) -{ - int64 result; - int i; - - result = FNV_OFFSET_BASIS ^ seed; - for (i = 0; i < 8; ++i) - { - int32 octet = val & 0xff; - - val = val >> 8; - result = result ^ octet; - result = result * FNV_PRIME; - } - - return result; -} - -/* - * Murmur2 hash function - * - * Based on original work of Austin Appleby - * https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp - */ -static int64 -getHashMurmur2(int64 val, uint64 seed) -{ - uint64 result = seed ^ MM2_MUL_TIMES_8; /* sizeof(int64) */ - uint64 k = (uint64) val; - - k *= MM2_MUL; - k ^= k >> MM2_ROT; - k *= MM2_MUL; - - result ^= k; - result *= MM2_MUL; - - result ^= result >> MM2_ROT; - result *= MM2_MUL; - result ^= result >> MM2_ROT; - - return (int64) result; -} - -/* - * Pseudorandom permutation function - * - * For small sizes, this generates each of the (size!) possible permutations - * of integers in the range [0, size) with roughly equal probability. Once - * the size is larger than 20, the number of possible permutations exceeds the - * number of distinct states of the internal pseudorandom number generator, - * and so not all possible permutations can be generated, but the permutations - * chosen should continue to give the appearance of being random. - * - * THIS FUNCTION IS NOT CRYPTOGRAPHICALLY SECURE. - * DO NOT USE FOR SUCH PURPOSE. - */ -static int64 -permute(const int64 val, const int64 isize, const int64 seed) -{ - /* using a high-end PRNG is probably overkill */ - pg_prng_state state; - uint64 size; - uint64 v; - int masklen; - uint64 mask; - int i; - - if (isize < 2) - return 0; /* nothing to permute */ - - /* Initialize prng state using the seed */ - pg_prng_seed(&state, (uint64) seed); - - /* Computations are performed on unsigned values */ - size = (uint64) isize; - v = (uint64) val % size; - - /* Mask to work modulo largest power of 2 less than or equal to size */ - masklen = pg_leftmost_one_pos64(size); - mask = (((uint64) 1) << masklen) - 1; - - /* - * Permute the input value by applying several rounds of pseudorandom - * bijective transformations. The intention here is to distribute each - * input uniformly randomly across the range, and separate adjacent inputs - * approximately uniformly randomly from each other, leading to a fairly - * random overall choice of permutation. - * - * To separate adjacent inputs, we multiply by a random number modulo - * (mask + 1), which is a power of 2. For this to be a bijection, the - * multiplier must be odd. Since this is known to lead to less randomness - * in the lower bits, we also apply a rotation that shifts the topmost bit - * into the least significant bit. In the special cases where size <= 3, - * mask = 1 and each of these operations is actually a no-op, so we also - * XOR the value with a different random number to inject additional - * randomness. Since the size is generally not a power of 2, we apply - * this bijection on overlapping upper and lower halves of the input. - * - * To distribute the inputs uniformly across the range, we then also apply - * a random offset modulo the full range. - * - * Taken together, these operations resemble a modified linear - * congruential generator, as is commonly used in pseudorandom number - * generators. The number of rounds is fairly arbitrary, but six has been - * found empirically to give a fairly good tradeoff between performance - * and uniform randomness. For small sizes it selects each of the (size!) - * possible permutations with roughly equal probability. For larger - * sizes, not all permutations can be generated, but the intended random - * spread is still produced. - */ - for (i = 0; i < 6; i++) - { - uint64 m, - r, - t; - - /* Random multiply (by an odd number), XOR and rotate of lower half */ - m = (pg_prng_uint64(&state) & mask) | 1; - r = pg_prng_uint64(&state) & mask; - if (v <= mask) - { - v = ((v * m) ^ r) & mask; - v = ((v << 1) & mask) | (v >> (masklen - 1)); - } - - /* Random multiply (by an odd number), XOR and rotate of upper half */ - m = (pg_prng_uint64(&state) & mask) | 1; - r = pg_prng_uint64(&state) & mask; - t = size - 1 - v; - if (t <= mask) - { - t = ((t * m) ^ r) & mask; - t = ((t << 1) & mask) | (t >> (masklen - 1)); - v = size - 1 - t; - } - - /* Random offset */ - r = pg_prng_uint64_range(&state, 0, size - 1); - v = (v + r) % size; - } - - return (int64) v; -} - /* * Initialize the given SimpleStats struct to all zeroes */ @@ -2665,7 +2355,7 @@ evalStandardFunc(CState *st, if (func == PGBENCH_RANDOM) { Assert(nargs == 2); - setIntValue(retval, getrand(&st->cs_func_rs, imin, imax)); + setIntValue(retval, pgbench_random(&st->cs_func_rs, imin, imax)); } else /* gaussian & exponential */ { @@ -2678,28 +2368,28 @@ evalStandardFunc(CState *st, if (func == PGBENCH_RANDOM_GAUSSIAN) { - if (param < MIN_GAUSSIAN_PARAM) + if (param < PGBENCH_MIN_GAUSSIAN_PARAM) { pg_log_error("gaussian parameter must be at least %f (not %f)", - MIN_GAUSSIAN_PARAM, param); + PGBENCH_MIN_GAUSSIAN_PARAM, param); return false; } setIntValue(retval, - getGaussianRand(&st->cs_func_rs, - imin, imax, param)); + pgbench_random_gaussian(&st->cs_func_rs, + imin, imax, param)); } else if (func == PGBENCH_RANDOM_ZIPFIAN) { - if (param < MIN_ZIPFIAN_PARAM || param > MAX_ZIPFIAN_PARAM) + if (param < PGBENCH_MIN_ZIPFIAN_PARAM || param > PGBENCH_MAX_ZIPFIAN_PARAM) { pg_log_error("zipfian parameter must be in range [%.3f, %.0f] (not %f)", - MIN_ZIPFIAN_PARAM, MAX_ZIPFIAN_PARAM, param); + PGBENCH_MIN_ZIPFIAN_PARAM, PGBENCH_MAX_ZIPFIAN_PARAM, param); return false; } setIntValue(retval, - getZipfianRand(&st->cs_func_rs, imin, imax, param)); + pgbench_random_zipfian(&st->cs_func_rs, imin, imax, param)); } else /* exponential */ { @@ -2711,8 +2401,8 @@ evalStandardFunc(CState *st, } setIntValue(retval, - getExponentialRand(&st->cs_func_rs, - imin, imax, param)); + pgbench_random_exponential(&st->cs_func_rs, + imin, imax, param)); } } @@ -2765,9 +2455,9 @@ evalStandardFunc(CState *st, return false; if (func == PGBENCH_HASH_MURMUR2) - setIntValue(retval, getHashMurmur2(val, seed)); + setIntValue(retval, pgbench_hash_murmur2(val, seed)); else if (func == PGBENCH_HASH_FNV1A) - setIntValue(retval, getHashFnv1a(val, seed)); + setIntValue(retval, pgbench_hash_fnv1a(val, seed)); else /* cannot get here */ Assert(0); @@ -2794,7 +2484,7 @@ evalStandardFunc(CState *st, return false; } - setIntValue(retval, permute(val, size, seed)); + setIntValue(retval, pgbench_permute(val, size, seed)); return true; } @@ -3049,7 +2739,7 @@ chooseScript(TState *thread) if (num_scripts == 1) return 0; - w = getrand(&thread->ts_choose_rs, 0, total_weight - 1); + w = pgbench_random(&thread->ts_choose_rs, 0, total_weight - 1); do { w -= sql_script[i++].weight; @@ -3782,7 +3472,7 @@ advanceConnectionState(TState *thread, CState *st, StatsData *agg) Assert(throttle_delay > 0); thread->throttle_trigger += - getPoissonRand(&thread->ts_throttle_rs, throttle_delay); + pgbench_random_poisson(&thread->ts_throttle_rs, throttle_delay); st->txn_scheduled = thread->throttle_trigger; /* diff --git a/src/common/Makefile b/src/common/Makefile index 1a2fbbe887f..3a2f90a3861 100644 --- a/src/common/Makefile +++ b/src/common/Makefile @@ -71,6 +71,7 @@ OBJS_COMMON = \ pg_get_line.o \ pg_lzcompress.o \ pg_prng.o \ + pgbench_funcs.o \ pgfnames.o \ psprintf.o \ relpath.o \ diff --git a/src/common/meson.build b/src/common/meson.build index 9bd55cda95b..72e8fa57ea2 100644 --- a/src/common/meson.build +++ b/src/common/meson.build @@ -25,6 +25,7 @@ common_sources = files( 'pg_get_line.c', 'pg_lzcompress.c', 'pg_prng.c', + 'pgbench_funcs.c', 'pgfnames.c', 'psprintf.c', 'relpath.c', diff --git a/src/common/pgbench_funcs.c b/src/common/pgbench_funcs.c new file mode 100644 index 00000000000..4076262dc28 --- /dev/null +++ b/src/common/pgbench_funcs.c @@ -0,0 +1,320 @@ +/*------------------------------------------------------------------------- + * + * pgbench_funcs.c + * Shared random distribution, permutation, and hashing functions. + * + * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/common/pgbench_funcs.c + * + *------------------------------------------------------------------------- + */ + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +#include + +#include "common/pgbench_funcs.h" +#include "port/pg_bitutils.h" + +/* + * random number generator: uniform distribution from min to max inclusive. + * + * Although the limits are expressed as int64, you can't generate the full + * int64 range in one call, because the difference of the limits mustn't + * overflow int64. This is not checked here; callers should check. + */ +int64 +pgbench_random(pg_prng_state *state, int64 min, int64 max) +{ + return min + (int64) pg_prng_uint64_range(state, 0, max - min); +} + +/* + * random number generator: exponential distribution from min to max inclusive. + * the parameter is so that the density of probability for the last cut-off max + * value is exp(-parameter). + */ +int64 +pgbench_random_exponential(pg_prng_state *state, int64 min, int64 max, + double parameter) +{ + double cut, + uniform, + rand; + + /* abort if wrong parameter, but must really be checked beforehand */ + Assert(parameter > 0.0); + cut = exp(-parameter); + /* pg_prng_double value in [0, 1), uniform in (0, 1] */ + uniform = 1.0 - pg_prng_double(state); + + /* + * inner expression in (cut, 1] (if parameter > 0), rand in [0, 1) + */ + Assert((1.0 - cut) != 0.0); + rand = -log(cut + (1.0 - cut) * uniform) / parameter; + /* return int64 random number within between min and max */ + return min + (int64) ((max - min + 1) * rand); +} + +/* random number generator: gaussian distribution from min to max inclusive */ +int64 +pgbench_random_gaussian(pg_prng_state *state, int64 min, int64 max, + double parameter) +{ + double stdev; + double rand; + + /* abort if parameter is too low, but must really be checked beforehand */ + Assert(parameter >= PGBENCH_MIN_GAUSSIAN_PARAM); + + /* + * Get normally-distributed random number in the range -parameter <= stdev + * < parameter. + * + * This loop is executed until the number is in the expected range. + * + * As the minimum parameter is 2.0, the probability of looping is low: + * sqrt(-2 ln(r)) <= 2 => r >= e^{-2} ~ 0.135, then when taking the + * average sinus multiplier as 2/pi, we have a 8.6% looping probability in + * the worst case. For a parameter value of 5.0, the looping probability + * is about e^{-5} * 2 / pi ~ 0.43%. + */ + do + { + stdev = pg_prng_double_normal(state); + } + while (stdev < -parameter || stdev >= parameter); + + /* stdev is in [-parameter, parameter), normalization to [0,1) */ + rand = (stdev + parameter) / (parameter * 2.0); + + /* return int64 random number within between min and max */ + return min + (int64) ((max - min + 1) * rand); +} + +/* + * random number generator: generate a value, such that the series of values + * will approximate a Poisson distribution centered on the given value. + * + * Individual results are rounded to integers, though the center value need + * not be one. + */ +int64 +pgbench_random_poisson(pg_prng_state *state, double center) +{ + /* + * Use inverse transform sampling to generate a value > 0, such that the + * expected (i.e. average) value is the given argument. + */ + double uniform; + + /* pg_prng_double value in [0, 1), uniform in (0, 1] */ + uniform = 1.0 - pg_prng_double(state); + + return (int64) (-log(uniform) * center + 0.5); +} + +/* + * Computing zipfian using rejection method, based on + * "Non-Uniform Random Variate Generation", + * Luc Devroye, p. 550-551, Springer 1986. + * + * This works for s > 1.0, but may perform badly for s very close to 1.0. + */ +static int64 +computeIterativeZipfian(pg_prng_state *state, int64 n, double s) +{ + double b = pow(2.0, s - 1.0); + double x, + t, + u, + v; + + /* Ensure n is sane */ + if (n <= 1) + return 1; + + while (true) + { + /* random variates */ + u = pg_prng_double(state); + v = pg_prng_double(state); + + x = floor(pow(u, -1.0 / (s - 1.0))); + + t = pow(1.0 + 1.0 / x, s - 1.0); + /* reject if too large or out of bound */ + if (v * x * (t - 1.0) / (b - 1.0) <= t / b && x <= n) + break; + } + return (int64) x; +} + +/* random number generator: zipfian distribution from min to max inclusive */ +int64 +pgbench_random_zipfian(pg_prng_state *state, int64 min, int64 max, double s) +{ + int64 n = max - min + 1; + + /* abort if parameter is invalid */ + Assert(PGBENCH_MIN_ZIPFIAN_PARAM <= s && s <= PGBENCH_MAX_ZIPFIAN_PARAM); + + return min - 1 + computeIterativeZipfian(state, n, s); +} + +/* + * FNV-1a hash function + */ +int64 +pgbench_hash_fnv1a(int64 val, uint64 seed) +{ + int64 result; + int i; + + result = PGBENCH_FNV_OFFSET_BASIS ^ seed; + for (i = 0; i < 8; ++i) + { + int32 octet = val & 0xff; + + val = val >> 8; + result = result ^ octet; + result = result * PGBENCH_FNV_PRIME; + } + + return result; +} + +/* + * Murmur2 hash function + * + * Based on original work of Austin Appleby + * https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp + */ +int64 +pgbench_hash_murmur2(int64 val, uint64 seed) +{ + uint64 result = seed ^ PGBENCH_MM2_MUL_TIMES_8; /* sizeof(int64) */ + uint64 k = (uint64) val; + + k *= PGBENCH_MM2_MUL; + k ^= k >> PGBENCH_MM2_ROT; + k *= PGBENCH_MM2_MUL; + + result ^= k; + result *= PGBENCH_MM2_MUL; + + result ^= result >> PGBENCH_MM2_ROT; + result *= PGBENCH_MM2_MUL; + result ^= result >> PGBENCH_MM2_ROT; + + return (int64) result; +} + +/* + * Pseudorandom permutation function + * + * For small sizes, this generates each of the (size!) possible permutations + * of integers in the range [0, size) with roughly equal probability. Once + * the size is larger than 20, the number of possible permutations exceeds the + * number of distinct states of the internal pseudorandom number generator, + * and so not all possible permutations can be generated, but the permutations + * chosen should continue to give the appearance of being random. + * + * THIS FUNCTION IS NOT CRYPTOGRAPHICALLY SECURE. + * DO NOT USE FOR SUCH PURPOSE. + */ +int64 +pgbench_permute(const int64 val, const int64 isize, const int64 seed) +{ + /* using a high-end PRNG is probably overkill */ + pg_prng_state state; + uint64 size; + uint64 v; + int masklen; + uint64 mask; + int i; + + if (isize < 2) + return 0; /* nothing to permute */ + + /* Initialize prng state using the seed */ + pg_prng_seed(&state, (uint64) seed); + + /* Computations are performed on unsigned values */ + size = (uint64) isize; + v = (uint64) val % size; + + /* Mask to work modulo largest power of 2 less than or equal to size */ + masklen = pg_leftmost_one_pos64(size); + mask = (((uint64) 1) << masklen) - 1; + + /* + * Permute the input value by applying several rounds of pseudorandom + * bijective transformations. The intention here is to distribute each + * input uniformly randomly across the range, and separate adjacent inputs + * approximately uniformly randomly from each other, leading to a fairly + * random overall choice of permutation. + * + * To separate adjacent inputs, we multiply by a random number modulo + * (mask + 1), which is a power of 2. For this to be a bijection, the + * multiplier must be odd. Since this is known to lead to less randomness + * in the lower bits, we also apply a rotation that shifts the topmost bit + * into the least significant bit. In the special cases where size <= 3, + * mask = 1 and each of these operations is actually a no-op, so we also + * XOR the value with a different random number to inject additional + * randomness. Since the size is generally not a power of 2, we apply + * this bijection on overlapping upper and lower halves of the input. + * + * To distribute the inputs uniformly across the range, we then also apply + * a random offset modulo the full range. + * + * Taken together, these operations resemble a modified linear + * congruential generator, as is commonly used in pseudorandom number + * generators. The number of rounds is fairly arbitrary, but six has been + * found empirically to give a fairly good tradeoff between performance + * and uniform randomness. For small sizes it selects each of the (size!) + * possible permutations with roughly equal probability. For larger + * sizes, not all permutations can be generated, but the intended random + * spread is still produced. + */ + for (i = 0; i < 6; i++) + { + uint64 m, + r, + t; + + /* Random multiply (by an odd number), XOR and rotate of lower half */ + m = (pg_prng_uint64(&state) & mask) | 1; + r = pg_prng_uint64(&state) & mask; + if (v <= mask) + { + v = ((v * m) ^ r) & mask; + v = ((v << 1) & mask) | (v >> (masklen - 1)); + } + + /* Random multiply (by an odd number), XOR and rotate of upper half */ + m = (pg_prng_uint64(&state) & mask) | 1; + r = pg_prng_uint64(&state) & mask; + t = size - 1 - v; + if (t <= mask) + { + t = ((t * m) ^ r) & mask; + t = ((t << 1) & mask) | (t >> (masklen - 1)); + v = size - 1 - t; + } + + /* Random offset */ + r = pg_prng_uint64_range(&state, 0, size - 1); + v = (v + r) % size; + } + + return (int64) v; +} diff --git a/src/include/common/pgbench_funcs.h b/src/include/common/pgbench_funcs.h new file mode 100644 index 00000000000..62095d93410 --- /dev/null +++ b/src/include/common/pgbench_funcs.h @@ -0,0 +1,48 @@ +/*------------------------------------------------------------------------- + * + * pgbench_funcs.h + * Shared random distribution, permutation, and hashing functions for + * pgbench and backend extensions. + * + * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/common/pgbench_funcs.h + * + *------------------------------------------------------------------------- + */ +#ifndef PGBENCH_FUNCS_H +#define PGBENCH_FUNCS_H + +#include "common/pg_prng.h" + +/* Parameter boundaries for statistical distributions */ +#define PGBENCH_MIN_GAUSSIAN_PARAM 2.0 +#define PGBENCH_MIN_ZIPFIAN_PARAM 1.001 +#define PGBENCH_MAX_ZIPFIAN_PARAM 1000.0 + +/* Hashing Constants */ +#define PGBENCH_FNV_PRIME UINT64CONST(0x100000001b3) +#define PGBENCH_FNV_OFFSET_BASIS UINT64CONST(0xcbf29ce484222325) +#define PGBENCH_MM2_MUL UINT64CONST(0xc6a4a7935bd1e995) +#define PGBENCH_MM2_MUL_TIMES_8 UINT64CONST(0x35253c9ade8f4ca8) +#define PGBENCH_MM2_ROT 47 + +/* Random Distribution Functions */ +extern int64 pgbench_random(pg_prng_state *state, int64 min, int64 max); +extern int64 pgbench_random_gaussian(pg_prng_state *state, int64 min, int64 max, + double parameter); +extern int64 pgbench_random_exponential(pg_prng_state *state, int64 min, int64 max, + double parameter); +extern int64 pgbench_random_zipfian(pg_prng_state *state, int64 min, int64 max, + double s); +extern int64 pgbench_random_poisson(pg_prng_state *state, double center); + +/* Hashing Functions */ +extern int64 pgbench_hash_fnv1a(int64 val, uint64 seed); +extern int64 pgbench_hash_murmur2(int64 val, uint64 seed); + +/* Permutation Function */ +extern int64 pgbench_permute(int64 val, int64 isize, int64 seed); + +#endif /* PGBENCH_FUNCS_H */ -- 2.55.0.897.gb25b4bd76c-goog