diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2021-11-28 21:32:36 -0500 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2021-11-28 21:33:07 -0500 |
commit | 3804539e48e794781c6145c7f988f5d507418fa8 (patch) | |
tree | 317904b43ca8c1d510b23cb8fdd7b05a75e971bc /src/backend | |
parent | f44ceb46ec2d8da48f6e145bf462d5620c25e079 (diff) | |
download | postgresql-3804539e48e794781c6145c7f988f5d507418fa8.tar.gz postgresql-3804539e48e794781c6145c7f988f5d507418fa8.zip |
Replace random(), pg_erand48(), etc with a better PRNG API and algorithm.
Standardize on xoroshiro128** as our basic PRNG algorithm, eliminating
a bunch of platform dependencies as well as fundamentally-obsolete PRNG
code. In addition, this API replacement will ease replacing the
algorithm again in future, should that become necessary.
xoroshiro128** is a few percent slower than the drand48 family,
but it can produce full-width 64-bit random values not only 48-bit,
and it should be much more trustworthy. It's likely to be noticeably
faster than the platform's random(), depending on which platform you
are thinking about; and we can have non-global state vectors easily,
unlike with random(). It is not cryptographically strong, but neither
are the functions it replaces.
Fabien Coelho, reviewed by Dean Rasheed, Aleksander Alekseev, and myself
Discussion: https://postgr.es/m/alpine.DEB.2.22.394.2105241211230.165418@pseudo
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/access/gin/ginget.c | 3 | ||||
-rw-r--r-- | src/backend/access/gist/gistutil.c | 5 | ||||
-rw-r--r-- | src/backend/access/nbtree/nbtinsert.c | 3 | ||||
-rw-r--r-- | src/backend/access/spgist/spgdoinsert.c | 5 | ||||
-rw-r--r-- | src/backend/access/transam/xact.c | 3 | ||||
-rw-r--r-- | src/backend/commands/analyze.c | 7 | ||||
-rw-r--r-- | src/backend/executor/nodeSamplescan.c | 3 | ||||
-rw-r--r-- | src/backend/lib/bloomfilter.c | 3 | ||||
-rw-r--r-- | src/backend/optimizer/geqo/geqo_random.c | 23 | ||||
-rw-r--r-- | src/backend/optimizer/geqo/geqo_selection.c | 14 | ||||
-rw-r--r-- | src/backend/postmaster/postmaster.c | 20 | ||||
-rw-r--r-- | src/backend/storage/file/fd.c | 4 | ||||
-rw-r--r-- | src/backend/storage/ipc/dsm.c | 9 | ||||
-rw-r--r-- | src/backend/storage/lmgr/Makefile | 5 | ||||
-rw-r--r-- | src/backend/storage/lmgr/s_lock.c | 5 | ||||
-rw-r--r-- | src/backend/tcop/postgres.c | 7 | ||||
-rw-r--r-- | src/backend/utils/adt/float.c | 20 | ||||
-rw-r--r-- | src/backend/utils/misc/sampling.c | 52 |
18 files changed, 110 insertions, 81 deletions
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c index 03191e016ce..e93bf299990 100644 --- a/src/backend/access/gin/ginget.c +++ b/src/backend/access/gin/ginget.c @@ -16,6 +16,7 @@ #include "access/gin_private.h" #include "access/relscan.h" +#include "common/pg_prng.h" #include "miscadmin.h" #include "storage/predicate.h" #include "utils/datum.h" @@ -787,7 +788,7 @@ entryLoadMoreItems(GinState *ginstate, GinScanEntry entry, } } -#define gin_rand() (((double) random()) / ((double) MAX_RANDOM_VALUE)) +#define gin_rand() pg_prng_double(&pg_global_prng_state) #define dropItem(e) ( gin_rand() > ((double)GinFuzzySearchLimit)/((double)((e)->predictNumberResult)) ) /* diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c index 43ba03b6eb9..94dbabc1988 100644 --- a/src/backend/access/gist/gistutil.c +++ b/src/backend/access/gist/gistutil.c @@ -19,6 +19,7 @@ #include "access/htup_details.h" #include "access/reloptions.h" #include "catalog/pg_opclass.h" +#include "common/pg_prng.h" #include "storage/indexfsm.h" #include "storage/lmgr.h" #include "utils/float.h" @@ -507,7 +508,7 @@ gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */ if (keep_current_best == -1) { /* we didn't make the random choice yet for this old best */ - keep_current_best = (random() <= (MAX_RANDOM_VALUE / 2)) ? 1 : 0; + keep_current_best = pg_prng_bool(&pg_global_prng_state) ? 1 : 0; } if (keep_current_best == 0) { @@ -529,7 +530,7 @@ gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */ if (keep_current_best == -1) { /* we didn't make the random choice yet for this old best */ - keep_current_best = (random() <= (MAX_RANDOM_VALUE / 2)) ? 1 : 0; + keep_current_best = pg_prng_bool(&pg_global_prng_state) ? 1 : 0; } if (keep_current_best == 1) break; diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c index 0fe8c709395..37ee0b4d6ee 100644 --- a/src/backend/access/nbtree/nbtinsert.c +++ b/src/backend/access/nbtree/nbtinsert.c @@ -19,6 +19,7 @@ #include "access/nbtxlog.h" #include "access/transam.h" #include "access/xloginsert.h" +#include "common/pg_prng.h" #include "lib/qunique.h" #include "miscadmin.h" #include "storage/lmgr.h" @@ -965,7 +966,7 @@ _bt_findinsertloc(Relation rel, if (P_RIGHTMOST(opaque) || _bt_compare(rel, itup_key, page, P_HIKEY) != 0 || - random() <= (MAX_RANDOM_VALUE / 100)) + pg_prng_uint32(&pg_global_prng_state) <= (PG_UINT32_MAX / 100)) break; _bt_stepright(rel, insertstate, stack); diff --git a/src/backend/access/spgist/spgdoinsert.c b/src/backend/access/spgist/spgdoinsert.c index 70557bcf3d0..e7afb2c242a 100644 --- a/src/backend/access/spgist/spgdoinsert.c +++ b/src/backend/access/spgist/spgdoinsert.c @@ -19,6 +19,7 @@ #include "access/spgist_private.h" #include "access/spgxlog.h" #include "access/xloginsert.h" +#include "common/pg_prng.h" #include "miscadmin.h" #include "storage/bufmgr.h" #include "utils/rel.h" @@ -2210,7 +2211,9 @@ spgdoinsert(Relation index, SpGistState *state, if (out.resultType == spgAddNode) elog(ERROR, "cannot add a node to an allTheSame inner tuple"); else if (out.resultType == spgMatchNode) - out.result.matchNode.nodeN = random() % innerTuple->nNodes; + out.result.matchNode.nodeN = + pg_prng_uint64_range(&pg_global_prng_state, + 0, innerTuple->nNodes - 1); } switch (out.resultType) diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 8e35c432f5c..e7b0bc804d8 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -37,6 +37,7 @@ #include "commands/async.h" #include "commands/tablecmds.h" #include "commands/trigger.h" +#include "common/pg_prng.h" #include "executor/spi.h" #include "libpq/be-fsstubs.h" #include "libpq/pqsignal.h" @@ -1990,7 +1991,7 @@ StartTransaction(void) /* Determine if statements are logged in this transaction */ xact_is_sampled = log_xact_sample_rate != 0 && (log_xact_sample_rate == 1 || - random() <= log_xact_sample_rate * MAX_RANDOM_VALUE); + pg_prng_double(&pg_global_prng_state) <= log_xact_sample_rate); /* * initialize current transaction state fields diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 4928702aec0..cd77907fc74 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -38,6 +38,7 @@ #include "commands/progress.h" #include "commands/tablecmds.h" #include "commands/vacuum.h" +#include "common/pg_prng.h" #include "executor/executor.h" #include "foreign/fdwapi.h" #include "miscadmin.h" @@ -1140,7 +1141,7 @@ acquire_sample_rows(Relation onerel, int elevel, double liverows = 0; /* # live rows seen */ double deadrows = 0; /* # dead rows seen */ double rowstoskip = -1; /* -1 means not set yet */ - long randseed; /* Seed for block sampler(s) */ + uint32 randseed; /* Seed for block sampler(s) */ BlockNumber totalblocks; TransactionId OldestXmin; BlockSamplerData bs; @@ -1162,7 +1163,7 @@ acquire_sample_rows(Relation onerel, int elevel, OldestXmin = GetOldestNonRemovableTransactionId(onerel); /* Prepare for sampling block numbers */ - randseed = random(); + randseed = pg_prng_uint32(&pg_global_prng_state); nblocks = BlockSampler_Init(&bs, totalblocks, targrows, randseed); #ifdef USE_PREFETCH @@ -1279,7 +1280,7 @@ acquire_sample_rows(Relation onerel, int elevel, * Found a suitable tuple, so save it, replacing one old * tuple at random */ - int k = (int) (targrows * sampler_random_fract(rstate.randstate)); + int k = (int) (targrows * sampler_random_fract(&rstate.randstate)); Assert(k >= 0 && k < targrows); heap_freetuple(rows[k]); diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index 44232d50d0a..33b6fb13778 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -17,6 +17,7 @@ #include "access/relscan.h" #include "access/tableam.h" #include "access/tsmapi.h" +#include "common/pg_prng.h" #include "executor/executor.h" #include "executor/nodeSamplescan.h" #include "miscadmin.h" @@ -154,7 +155,7 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags) * do this just once, since the seed shouldn't change over rescans. */ if (tsc->repeatable == NULL) - scanstate->seed = random(); + scanstate->seed = pg_prng_uint32(&pg_global_prng_state); /* * Finally, initialize the TABLESAMPLE method handler. diff --git a/src/backend/lib/bloomfilter.c b/src/backend/lib/bloomfilter.c index daf2c40ebf5..ac6001b712d 100644 --- a/src/backend/lib/bloomfilter.c +++ b/src/backend/lib/bloomfilter.c @@ -81,8 +81,7 @@ static inline uint32 mod_m(uint32 a, uint64 m); * distinct seed value on every call makes it unlikely that the same false * positives will reoccur when the same set is fingerprinted a second time. * Callers that don't care about this pass a constant as their seed, typically - * 0. Callers can use a pseudo-random seed in the range of 0 - INT_MAX by - * calling random(). + * 0. Callers can also use a pseudo-random seed, eg from pg_prng_uint64(). */ bloom_filter * bloom_create(int64 total_elems, int bloom_work_mem, uint64 seed) diff --git a/src/backend/optimizer/geqo/geqo_random.c b/src/backend/optimizer/geqo/geqo_random.c index f21bc047e68..4d9432c0dfe 100644 --- a/src/backend/optimizer/geqo/geqo_random.c +++ b/src/backend/optimizer/geqo/geqo_random.c @@ -21,14 +21,7 @@ geqo_set_seed(PlannerInfo *root, double seed) { GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private; - /* - * XXX. This seeding algorithm could certainly be improved - but it is not - * critical to do so. - */ - memset(private->random_state, 0, sizeof(private->random_state)); - memcpy(private->random_state, - &seed, - Min(sizeof(private->random_state), sizeof(seed))); + pg_prng_fseed(&private->random_state, seed); } double @@ -36,5 +29,17 @@ geqo_rand(PlannerInfo *root) { GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private; - return pg_erand48(private->random_state); + return pg_prng_double(&private->random_state); +} + +int +geqo_randint(PlannerInfo *root, int upper, int lower) +{ + GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private; + + /* + * In current usage, "lower" is never negative so we can just use + * pg_prng_uint64_range directly. + */ + return (int) pg_prng_uint64_range(&private->random_state, lower, upper); } diff --git a/src/backend/optimizer/geqo/geqo_selection.c b/src/backend/optimizer/geqo/geqo_selection.c index 66b6c8ae38e..68a2e4e9f15 100644 --- a/src/backend/optimizer/geqo/geqo_selection.c +++ b/src/backend/optimizer/geqo/geqo_selection.c @@ -63,10 +63,6 @@ geqo_selection(PlannerInfo *root, Chromosome *momma, Chromosome *daddy, /* * Ensure we have selected different genes, except if pool size is only * one, when we can't. - * - * This code was observed to hang up in an infinite loop when the - * platform's implementation of erand48() was broken. We now always use - * our own version. */ if (pool->size > 1) { @@ -95,11 +91,11 @@ linear_rand(PlannerInfo *root, int pool_size, double bias) double max = (double) pool_size; /* - * If geqo_rand() returns exactly 1.0 then we will get exactly max from - * this equation, whereas we need 0 <= index < max. Also it seems - * possible that roundoff error might deliver values slightly outside the - * range; in particular avoid passing a value slightly less than 0 to - * sqrt(). If we get a bad value just try again. + * geqo_rand() is not supposed to return 1.0, but if it does then we will + * get exactly max from this equation, whereas we need 0 <= index < max. + * Also it seems possible that roundoff error might deliver values + * slightly outside the range; in particular avoid passing a value + * slightly less than 0 to sqrt(). If we get a bad value just try again. */ do { diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index db797c040bf..328ecafa8cb 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -98,6 +98,7 @@ #include "catalog/pg_control.h" #include "common/file_perm.h" #include "common/ip.h" +#include "common/pg_prng.h" #include "common/string.h" #include "lib/ilist.h" #include "libpq/auth.h" @@ -2699,19 +2700,19 @@ ClosePostmasterPorts(bool am_syslogger) void InitProcessGlobals(void) { - unsigned int rseed; - MyProcPid = getpid(); MyStartTimestamp = GetCurrentTimestamp(); MyStartTime = timestamptz_to_time_t(MyStartTimestamp); /* - * Set a different seed for random() in every process. We want something + * Set a different global seed in every process. We want something * unpredictable, so if possible, use high-quality random bits for the * seed. Otherwise, fall back to a seed based on timestamp and PID. */ - if (!pg_strong_random(&rseed, sizeof(rseed))) + if (unlikely(!pg_prng_strong_seed(&pg_global_prng_state))) { + uint64 rseed; + /* * Since PIDs and timestamps tend to change more frequently in their * least significant bits, shift the timestamp left to allow a larger @@ -2722,8 +2723,17 @@ InitProcessGlobals(void) rseed = ((uint64) MyProcPid) ^ ((uint64) MyStartTimestamp << 12) ^ ((uint64) MyStartTimestamp >> 20); + + pg_prng_seed(&pg_global_prng_state, rseed); } - srandom(rseed); + + /* + * Also make sure that we've set a good seed for random(3). Use of that + * is deprecated in core Postgres, but extensions might use it. + */ +#ifndef WIN32 + srandom(pg_prng_uint32(&pg_global_prng_state)); +#endif } diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c index cb1a8dd34f2..263057841dd 100644 --- a/src/backend/storage/file/fd.c +++ b/src/backend/storage/file/fd.c @@ -92,6 +92,7 @@ #include "catalog/pg_tablespace.h" #include "common/file_perm.h" #include "common/file_utils.h" +#include "common/pg_prng.h" #include "miscadmin.h" #include "pgstat.h" #include "port/pg_iovec.h" @@ -2939,7 +2940,8 @@ SetTempTablespaces(Oid *tableSpaces, int numSpaces) * available tablespaces. */ if (numSpaces > 1) - nextTempTableSpace = random() % numSpaces; + nextTempTableSpace = pg_prng_uint64_range(&pg_global_prng_state, + 0, numSpaces - 1); else nextTempTableSpace = 0; } diff --git a/src/backend/storage/ipc/dsm.c b/src/backend/storage/ipc/dsm.c index b461a5f7e96..074dff4e6d8 100644 --- a/src/backend/storage/ipc/dsm.c +++ b/src/backend/storage/ipc/dsm.c @@ -33,6 +33,7 @@ #endif #include <sys/stat.h> +#include "common/pg_prng.h" #include "lib/ilist.h" #include "miscadmin.h" #include "port/pg_bitutils.h" @@ -180,7 +181,8 @@ dsm_postmaster_startup(PGShmemHeader *shim) { Assert(dsm_control_address == NULL); Assert(dsm_control_mapped_size == 0); - dsm_control_handle = random() << 1; /* Even numbers only */ + /* Use even numbers only */ + dsm_control_handle = pg_prng_uint32(&pg_global_prng_state) << 1; if (dsm_control_handle == DSM_HANDLE_INVALID) continue; if (dsm_impl_op(DSM_OP_CREATE, dsm_control_handle, segsize, @@ -536,7 +538,8 @@ dsm_create(Size size, int flags) for (;;) { Assert(seg->mapped_address == NULL && seg->mapped_size == 0); - seg->handle = random() << 1; /* Even numbers only */ + /* Use even numbers only */ + seg->handle = pg_prng_uint32(&pg_global_prng_state) << 1; if (seg->handle == DSM_HANDLE_INVALID) /* Reserve sentinel */ continue; if (dsm_impl_op(DSM_OP_CREATE, seg->handle, size, &seg->impl_private, @@ -1237,7 +1240,7 @@ make_main_region_dsm_handle(int slot) */ handle = 1; handle |= slot << 1; - handle |= random() << (pg_leftmost_one_pos32(dsm_control->maxitems) + 1); + handle |= pg_prng_uint32(&pg_global_prng_state) << (pg_leftmost_one_pos32(dsm_control->maxitems) + 1); return handle; } diff --git a/src/backend/storage/lmgr/Makefile b/src/backend/storage/lmgr/Makefile index 829b792fcb1..b25b7ee421d 100644 --- a/src/backend/storage/lmgr/Makefile +++ b/src/backend/storage/lmgr/Makefile @@ -30,9 +30,10 @@ ifdef TAS TASPATH = $(top_builddir)/src/backend/port/tas.o endif -s_lock_test: s_lock.c $(top_builddir)/src/port/libpgport.a +s_lock_test: s_lock.c $(top_builddir)/src/common/libpgcommon.a $(top_builddir)/src/port/libpgport.a $(CC) $(CPPFLAGS) $(CFLAGS) -DS_LOCK_TEST=1 $(srcdir)/s_lock.c \ - $(TASPATH) -L $(top_builddir)/src/port -lpgport -o s_lock_test + $(TASPATH) -L $(top_builddir)/src/common -lpgcommon \ + -L $(top_builddir)/src/port -lpgport -o s_lock_test # see notes in src/backend/parser/Makefile lwlocknames.c: lwlocknames.h diff --git a/src/backend/storage/lmgr/s_lock.c b/src/backend/storage/lmgr/s_lock.c index 91322a40c1c..75c0f4535cd 100644 --- a/src/backend/storage/lmgr/s_lock.c +++ b/src/backend/storage/lmgr/s_lock.c @@ -50,6 +50,7 @@ #include <time.h> #include <unistd.h> +#include "common/pg_prng.h" #include "port/atomics.h" #include "storage/s_lock.h" @@ -144,7 +145,7 @@ perform_spin_delay(SpinDelayStatus *status) /* increase delay by a random fraction between 1X and 2X */ status->cur_delay += (int) (status->cur_delay * - ((double) random() / (double) MAX_RANDOM_VALUE) + 0.5); + pg_prng_double(&pg_global_prng_state) + 0.5); /* wrap back to minimum delay when max is exceeded */ if (status->cur_delay > MAX_DELAY_USEC) status->cur_delay = MIN_DELAY_USEC; @@ -303,7 +304,7 @@ volatile struct test_lock_struct test_lock; int main() { - srandom((unsigned int) time(NULL)); + pg_prng_seed(&pg_global_prng_state, (uint64) time(NULL)); test_lock.pad1 = test_lock.pad2 = 0x44; diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 0775abe35de..82de01cdc67 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -42,6 +42,7 @@ #include "catalog/pg_type.h" #include "commands/async.h" #include "commands/prepare.h" +#include "common/pg_prng.h" #include "executor/spi.h" #include "jit/jit.h" #include "libpq/libpq.h" @@ -2355,13 +2356,13 @@ check_log_duration(char *msec_str, bool was_logged) /* * Do not log if log_statement_sample_rate = 0. Log a sample if - * log_statement_sample_rate <= 1 and avoid unnecessary random() call - * if log_statement_sample_rate = 1. + * log_statement_sample_rate <= 1 and avoid unnecessary PRNG call if + * log_statement_sample_rate = 1. */ if (exceeded_sample_duration) in_sample = log_statement_sample_rate != 0 && (log_statement_sample_rate == 1 || - random() <= log_statement_sample_rate * MAX_RANDOM_VALUE); + pg_prng_double(&pg_global_prng_state) <= log_statement_sample_rate); if (exceeded_duration || in_sample || log_duration || xact_is_sampled) { diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c index 098bbb372bf..455e5d8cbea 100644 --- a/src/backend/utils/adt/float.c +++ b/src/backend/utils/adt/float.c @@ -21,6 +21,7 @@ #include "catalog/pg_type.h" #include "common/int.h" +#include "common/pg_prng.h" #include "common/shortest_dec.h" #include "libpq/pqformat.h" #include "miscadmin.h" @@ -65,7 +66,7 @@ float8 degree_c_one = 1.0; /* State for drandom() and setseed() */ static bool drandom_seed_set = false; -static unsigned short drandom_seed[3] = {0, 0, 0}; +static pg_prng_state drandom_seed; /* Local function prototypes */ static double sind_q1(double x); @@ -2762,22 +2763,20 @@ drandom(PG_FUNCTION_ARGS) * Should that fail for some reason, we fall back on a lower-quality * seed based on current time and PID. */ - if (!pg_strong_random(drandom_seed, sizeof(drandom_seed))) + if (unlikely(!pg_prng_strong_seed(&drandom_seed))) { TimestampTz now = GetCurrentTimestamp(); uint64 iseed; /* Mix the PID with the most predictable bits of the timestamp */ iseed = (uint64) now ^ ((uint64) MyProcPid << 32); - drandom_seed[0] = (unsigned short) iseed; - drandom_seed[1] = (unsigned short) (iseed >> 16); - drandom_seed[2] = (unsigned short) (iseed >> 32); + pg_prng_seed(&drandom_seed, iseed); } drandom_seed_set = true; } - /* pg_erand48 produces desired result range [0.0 - 1.0) */ - result = pg_erand48(drandom_seed); + /* pg_prng_double produces desired result range [0.0 - 1.0) */ + result = pg_prng_double(&drandom_seed); PG_RETURN_FLOAT8(result); } @@ -2790,7 +2789,6 @@ Datum setseed(PG_FUNCTION_ARGS) { float8 seed = PG_GETARG_FLOAT8(0); - uint64 iseed; if (seed < -1 || seed > 1 || isnan(seed)) ereport(ERROR, @@ -2798,11 +2796,7 @@ setseed(PG_FUNCTION_ARGS) errmsg("setseed parameter %g is out of allowed range [-1,1]", seed))); - /* Use sign bit + 47 fractional bits to fill drandom_seed[] */ - iseed = (int64) (seed * (float8) UINT64CONST(0x7FFFFFFFFFFF)); - drandom_seed[0] = (unsigned short) iseed; - drandom_seed[1] = (unsigned short) (iseed >> 16); - drandom_seed[2] = (unsigned short) (iseed >> 32); + pg_prng_fseed(&drandom_seed, seed); drandom_seed_set = true; PG_RETURN_VOID(); diff --git a/src/backend/utils/misc/sampling.c b/src/backend/utils/misc/sampling.c index 0c327e823f7..d1a2416e8b7 100644 --- a/src/backend/utils/misc/sampling.c +++ b/src/backend/utils/misc/sampling.c @@ -37,7 +37,7 @@ */ BlockNumber BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize, - long randseed) + uint32 randseed) { bs->N = nblocks; /* measured table size */ @@ -49,7 +49,7 @@ BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize, bs->t = 0; /* blocks scanned so far */ bs->m = 0; /* blocks selected so far */ - sampler_random_init_state(randseed, bs->randstate); + sampler_random_init_state(randseed, &bs->randstate); return Min(bs->n, bs->N); } @@ -98,7 +98,7 @@ BlockSampler_Next(BlockSampler bs) * less than k, which means that we cannot fail to select enough blocks. *---------- */ - V = sampler_random_fract(bs->randstate); + V = sampler_random_fract(&bs->randstate); p = 1.0 - (double) k / (double) K; while (V < p) { @@ -136,10 +136,11 @@ reservoir_init_selection_state(ReservoirState rs, int n) * Reservoir sampling is not used anywhere where it would need to return * repeatable results so we can initialize it randomly. */ - sampler_random_init_state(random(), rs->randstate); + sampler_random_init_state(pg_prng_uint32(&pg_global_prng_state), + &rs->randstate); /* Initial value of W (for use when Algorithm Z is first applied) */ - rs->W = exp(-log(sampler_random_fract(rs->randstate)) / n); + rs->W = exp(-log(sampler_random_fract(&rs->randstate)) / n); } double @@ -154,7 +155,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n) double V, quot; - V = sampler_random_fract(rs->randstate); /* Generate V */ + V = sampler_random_fract(&rs->randstate); /* Generate V */ S = 0; t += 1; /* Note: "num" in Vitter's code is always equal to t - n */ @@ -186,7 +187,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n) tmp; /* Generate U and X */ - U = sampler_random_fract(rs->randstate); + U = sampler_random_fract(&rs->randstate); X = t * (W - 1.0); S = floor(X); /* S is tentatively set to floor(X) */ /* Test if U <= h(S)/cg(X) in the manner of (6.3) */ @@ -215,7 +216,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n) y *= numer / denom; denom -= 1; } - W = exp(-log(sampler_random_fract(rs->randstate)) / n); /* Generate W in advance */ + W = exp(-log(sampler_random_fract(&rs->randstate)) / n); /* Generate W in advance */ if (exp(log(y) / n) <= (t + X) / t) break; } @@ -230,24 +231,22 @@ reservoir_get_next_S(ReservoirState rs, double t, int n) *---------- */ void -sampler_random_init_state(long seed, SamplerRandomState randstate) +sampler_random_init_state(uint32 seed, pg_prng_state *randstate) { - randstate[0] = 0x330e; /* same as pg_erand48, but could be anything */ - randstate[1] = (unsigned short) seed; - randstate[2] = (unsigned short) (seed >> 16); + pg_prng_seed(randstate, (uint64) seed); } /* Select a random value R uniformly distributed in (0 - 1) */ double -sampler_random_fract(SamplerRandomState randstate) +sampler_random_fract(pg_prng_state *randstate) { double res; - /* pg_erand48 returns a value in [0.0 - 1.0), so we must reject 0 */ + /* pg_prng_double returns a value in [0.0 - 1.0), so we must reject 0.0 */ do { - res = pg_erand48(randstate); - } while (res == 0.0); + res = pg_prng_double(randstate); + } while (unlikely(res == 0.0)); return res; } @@ -261,27 +260,36 @@ sampler_random_fract(SamplerRandomState randstate) * except that a common random state is used across all callers. */ static ReservoirStateData oldrs; +static bool oldrs_initialized = false; double anl_random_fract(void) { /* initialize if first time through */ - if (oldrs.randstate[0] == 0) - sampler_random_init_state(random(), oldrs.randstate); + if (unlikely(!oldrs_initialized)) + { + sampler_random_init_state(pg_prng_uint32(&pg_global_prng_state), + &oldrs.randstate); + oldrs_initialized = true; + } /* and compute a random fraction */ - return sampler_random_fract(oldrs.randstate); + return sampler_random_fract(&oldrs.randstate); } double anl_init_selection_state(int n) { /* initialize if first time through */ - if (oldrs.randstate[0] == 0) - sampler_random_init_state(random(), oldrs.randstate); + if (unlikely(!oldrs_initialized)) + { + sampler_random_init_state(pg_prng_uint32(&pg_global_prng_state), + &oldrs.randstate); + oldrs_initialized = true; + } /* Initial value of W (for use when Algorithm Z is first applied) */ - return exp(-log(sampler_random_fract(oldrs.randstate)) / n); + return exp(-log(sampler_random_fract(&oldrs.randstate)) / n); } double |