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
This commit is contained in:
Tom Lane 2021-11-28 21:32:36 -05:00
parent f44ceb46ec
commit 3804539e48
50 changed files with 543 additions and 480 deletions

26
configure vendored
View File

@ -16463,32 +16463,6 @@ esac
fi fi
ac_fn_c_check_func "$LINENO" "random" "ac_cv_func_random"
if test "x$ac_cv_func_random" = xyes; then :
$as_echo "#define HAVE_RANDOM 1" >>confdefs.h
else
case " $LIBOBJS " in
*" random.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS random.$ac_objext"
;;
esac
fi
ac_fn_c_check_func "$LINENO" "srandom" "ac_cv_func_srandom"
if test "x$ac_cv_func_srandom" = xyes; then :
$as_echo "#define HAVE_SRANDOM 1" >>confdefs.h
else
case " $LIBOBJS " in
*" srandom.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS srandom.$ac_objext"
;;
esac
fi
ac_fn_c_check_func "$LINENO" "strlcat" "ac_cv_func_strlcat" ac_fn_c_check_func "$LINENO" "strlcat" "ac_cv_func_strlcat"
if test "x$ac_cv_func_strlcat" = xyes; then : if test "x$ac_cv_func_strlcat" = xyes; then :
$as_echo "#define HAVE_STRLCAT 1" >>confdefs.h $as_echo "#define HAVE_STRLCAT 1" >>confdefs.h

View File

@ -1858,8 +1858,6 @@ AC_REPLACE_FUNCS(m4_normalize([
mkdtemp mkdtemp
pread pread
pwrite pwrite
random
srandom
strlcat strlcat
strlcpy strlcpy
strnlen strnlen

View File

@ -32,6 +32,7 @@
#include "catalog/index.h" #include "catalog/index.h"
#include "catalog/pg_am.h" #include "catalog/pg_am.h"
#include "commands/tablecmds.h" #include "commands/tablecmds.h"
#include "common/pg_prng.h"
#include "lib/bloomfilter.h" #include "lib/bloomfilter.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
@ -466,8 +467,8 @@ bt_check_every_level(Relation rel, Relation heaprel, bool heapkeyspace,
total_pages = RelationGetNumberOfBlocks(rel); total_pages = RelationGetNumberOfBlocks(rel);
total_elems = Max(total_pages * (MaxTIDsPerBTreePage / 3), total_elems = Max(total_pages * (MaxTIDsPerBTreePage / 3),
(int64) state->rel->rd_rel->reltuples); (int64) state->rel->rd_rel->reltuples);
/* Random seed relies on backend srandom() call to avoid repetition */ /* Generate a random seed to avoid repetition */
seed = random(); seed = pg_prng_uint64(&pg_global_prng_state);
/* Create Bloom filter to fingerprint index */ /* Create Bloom filter to fingerprint index */
state->filter = bloom_create(total_elems, maintenance_work_mem, seed); state->filter = bloom_create(total_elems, maintenance_work_mem, seed);
state->heaptuplespresent = 0; state->heaptuplespresent = 0;

View File

@ -16,6 +16,7 @@
#include "access/parallel.h" #include "access/parallel.h"
#include "commands/explain.h" #include "commands/explain.h"
#include "common/pg_prng.h"
#include "executor/instrument.h" #include "executor/instrument.h"
#include "jit/jit.h" #include "jit/jit.h"
#include "utils/guc.h" #include "utils/guc.h"
@ -275,8 +276,7 @@ explain_ExecutorStart(QueryDesc *queryDesc, int eflags)
if (nesting_level == 0) if (nesting_level == 0)
{ {
if (auto_explain_log_min_duration >= 0 && !IsParallelWorker()) if (auto_explain_log_min_duration >= 0 && !IsParallelWorker())
current_query_sampled = (random() < auto_explain_sample_rate * current_query_sampled = (pg_prng_double(&pg_global_prng_state) < auto_explain_sample_rate);
((double) MAX_RANDOM_VALUE + 1));
else else
current_query_sampled = false; current_query_sampled = false;
} }

View File

@ -1188,7 +1188,7 @@ file_acquire_sample_rows(Relation onerel, int elevel,
* Found a suitable tuple, so save it, replacing one old tuple * Found a suitable tuple, so save it, replacing one old tuple
* at random * 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); Assert(k >= 0 && k < targrows);
heap_freetuple(rows[k]); heap_freetuple(rows[k]);

View File

@ -5158,7 +5158,7 @@ analyze_row_processor(PGresult *res, int row, PgFdwAnalyzeState *astate)
if (astate->rowstoskip <= 0) if (astate->rowstoskip <= 0)
{ {
/* Choose a random reservoir element to replace. */ /* Choose a random reservoir element to replace. */
pos = (int) (targrows * sampler_random_fract(astate->rstate.randstate)); pos = (int) (targrows * sampler_random_fract(&astate->rstate.randstate));
Assert(pos >= 0 && pos < targrows); Assert(pos >= 0 && pos < targrows);
heap_freetuple(astate->rows[pos]); heap_freetuple(astate->rows[pos]);
} }

View File

@ -36,6 +36,7 @@
#include "access/htup_details.h" #include "access/htup_details.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "common/pg_prng.h"
#include "executor/spi.h" #include "executor/spi.h"
#include "funcapi.h" #include "funcapi.h"
#include "lib/stringinfo.h" #include "lib/stringinfo.h"
@ -290,8 +291,8 @@ get_normal_pair(float8 *x1, float8 *x2)
do do
{ {
u1 = (float8) random() / (float8) MAX_RANDOM_VALUE; u1 = pg_prng_double(&pg_global_prng_state);
u2 = (float8) random() / (float8) MAX_RANDOM_VALUE; u2 = pg_prng_double(&pg_global_prng_state);
v1 = (2.0 * u1) - 1.0; v1 = (2.0 * u1) - 1.0;
v2 = (2.0 * u2) - 1.0; v2 = (2.0 * u2) - 1.0;

View File

@ -69,7 +69,7 @@ static BlockNumber system_rows_nextsampleblock(SampleScanState *node, BlockNumbe
static OffsetNumber system_rows_nextsampletuple(SampleScanState *node, static OffsetNumber system_rows_nextsampletuple(SampleScanState *node,
BlockNumber blockno, BlockNumber blockno,
OffsetNumber maxoffset); OffsetNumber maxoffset);
static uint32 random_relative_prime(uint32 n, SamplerRandomState randstate); static uint32 random_relative_prime(uint32 n, pg_prng_state *randstate);
/* /*
@ -213,25 +213,25 @@ system_rows_nextsampleblock(SampleScanState *node, BlockNumber nblocks)
if (sampler->step == 0) if (sampler->step == 0)
{ {
/* Initialize now that we have scan descriptor */ /* Initialize now that we have scan descriptor */
SamplerRandomState randstate; pg_prng_state randstate;
/* If relation is empty, there's nothing to scan */ /* If relation is empty, there's nothing to scan */
if (nblocks == 0) if (nblocks == 0)
return InvalidBlockNumber; return InvalidBlockNumber;
/* We only need an RNG during this setup step */ /* We only need an RNG during this setup step */
sampler_random_init_state(sampler->seed, randstate); sampler_random_init_state(sampler->seed, &randstate);
/* Compute nblocks/firstblock/step only once per query */ /* Compute nblocks/firstblock/step only once per query */
sampler->nblocks = nblocks; sampler->nblocks = nblocks;
/* Choose random starting block within the relation */ /* Choose random starting block within the relation */
/* (Actually this is the predecessor of the first block visited) */ /* (Actually this is the predecessor of the first block visited) */
sampler->firstblock = sampler_random_fract(randstate) * sampler->firstblock = sampler_random_fract(&randstate) *
sampler->nblocks; sampler->nblocks;
/* Find relative prime as step size for linear probing */ /* Find relative prime as step size for linear probing */
sampler->step = random_relative_prime(sampler->nblocks, randstate); sampler->step = random_relative_prime(sampler->nblocks, &randstate);
} }
/* Reinitialize lb */ /* Reinitialize lb */
@ -317,7 +317,7 @@ gcd(uint32 a, uint32 b)
* (else return 1). * (else return 1).
*/ */
static uint32 static uint32
random_relative_prime(uint32 n, SamplerRandomState randstate) random_relative_prime(uint32 n, pg_prng_state *randstate)
{ {
uint32 r; uint32 r;

View File

@ -69,7 +69,7 @@ static BlockNumber system_time_nextsampleblock(SampleScanState *node, BlockNumbe
static OffsetNumber system_time_nextsampletuple(SampleScanState *node, static OffsetNumber system_time_nextsampletuple(SampleScanState *node,
BlockNumber blockno, BlockNumber blockno,
OffsetNumber maxoffset); OffsetNumber maxoffset);
static uint32 random_relative_prime(uint32 n, SamplerRandomState randstate); static uint32 random_relative_prime(uint32 n, pg_prng_state *randstate);
/* /*
@ -224,25 +224,25 @@ system_time_nextsampleblock(SampleScanState *node, BlockNumber nblocks)
if (sampler->step == 0) if (sampler->step == 0)
{ {
/* Initialize now that we have scan descriptor */ /* Initialize now that we have scan descriptor */
SamplerRandomState randstate; pg_prng_state randstate;
/* If relation is empty, there's nothing to scan */ /* If relation is empty, there's nothing to scan */
if (nblocks == 0) if (nblocks == 0)
return InvalidBlockNumber; return InvalidBlockNumber;
/* We only need an RNG during this setup step */ /* We only need an RNG during this setup step */
sampler_random_init_state(sampler->seed, randstate); sampler_random_init_state(sampler->seed, &randstate);
/* Compute nblocks/firstblock/step only once per query */ /* Compute nblocks/firstblock/step only once per query */
sampler->nblocks = nblocks; sampler->nblocks = nblocks;
/* Choose random starting block within the relation */ /* Choose random starting block within the relation */
/* (Actually this is the predecessor of the first block visited) */ /* (Actually this is the predecessor of the first block visited) */
sampler->firstblock = sampler_random_fract(randstate) * sampler->firstblock = sampler_random_fract(&randstate) *
sampler->nblocks; sampler->nblocks;
/* Find relative prime as step size for linear probing */ /* Find relative prime as step size for linear probing */
sampler->step = random_relative_prime(sampler->nblocks, randstate); sampler->step = random_relative_prime(sampler->nblocks, &randstate);
} }
/* Reinitialize lb and start_time */ /* Reinitialize lb and start_time */
@ -330,7 +330,7 @@ gcd(uint32 a, uint32 b)
* (else return 1). * (else return 1).
*/ */
static uint32 static uint32
random_relative_prime(uint32 n, SamplerRandomState randstate) random_relative_prime(uint32 n, pg_prng_state *randstate)
{ {
uint32 r; uint32 r;

View File

@ -16,6 +16,7 @@
#include "access/gin_private.h" #include "access/gin_private.h"
#include "access/relscan.h" #include "access/relscan.h"
#include "common/pg_prng.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "storage/predicate.h" #include "storage/predicate.h"
#include "utils/datum.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)) ) #define dropItem(e) ( gin_rand() > ((double)GinFuzzySearchLimit)/((double)((e)->predictNumberResult)) )
/* /*

View File

@ -19,6 +19,7 @@
#include "access/htup_details.h" #include "access/htup_details.h"
#include "access/reloptions.h" #include "access/reloptions.h"
#include "catalog/pg_opclass.h" #include "catalog/pg_opclass.h"
#include "common/pg_prng.h"
#include "storage/indexfsm.h" #include "storage/indexfsm.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
#include "utils/float.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) if (keep_current_best == -1)
{ {
/* we didn't make the random choice yet for this old best */ /* 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) 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) if (keep_current_best == -1)
{ {
/* we didn't make the random choice yet for this old best */ /* 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) if (keep_current_best == 1)
break; break;

View File

@ -19,6 +19,7 @@
#include "access/nbtxlog.h" #include "access/nbtxlog.h"
#include "access/transam.h" #include "access/transam.h"
#include "access/xloginsert.h" #include "access/xloginsert.h"
#include "common/pg_prng.h"
#include "lib/qunique.h" #include "lib/qunique.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
@ -965,7 +966,7 @@ _bt_findinsertloc(Relation rel,
if (P_RIGHTMOST(opaque) || if (P_RIGHTMOST(opaque) ||
_bt_compare(rel, itup_key, page, P_HIKEY) != 0 || _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; break;
_bt_stepright(rel, insertstate, stack); _bt_stepright(rel, insertstate, stack);

View File

@ -19,6 +19,7 @@
#include "access/spgist_private.h" #include "access/spgist_private.h"
#include "access/spgxlog.h" #include "access/spgxlog.h"
#include "access/xloginsert.h" #include "access/xloginsert.h"
#include "common/pg_prng.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "utils/rel.h" #include "utils/rel.h"
@ -2210,7 +2211,9 @@ spgdoinsert(Relation index, SpGistState *state,
if (out.resultType == spgAddNode) if (out.resultType == spgAddNode)
elog(ERROR, "cannot add a node to an allTheSame inner tuple"); elog(ERROR, "cannot add a node to an allTheSame inner tuple");
else if (out.resultType == spgMatchNode) 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) switch (out.resultType)

View File

@ -37,6 +37,7 @@
#include "commands/async.h" #include "commands/async.h"
#include "commands/tablecmds.h" #include "commands/tablecmds.h"
#include "commands/trigger.h" #include "commands/trigger.h"
#include "common/pg_prng.h"
#include "executor/spi.h" #include "executor/spi.h"
#include "libpq/be-fsstubs.h" #include "libpq/be-fsstubs.h"
#include "libpq/pqsignal.h" #include "libpq/pqsignal.h"
@ -1990,7 +1991,7 @@ StartTransaction(void)
/* Determine if statements are logged in this transaction */ /* Determine if statements are logged in this transaction */
xact_is_sampled = log_xact_sample_rate != 0 && xact_is_sampled = log_xact_sample_rate != 0 &&
(log_xact_sample_rate == 1 || (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 * initialize current transaction state fields

View File

@ -38,6 +38,7 @@
#include "commands/progress.h" #include "commands/progress.h"
#include "commands/tablecmds.h" #include "commands/tablecmds.h"
#include "commands/vacuum.h" #include "commands/vacuum.h"
#include "common/pg_prng.h"
#include "executor/executor.h" #include "executor/executor.h"
#include "foreign/fdwapi.h" #include "foreign/fdwapi.h"
#include "miscadmin.h" #include "miscadmin.h"
@ -1140,7 +1141,7 @@ acquire_sample_rows(Relation onerel, int elevel,
double liverows = 0; /* # live rows seen */ double liverows = 0; /* # live rows seen */
double deadrows = 0; /* # dead rows seen */ double deadrows = 0; /* # dead rows seen */
double rowstoskip = -1; /* -1 means not set yet */ double rowstoskip = -1; /* -1 means not set yet */
long randseed; /* Seed for block sampler(s) */ uint32 randseed; /* Seed for block sampler(s) */
BlockNumber totalblocks; BlockNumber totalblocks;
TransactionId OldestXmin; TransactionId OldestXmin;
BlockSamplerData bs; BlockSamplerData bs;
@ -1162,7 +1163,7 @@ acquire_sample_rows(Relation onerel, int elevel,
OldestXmin = GetOldestNonRemovableTransactionId(onerel); OldestXmin = GetOldestNonRemovableTransactionId(onerel);
/* Prepare for sampling block numbers */ /* Prepare for sampling block numbers */
randseed = random(); randseed = pg_prng_uint32(&pg_global_prng_state);
nblocks = BlockSampler_Init(&bs, totalblocks, targrows, randseed); nblocks = BlockSampler_Init(&bs, totalblocks, targrows, randseed);
#ifdef USE_PREFETCH #ifdef USE_PREFETCH
@ -1279,7 +1280,7 @@ acquire_sample_rows(Relation onerel, int elevel,
* Found a suitable tuple, so save it, replacing one old * Found a suitable tuple, so save it, replacing one old
* tuple at random * 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); Assert(k >= 0 && k < targrows);
heap_freetuple(rows[k]); heap_freetuple(rows[k]);

View File

@ -17,6 +17,7 @@
#include "access/relscan.h" #include "access/relscan.h"
#include "access/tableam.h" #include "access/tableam.h"
#include "access/tsmapi.h" #include "access/tsmapi.h"
#include "common/pg_prng.h"
#include "executor/executor.h" #include "executor/executor.h"
#include "executor/nodeSamplescan.h" #include "executor/nodeSamplescan.h"
#include "miscadmin.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. * do this just once, since the seed shouldn't change over rescans.
*/ */
if (tsc->repeatable == NULL) if (tsc->repeatable == NULL)
scanstate->seed = random(); scanstate->seed = pg_prng_uint32(&pg_global_prng_state);
/* /*
* Finally, initialize the TABLESAMPLE method handler. * Finally, initialize the TABLESAMPLE method handler.

View File

@ -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 * 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. * 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 * 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 * 0. Callers can also use a pseudo-random seed, eg from pg_prng_uint64().
* calling random().
*/ */
bloom_filter * bloom_filter *
bloom_create(int64 total_elems, int bloom_work_mem, uint64 seed) bloom_create(int64 total_elems, int bloom_work_mem, uint64 seed)

View File

@ -21,14 +21,7 @@ geqo_set_seed(PlannerInfo *root, double seed)
{ {
GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private; GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
/* pg_prng_fseed(&private->random_state, seed);
* 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)));
} }
double double
@ -36,5 +29,17 @@ geqo_rand(PlannerInfo *root)
{ {
GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private; 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);
} }

View File

@ -63,10 +63,6 @@ geqo_selection(PlannerInfo *root, Chromosome *momma, Chromosome *daddy,
/* /*
* Ensure we have selected different genes, except if pool size is only * Ensure we have selected different genes, except if pool size is only
* one, when we can't. * 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) if (pool->size > 1)
{ {
@ -95,11 +91,11 @@ linear_rand(PlannerInfo *root, int pool_size, double bias)
double max = (double) pool_size; double max = (double) pool_size;
/* /*
* If geqo_rand() returns exactly 1.0 then we will get exactly max from * geqo_rand() is not supposed to return 1.0, but if it does then we will
* this equation, whereas we need 0 <= index < max. Also it seems * get exactly max from this equation, whereas we need 0 <= index < max.
* possible that roundoff error might deliver values slightly outside the * Also it seems possible that roundoff error might deliver values
* range; in particular avoid passing a value slightly less than 0 to * slightly outside the range; in particular avoid passing a value
* sqrt(). If we get a bad value just try again. * slightly less than 0 to sqrt(). If we get a bad value just try again.
*/ */
do do
{ {

View File

@ -98,6 +98,7 @@
#include "catalog/pg_control.h" #include "catalog/pg_control.h"
#include "common/file_perm.h" #include "common/file_perm.h"
#include "common/ip.h" #include "common/ip.h"
#include "common/pg_prng.h"
#include "common/string.h" #include "common/string.h"
#include "lib/ilist.h" #include "lib/ilist.h"
#include "libpq/auth.h" #include "libpq/auth.h"
@ -2699,19 +2700,19 @@ ClosePostmasterPorts(bool am_syslogger)
void void
InitProcessGlobals(void) InitProcessGlobals(void)
{ {
unsigned int rseed;
MyProcPid = getpid(); MyProcPid = getpid();
MyStartTimestamp = GetCurrentTimestamp(); MyStartTimestamp = GetCurrentTimestamp();
MyStartTime = timestamptz_to_time_t(MyStartTimestamp); 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 * unpredictable, so if possible, use high-quality random bits for the
* seed. Otherwise, fall back to a seed based on timestamp and PID. * 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 * Since PIDs and timestamps tend to change more frequently in their
* least significant bits, shift the timestamp left to allow a larger * least significant bits, shift the timestamp left to allow a larger
@ -2722,8 +2723,17 @@ InitProcessGlobals(void)
rseed = ((uint64) MyProcPid) ^ rseed = ((uint64) MyProcPid) ^
((uint64) MyStartTimestamp << 12) ^ ((uint64) MyStartTimestamp << 12) ^
((uint64) MyStartTimestamp >> 20); ((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
} }

View File

@ -92,6 +92,7 @@
#include "catalog/pg_tablespace.h" #include "catalog/pg_tablespace.h"
#include "common/file_perm.h" #include "common/file_perm.h"
#include "common/file_utils.h" #include "common/file_utils.h"
#include "common/pg_prng.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "pgstat.h" #include "pgstat.h"
#include "port/pg_iovec.h" #include "port/pg_iovec.h"
@ -2939,7 +2940,8 @@ SetTempTablespaces(Oid *tableSpaces, int numSpaces)
* available tablespaces. * available tablespaces.
*/ */
if (numSpaces > 1) if (numSpaces > 1)
nextTempTableSpace = random() % numSpaces; nextTempTableSpace = pg_prng_uint64_range(&pg_global_prng_state,
0, numSpaces - 1);
else else
nextTempTableSpace = 0; nextTempTableSpace = 0;
} }

View File

@ -33,6 +33,7 @@
#endif #endif
#include <sys/stat.h> #include <sys/stat.h>
#include "common/pg_prng.h"
#include "lib/ilist.h" #include "lib/ilist.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "port/pg_bitutils.h" #include "port/pg_bitutils.h"
@ -180,7 +181,8 @@ dsm_postmaster_startup(PGShmemHeader *shim)
{ {
Assert(dsm_control_address == NULL); Assert(dsm_control_address == NULL);
Assert(dsm_control_mapped_size == 0); 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) if (dsm_control_handle == DSM_HANDLE_INVALID)
continue; continue;
if (dsm_impl_op(DSM_OP_CREATE, dsm_control_handle, segsize, if (dsm_impl_op(DSM_OP_CREATE, dsm_control_handle, segsize,
@ -536,7 +538,8 @@ dsm_create(Size size, int flags)
for (;;) for (;;)
{ {
Assert(seg->mapped_address == NULL && seg->mapped_size == 0); 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 */ if (seg->handle == DSM_HANDLE_INVALID) /* Reserve sentinel */
continue; continue;
if (dsm_impl_op(DSM_OP_CREATE, seg->handle, size, &seg->impl_private, 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 = 1;
handle |= slot << 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; return handle;
} }

View File

@ -30,9 +30,10 @@ ifdef TAS
TASPATH = $(top_builddir)/src/backend/port/tas.o TASPATH = $(top_builddir)/src/backend/port/tas.o
endif 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 \ $(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 # see notes in src/backend/parser/Makefile
lwlocknames.c: lwlocknames.h lwlocknames.c: lwlocknames.h

View File

@ -50,6 +50,7 @@
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#include "common/pg_prng.h"
#include "port/atomics.h" #include "port/atomics.h"
#include "storage/s_lock.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 */ /* increase delay by a random fraction between 1X and 2X */
status->cur_delay += (int) (status->cur_delay * 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 */ /* wrap back to minimum delay when max is exceeded */
if (status->cur_delay > MAX_DELAY_USEC) if (status->cur_delay > MAX_DELAY_USEC)
status->cur_delay = MIN_DELAY_USEC; status->cur_delay = MIN_DELAY_USEC;
@ -303,7 +304,7 @@ volatile struct test_lock_struct test_lock;
int int
main() main()
{ {
srandom((unsigned int) time(NULL)); pg_prng_seed(&pg_global_prng_state, (uint64) time(NULL));
test_lock.pad1 = test_lock.pad2 = 0x44; test_lock.pad1 = test_lock.pad2 = 0x44;

View File

@ -42,6 +42,7 @@
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "commands/async.h" #include "commands/async.h"
#include "commands/prepare.h" #include "commands/prepare.h"
#include "common/pg_prng.h"
#include "executor/spi.h" #include "executor/spi.h"
#include "jit/jit.h" #include "jit/jit.h"
#include "libpq/libpq.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 * Do not log if log_statement_sample_rate = 0. Log a sample if
* log_statement_sample_rate <= 1 and avoid unnecessary random() call * log_statement_sample_rate <= 1 and avoid unnecessary PRNG call if
* if log_statement_sample_rate = 1. * log_statement_sample_rate = 1.
*/ */
if (exceeded_sample_duration) if (exceeded_sample_duration)
in_sample = log_statement_sample_rate != 0 && in_sample = log_statement_sample_rate != 0 &&
(log_statement_sample_rate == 1 || (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) if (exceeded_duration || in_sample || log_duration || xact_is_sampled)
{ {

View File

@ -21,6 +21,7 @@
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "common/int.h" #include "common/int.h"
#include "common/pg_prng.h"
#include "common/shortest_dec.h" #include "common/shortest_dec.h"
#include "libpq/pqformat.h" #include "libpq/pqformat.h"
#include "miscadmin.h" #include "miscadmin.h"
@ -65,7 +66,7 @@ float8 degree_c_one = 1.0;
/* State for drandom() and setseed() */ /* State for drandom() and setseed() */
static bool drandom_seed_set = false; static bool drandom_seed_set = false;
static unsigned short drandom_seed[3] = {0, 0, 0}; static pg_prng_state drandom_seed;
/* Local function prototypes */ /* Local function prototypes */
static double sind_q1(double x); 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 * Should that fail for some reason, we fall back on a lower-quality
* seed based on current time and PID. * 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(); TimestampTz now = GetCurrentTimestamp();
uint64 iseed; uint64 iseed;
/* Mix the PID with the most predictable bits of the timestamp */ /* Mix the PID with the most predictable bits of the timestamp */
iseed = (uint64) now ^ ((uint64) MyProcPid << 32); iseed = (uint64) now ^ ((uint64) MyProcPid << 32);
drandom_seed[0] = (unsigned short) iseed; pg_prng_seed(&drandom_seed, iseed);
drandom_seed[1] = (unsigned short) (iseed >> 16);
drandom_seed[2] = (unsigned short) (iseed >> 32);
} }
drandom_seed_set = true; drandom_seed_set = true;
} }
/* pg_erand48 produces desired result range [0.0 - 1.0) */ /* pg_prng_double produces desired result range [0.0 - 1.0) */
result = pg_erand48(drandom_seed); result = pg_prng_double(&drandom_seed);
PG_RETURN_FLOAT8(result); PG_RETURN_FLOAT8(result);
} }
@ -2790,7 +2789,6 @@ Datum
setseed(PG_FUNCTION_ARGS) setseed(PG_FUNCTION_ARGS)
{ {
float8 seed = PG_GETARG_FLOAT8(0); float8 seed = PG_GETARG_FLOAT8(0);
uint64 iseed;
if (seed < -1 || seed > 1 || isnan(seed)) if (seed < -1 || seed > 1 || isnan(seed))
ereport(ERROR, ereport(ERROR,
@ -2798,11 +2796,7 @@ setseed(PG_FUNCTION_ARGS)
errmsg("setseed parameter %g is out of allowed range [-1,1]", errmsg("setseed parameter %g is out of allowed range [-1,1]",
seed))); seed)));
/* Use sign bit + 47 fractional bits to fill drandom_seed[] */ pg_prng_fseed(&drandom_seed, 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);
drandom_seed_set = true; drandom_seed_set = true;
PG_RETURN_VOID(); PG_RETURN_VOID();

View File

@ -37,7 +37,7 @@
*/ */
BlockNumber BlockNumber
BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize, BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize,
long randseed) uint32 randseed)
{ {
bs->N = nblocks; /* measured table size */ 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->t = 0; /* blocks scanned so far */
bs->m = 0; /* blocks selected 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); 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. * 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; p = 1.0 - (double) k / (double) K;
while (V < p) 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 * Reservoir sampling is not used anywhere where it would need to return
* repeatable results so we can initialize it randomly. * 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) */ /* 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 double
@ -154,7 +155,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
double V, double V,
quot; quot;
V = sampler_random_fract(rs->randstate); /* Generate V */ V = sampler_random_fract(&rs->randstate); /* Generate V */
S = 0; S = 0;
t += 1; t += 1;
/* Note: "num" in Vitter's code is always equal to t - n */ /* 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; tmp;
/* Generate U and X */ /* Generate U and X */
U = sampler_random_fract(rs->randstate); U = sampler_random_fract(&rs->randstate);
X = t * (W - 1.0); X = t * (W - 1.0);
S = floor(X); /* S is tentatively set to floor(X) */ S = floor(X); /* S is tentatively set to floor(X) */
/* Test if U <= h(S)/cg(X) in the manner of (6.3) */ /* 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; y *= numer / denom;
denom -= 1; 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) if (exp(log(y) / n) <= (t + X) / t)
break; break;
} }
@ -230,24 +231,22 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
*---------- *----------
*/ */
void 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 */ pg_prng_seed(randstate, (uint64) seed);
randstate[1] = (unsigned short) seed;
randstate[2] = (unsigned short) (seed >> 16);
} }
/* Select a random value R uniformly distributed in (0 - 1) */ /* Select a random value R uniformly distributed in (0 - 1) */
double double
sampler_random_fract(SamplerRandomState randstate) sampler_random_fract(pg_prng_state *randstate)
{ {
double res; 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 do
{ {
res = pg_erand48(randstate); res = pg_prng_double(randstate);
} while (res == 0.0); } while (unlikely(res == 0.0));
return res; return res;
} }
@ -261,27 +260,36 @@ sampler_random_fract(SamplerRandomState randstate)
* except that a common random state is used across all callers. * except that a common random state is used across all callers.
*/ */
static ReservoirStateData oldrs; static ReservoirStateData oldrs;
static bool oldrs_initialized = false;
double double
anl_random_fract(void) anl_random_fract(void)
{ {
/* initialize if first time through */ /* initialize if first time through */
if (oldrs.randstate[0] == 0) if (unlikely(!oldrs_initialized))
sampler_random_init_state(random(), oldrs.randstate); {
sampler_random_init_state(pg_prng_uint32(&pg_global_prng_state),
&oldrs.randstate);
oldrs_initialized = true;
}
/* and compute a random fraction */ /* and compute a random fraction */
return sampler_random_fract(oldrs.randstate); return sampler_random_fract(&oldrs.randstate);
} }
double double
anl_init_selection_state(int n) anl_init_selection_state(int n)
{ {
/* initialize if first time through */ /* initialize if first time through */
if (oldrs.randstate[0] == 0) if (unlikely(!oldrs_initialized))
sampler_random_init_state(random(), oldrs.randstate); {
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) */ /* 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 double

View File

@ -66,6 +66,7 @@
#include "common/file_perm.h" #include "common/file_perm.h"
#include "common/file_utils.h" #include "common/file_utils.h"
#include "common/logging.h" #include "common/logging.h"
#include "common/pg_prng.h"
#include "common/restricted_token.h" #include "common/restricted_token.h"
#include "common/string.h" #include "common/string.h"
#include "common/username.h" #include "common/username.h"
@ -880,9 +881,10 @@ choose_dsm_implementation(void)
{ {
#ifdef HAVE_SHM_OPEN #ifdef HAVE_SHM_OPEN
int ntries = 10; int ntries = 10;
pg_prng_state prng_state;
/* Initialize random(); this function is its only user in this program. */ /* Initialize prng; this function is its only user in this program. */
srandom((unsigned int) (getpid() ^ time(NULL))); pg_prng_seed(&prng_state, (uint64) (getpid() ^ time(NULL)));
while (ntries > 0) while (ntries > 0)
{ {
@ -890,7 +892,7 @@ choose_dsm_implementation(void)
char name[64]; char name[64];
int fd; int fd;
handle = random(); handle = pg_prng_uint32(&prng_state);
snprintf(name, 64, "/PostgreSQL.%u", handle); snprintf(name, 64, "/PostgreSQL.%u", handle);
if ((fd = shm_open(name, O_CREAT | O_RDWR | O_EXCL, 0600)) != -1) if ((fd = shm_open(name, O_CREAT | O_RDWR | O_EXCL, 0600)) != -1)
{ {

View File

@ -15,6 +15,7 @@
#include "access/xlogdefs.h" #include "access/xlogdefs.h"
#include "common/logging.h" #include "common/logging.h"
#include "common/pg_prng.h"
#include "getopt_long.h" #include "getopt_long.h"
/* /*
@ -117,6 +118,8 @@ main(int argc, char *argv[])
pqsignal(SIGHUP, signal_cleanup); pqsignal(SIGHUP, signal_cleanup);
#endif #endif
pg_prng_seed(&pg_global_prng_state, (uint64) time(NULL));
prepare_buf(); prepare_buf();
test_open(); test_open();
@ -233,7 +236,7 @@ prepare_buf(void)
/* write random data into buffer */ /* write random data into buffer */
for (ops = 0; ops < DEFAULT_XLOG_SEG_SIZE; ops++) for (ops = 0; ops < DEFAULT_XLOG_SEG_SIZE; ops++)
full_buf[ops] = random(); full_buf[ops] = (char) pg_prng_int32(&pg_global_prng_state);
buf = (char *) TYPEALIGN(XLOG_BLCKSZ, full_buf); buf = (char *) TYPEALIGN(XLOG_BLCKSZ, full_buf);
} }

View File

@ -59,6 +59,7 @@
#include "common/int.h" #include "common/int.h"
#include "common/logging.h" #include "common/logging.h"
#include "common/pg_prng.h"
#include "common/string.h" #include "common/string.h"
#include "common/username.h" #include "common/username.h"
#include "fe_utils/cancel.h" #include "fe_utils/cancel.h"
@ -350,16 +351,8 @@ typedef struct StatsData
*/ */
pg_time_usec_t epoch_shift; pg_time_usec_t epoch_shift;
/*
* Struct to keep random state.
*/
typedef struct RandomState
{
unsigned short xseed[3];
} RandomState;
/* Various random sequences are initialized from this one. */ /* Various random sequences are initialized from this one. */
static RandomState base_random_sequence; static pg_prng_state base_random_sequence;
/* Synchronization barrier for start and connection */ /* Synchronization barrier for start and connection */
static THREAD_BARRIER_T barrier; static THREAD_BARRIER_T barrier;
@ -461,7 +454,7 @@ typedef struct
* Separate randomness for each client. This is used for random functions * Separate randomness for each client. This is used for random functions
* PGBENCH_RANDOM_* during the execution of the script. * PGBENCH_RANDOM_* during the execution of the script.
*/ */
RandomState cs_func_rs; pg_prng_state cs_func_rs;
int use_file; /* index in sql_script for this client */ int use_file; /* index in sql_script for this client */
int command; /* command number in script */ int command; /* command number in script */
@ -498,9 +491,9 @@ typedef struct
* random state to make all of them independent of each other and * random state to make all of them independent of each other and
* therefore deterministic at the thread level. * therefore deterministic at the thread level.
*/ */
RandomState ts_choose_rs; /* random state for selecting a script */ pg_prng_state ts_choose_rs; /* random state for selecting a script */
RandomState ts_throttle_rs; /* random state for transaction throttling */ pg_prng_state ts_throttle_rs; /* random state for transaction throttling */
RandomState ts_sample_rs; /* random state for log sampling */ pg_prng_state ts_sample_rs; /* random state for log sampling */
int64 throttle_trigger; /* previous/next throttling (us) */ int64 throttle_trigger; /* previous/next throttling (us) */
FILE *logfile; /* where to log, or NULL */ FILE *logfile; /* where to log, or NULL */
@ -898,42 +891,28 @@ strtodouble(const char *str, bool errorOK, double *dv)
} }
/* /*
* Initialize a random state struct. * Initialize a prng state struct.
* *
* We derive the seed from base_random_sequence, which must be set up already. * We derive the seed from base_random_sequence, which must be set up already.
*/ */
static void static void
initRandomState(RandomState *random_state) initRandomState(pg_prng_state *state)
{ {
random_state->xseed[0] = (unsigned short) pg_prng_seed(state, pg_prng_uint64(&base_random_sequence));
(pg_jrand48(base_random_sequence.xseed) & 0xFFFF);
random_state->xseed[1] = (unsigned short)
(pg_jrand48(base_random_sequence.xseed) & 0xFFFF);
random_state->xseed[2] = (unsigned short)
(pg_jrand48(base_random_sequence.xseed) & 0xFFFF);
} }
/* /*
* Random number generator: uniform distribution from min to max inclusive. * random number generator: uniform distribution from min to max inclusive.
* *
* Although the limits are expressed as int64, you can't generate the full * 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 * int64 range in one call, because the difference of the limits mustn't
* overflow int64. In practice it's unwise to ask for more than an int32 * overflow int64. This is not checked.
* range, because of the limited precision of pg_erand48().
*/ */
static int64 static int64
getrand(RandomState *random_state, int64 min, int64 max) getrand(pg_prng_state *state, int64 min, int64 max)
{ {
/* return min + (int64) pg_prng_uint64_range(state, 0, max - min);
* Odd coding is so that min and max have approximately the same chance of
* being selected as do numbers between them.
*
* pg_erand48() is thread-safe and concurrent, which is why we use it
* rather than random(), which in glibc is non-reentrant, and therefore
* protected by a mutex, and therefore a bottleneck on machines with many
* CPUs.
*/
return min + (int64) ((max - min + 1) * pg_erand48(random_state->xseed));
} }
/* /*
@ -942,7 +921,7 @@ getrand(RandomState *random_state, int64 min, int64 max)
* value is exp(-parameter). * value is exp(-parameter).
*/ */
static int64 static int64
getExponentialRand(RandomState *random_state, int64 min, int64 max, getExponentialRand(pg_prng_state *state, int64 min, int64 max,
double parameter) double parameter)
{ {
double cut, double cut,
@ -952,8 +931,8 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
/* abort if wrong parameter, but must really be checked beforehand */ /* abort if wrong parameter, but must really be checked beforehand */
Assert(parameter > 0.0); Assert(parameter > 0.0);
cut = exp(-parameter); cut = exp(-parameter);
/* erand in [0, 1), uniform in (0, 1] */ /* pg_prng_double value in [0, 1), uniform in (0, 1] */
uniform = 1.0 - pg_erand48(random_state->xseed); uniform = 1.0 - pg_prng_double(state);
/* /*
* inner expression in (cut, 1] (if parameter > 0), rand in [0, 1) * inner expression in (cut, 1] (if parameter > 0), rand in [0, 1)
@ -966,7 +945,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
/* random number generator: gaussian distribution from min to max inclusive */ /* random number generator: gaussian distribution from min to max inclusive */
static int64 static int64
getGaussianRand(RandomState *random_state, int64 min, int64 max, getGaussianRand(pg_prng_state *state, int64 min, int64 max,
double parameter) double parameter)
{ {
double stdev; double stdev;
@ -990,13 +969,13 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
do do
{ {
/* /*
* pg_erand48 generates [0,1), but for the basic version of the * pg_prng_double generates [0, 1), but for the basic version of the
* Box-Muller transform the two uniformly distributed random numbers * Box-Muller transform the two uniformly distributed random numbers
* are expected in (0, 1] (see * are expected to be in (0, 1] (see
* https://en.wikipedia.org/wiki/Box-Muller_transform) * https://en.wikipedia.org/wiki/Box-Muller_transform)
*/ */
double rand1 = 1.0 - pg_erand48(random_state->xseed); double rand1 = 1.0 - pg_prng_double(state);
double rand2 = 1.0 - pg_erand48(random_state->xseed); double rand2 = 1.0 - pg_prng_double(state);
/* Box-Muller basic form transform */ /* Box-Muller basic form transform */
double var_sqrt = sqrt(-2.0 * log(rand1)); double var_sqrt = sqrt(-2.0 * log(rand1));
@ -1026,7 +1005,7 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
* not be one. * not be one.
*/ */
static int64 static int64
getPoissonRand(RandomState *random_state, double center) getPoissonRand(pg_prng_state *state, double center)
{ {
/* /*
* Use inverse transform sampling to generate a value > 0, such that the * Use inverse transform sampling to generate a value > 0, such that the
@ -1034,8 +1013,8 @@ getPoissonRand(RandomState *random_state, double center)
*/ */
double uniform; double uniform;
/* erand in [0, 1), uniform in (0, 1] */ /* pg_prng_double value in [0, 1), uniform in (0, 1] */
uniform = 1.0 - pg_erand48(random_state->xseed); uniform = 1.0 - pg_prng_double(state);
return (int64) (-log(uniform) * center + 0.5); return (int64) (-log(uniform) * center + 0.5);
} }
@ -1048,7 +1027,7 @@ getPoissonRand(RandomState *random_state, double center)
* This works for s > 1.0, but may perform badly for s very close to 1.0. * This works for s > 1.0, but may perform badly for s very close to 1.0.
*/ */
static int64 static int64
computeIterativeZipfian(RandomState *random_state, int64 n, double s) computeIterativeZipfian(pg_prng_state *state, int64 n, double s)
{ {
double b = pow(2.0, s - 1.0); double b = pow(2.0, s - 1.0);
double x, double x,
@ -1063,8 +1042,8 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
while (true) while (true)
{ {
/* random variates */ /* random variates */
u = pg_erand48(random_state->xseed); u = pg_prng_double(state);
v = pg_erand48(random_state->xseed); v = pg_prng_double(state);
x = floor(pow(u, -1.0 / (s - 1.0))); x = floor(pow(u, -1.0 / (s - 1.0)));
@ -1078,14 +1057,14 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
/* random number generator: zipfian distribution from min to max inclusive */ /* random number generator: zipfian distribution from min to max inclusive */
static int64 static int64
getZipfianRand(RandomState *random_state, int64 min, int64 max, double s) getZipfianRand(pg_prng_state *state, int64 min, int64 max, double s)
{ {
int64 n = max - min + 1; int64 n = max - min + 1;
/* abort if parameter is invalid */ /* abort if parameter is invalid */
Assert(MIN_ZIPFIAN_PARAM <= s && s <= MAX_ZIPFIAN_PARAM); Assert(MIN_ZIPFIAN_PARAM <= s && s <= MAX_ZIPFIAN_PARAM);
return min - 1 + computeIterativeZipfian(random_state, n, s); return min - 1 + computeIterativeZipfian(state, n, s);
} }
/* /*
@ -1142,7 +1121,7 @@ getHashMurmur2(int64 val, uint64 seed)
* For small sizes, this generates each of the (size!) possible permutations * For small sizes, this generates each of the (size!) possible permutations
* of integers in the range [0, size) with roughly equal probability. Once * 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 * the size is larger than 20, the number of possible permutations exceeds the
* number of distinct states of the internal pseudorandom number generators, * number of distinct states of the internal pseudorandom number generator,
* and so not all possible permutations can be generated, but the permutations * and so not all possible permutations can be generated, but the permutations
* chosen should continue to give the appearance of being random. * chosen should continue to give the appearance of being random.
* *
@ -1152,8 +1131,8 @@ getHashMurmur2(int64 val, uint64 seed)
static int64 static int64
permute(const int64 val, const int64 isize, const int64 seed) permute(const int64 val, const int64 isize, const int64 seed)
{ {
RandomState random_state1; /* using a high-end PRNG is probably overkill */
RandomState random_state2; pg_prng_state state;
uint64 size; uint64 size;
uint64 v; uint64 v;
int masklen; int masklen;
@ -1163,14 +1142,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
if (isize < 2) if (isize < 2)
return 0; /* nothing to permute */ return 0; /* nothing to permute */
/* Initialize a pair of random states using the seed */ /* Initialize prng state using the seed */
random_state1.xseed[0] = seed & 0xFFFF; pg_prng_seed(&state, (uint64) seed);
random_state1.xseed[1] = (seed >> 16) & 0xFFFF;
random_state1.xseed[2] = (seed >> 32) & 0xFFFF;
random_state2.xseed[0] = (((uint64) seed) >> 48) & 0xFFFF;
random_state2.xseed[1] = seed & 0xFFFF;
random_state2.xseed[2] = (seed >> 16) & 0xFFFF;
/* Computations are performed on unsigned values */ /* Computations are performed on unsigned values */
size = (uint64) isize; size = (uint64) isize;
@ -1216,8 +1189,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
t; t;
/* Random multiply (by an odd number), XOR and rotate of lower half */ /* Random multiply (by an odd number), XOR and rotate of lower half */
m = (uint64) getrand(&random_state1, 0, mask) | 1; m = (pg_prng_uint64(&state) & mask) | 1;
r = (uint64) getrand(&random_state2, 0, mask); r = pg_prng_uint64(&state) & mask;
if (v <= mask) if (v <= mask)
{ {
v = ((v * m) ^ r) & mask; v = ((v * m) ^ r) & mask;
@ -1225,8 +1198,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
} }
/* Random multiply (by an odd number), XOR and rotate of upper half */ /* Random multiply (by an odd number), XOR and rotate of upper half */
m = (uint64) getrand(&random_state1, 0, mask) | 1; m = (pg_prng_uint64(&state) & mask) | 1;
r = (uint64) getrand(&random_state2, 0, mask); r = pg_prng_uint64(&state) & mask;
t = size - 1 - v; t = size - 1 - v;
if (t <= mask) if (t <= mask)
{ {
@ -1236,7 +1209,7 @@ permute(const int64 val, const int64 isize, const int64 seed)
} }
/* Random offset */ /* Random offset */
r = (uint64) getrand(&random_state2, 0, size - 1); r = pg_prng_uint64_range(&state, 0, size - 1);
v = (v + r) % size; v = (v + r) % size;
} }
@ -3831,7 +3804,7 @@ doLog(TState *thread, CState *st,
* to the random sample. * to the random sample.
*/ */
if (sample_rate != 0.0 && if (sample_rate != 0.0 &&
pg_erand48(thread->ts_sample_rs.xseed) > sample_rate) pg_prng_double(&thread->ts_sample_rs) > sample_rate)
return; return;
/* should we aggregate the results or not? */ /* should we aggregate the results or not? */
@ -5770,12 +5743,11 @@ set_random_seed(const char *seed)
if (seed != NULL) if (seed != NULL)
pg_log_info("setting random seed to %llu", (unsigned long long) iseed); pg_log_info("setting random seed to %llu", (unsigned long long) iseed);
random_seed = iseed; random_seed = iseed;
/* Fill base_random_sequence with low-order bits of seed */ /* Initialize base_random_sequence using seed */
base_random_sequence.xseed[0] = iseed & 0xFFFF; pg_prng_seed(&base_random_sequence, (uint64) iseed);
base_random_sequence.xseed[1] = (iseed >> 16) & 0xFFFF;
base_random_sequence.xseed[2] = (iseed >> 32) & 0xFFFF;
return true; return true;
} }
@ -6449,9 +6421,7 @@ main(int argc, char **argv)
/* set default seed for hash functions */ /* set default seed for hash functions */
if (lookupVariable(&state[0], "default_seed") == NULL) if (lookupVariable(&state[0], "default_seed") == NULL)
{ {
uint64 seed = uint64 seed = pg_prng_uint64(&base_random_sequence);
((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) |
(((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) << 32);
for (i = 0; i < nclients; i++) for (i = 0; i < nclients; i++)
if (!putVariableInt(&state[i], "startup", "default_seed", (int64) seed)) if (!putVariableInt(&state[i], "startup", "default_seed", (int64) seed))

View File

@ -369,7 +369,6 @@ $node->append_conf('postgresql.conf',
$node->reload; $node->reload;
# test expressions # test expressions
# command 1..3 and 23 depend on random seed which is used to call srandom.
$node->pgbench( $node->pgbench(
'--random-seed=5432 -t 1 -Dfoo=-10.1 -Dbla=false -Di=+3 -Dn=null -Dt=t -Df=of -Dd=1.0', '--random-seed=5432 -t 1 -Dfoo=-10.1 -Dbla=false -Di=+3 -Dn=null -Dt=t -Df=of -Dd=1.0',
0, 0,
@ -378,9 +377,9 @@ $node->pgbench(
qr{setting random seed to 5432\b}, qr{setting random seed to 5432\b},
# After explicit seeding, the four random checks (1-3,20) are # After explicit seeding, the four random checks (1-3,20) are
# deterministic # deterministic; but see also magic values in checks 111,113.
qr{command=1.: int 13\b}, # uniform random qr{command=1.: int 17\b}, # uniform random
qr{command=2.: int 116\b}, # exponential random qr{command=2.: int 104\b}, # exponential random
qr{command=3.: int 1498\b}, # gaussian random qr{command=3.: int 1498\b}, # gaussian random
qr{command=4.: int 4\b}, qr{command=4.: int 4\b},
qr{command=5.: int 5\b}, qr{command=5.: int 5\b},
@ -394,7 +393,7 @@ $node->pgbench(
qr{command=15.: double 15\b}, qr{command=15.: double 15\b},
qr{command=16.: double 16\b}, qr{command=16.: double 16\b},
qr{command=17.: double 17\b}, qr{command=17.: double 17\b},
qr{command=20.: int 1\b}, # zipfian random qr{command=20.: int 3\b}, # zipfian random
qr{command=21.: double -27\b}, qr{command=21.: double -27\b},
qr{command=22.: double 1024\b}, qr{command=22.: double 1024\b},
qr{command=23.: double 1\b}, qr{command=23.: double 1\b},
@ -448,6 +447,7 @@ $node->pgbench(
qr{command=109.: boolean true\b}, qr{command=109.: boolean true\b},
qr{command=110.: boolean true\b}, qr{command=110.: boolean true\b},
qr{command=111.: boolean true\b}, qr{command=111.: boolean true\b},
qr{command=113.: boolean true\b},
], ],
'pgbench expressions', 'pgbench expressions',
{ {
@ -591,8 +591,17 @@ SELECT :v0, :v1, :v2, :v3;
\set t debug(0 <= :p and :p < :size and :p = permute(:v + :size, :size) and :p <> permute(:v + 1, :size)) \set t debug(0 <= :p and :p < :size and :p = permute(:v + :size, :size) and :p <> permute(:v + 1, :size))
-- actual values -- actual values
\set t debug(permute(:v, 1) = 0) \set t debug(permute(:v, 1) = 0)
\set t debug(permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1 and \ \set t debug(permute(0, 2, 5431) = 0 and permute(1, 2, 5431) = 1 and \
permute(0, 2, 5435) = 1 and permute(1, 2, 5435) = 0) permute(0, 2, 5433) = 1 and permute(1, 2, 5433) = 0)
-- check permute's portability across architectures
\set size debug(:max - 10)
\set t debug(permute(:size-1, :size, 5432) = 520382784483822430 and \
permute(:size-2, :size, 5432) = 1143715004660802862 and \
permute(:size-3, :size, 5432) = 447293596416496998 and \
permute(:size-4, :size, 5432) = 916527772266572956 and \
permute(:size-5, :size, 5432) = 2763809008686028849 and \
permute(:size-6, :size, 5432) = 8648551549198294572 and \
permute(:size-7, :size, 5432) = 4542876852200565125)
} }
}); });

View File

@ -66,6 +66,7 @@ OBJS_COMMON = \
md5_common.o \ md5_common.o \
pg_get_line.o \ pg_get_line.o \
pg_lzcompress.o \ pg_lzcompress.o \
pg_prng.o \
pgfnames.o \ pgfnames.o \
psprintf.o \ psprintf.o \
relpath.o \ relpath.o \

247
src/common/pg_prng.c Normal file
View File

@ -0,0 +1,247 @@
/*-------------------------------------------------------------------------
*
* Pseudo-Random Number Generator
*
* We use Blackman and Vigna's xoroshiro128** 1.0 algorithm
* to have a small, fast PRNG suitable for generating reasonably
* good-quality 64-bit data. This should not be considered
* cryptographically strong, however.
*
* About these generators: https://prng.di.unimi.it/
* See also https://en.wikipedia.org/wiki/List_of_random_number_generators
*
* Copyright (c) 2021, PostgreSQL Global Development Group
*
* src/common/pg_prng.c
*
*-------------------------------------------------------------------------
*/
#include "c.h"
#include <math.h> /* for ldexp() */
#include "common/pg_prng.h"
#include "port/pg_bitutils.h"
/* process-wide state vector */
pg_prng_state pg_global_prng_state;
/*
* 64-bit rotate left
*/
static inline uint64
rotl(uint64 x, int bits)
{
return (x << bits) | (x >> (64 - bits));
}
/*
* The basic xoroshiro128** algorithm.
* Generates and returns a 64-bit uniformly distributed number,
* updating the state vector for next time.
*
* Note: the state vector must not be all-zeroes, as that is a fixed point.
*/
static uint64
xoroshiro128ss(pg_prng_state *state)
{
uint64 s0 = state->s0,
sx = state->s1 ^ s0,
val = rotl(s0 * 5, 7) * 9;
/* update state */
state->s0 = rotl(s0, 24) ^ sx ^ (sx << 16);
state->s1 = rotl(sx, 37);
return val;
}
/*
* We use this generator just to fill the xoroshiro128** state vector
* from a 64-bit seed.
*/
static uint64
splitmix64(uint64 *state)
{
/* state update */
uint64 val = (*state += UINT64CONST(0x9E3779B97f4A7C15));
/* value extraction */
val = (val ^ (val >> 30)) * UINT64CONST(0xBF58476D1CE4E5B9);
val = (val ^ (val >> 27)) * UINT64CONST(0x94D049BB133111EB);
return val ^ (val >> 31);
}
/*
* Initialize the PRNG state from a 64-bit integer,
* taking care that we don't produce all-zeroes.
*/
void
pg_prng_seed(pg_prng_state *state, uint64 seed)
{
state->s0 = splitmix64(&seed);
state->s1 = splitmix64(&seed);
/* Let's just make sure we didn't get all-zeroes */
(void) pg_prng_seed_check(state);
}
/*
* Initialize the PRNG state from a double in the range [-1.0, 1.0],
* taking care that we don't produce all-zeroes.
*/
void
pg_prng_fseed(pg_prng_state *state, double fseed)
{
/* Assume there's about 52 mantissa bits; the sign contributes too. */
int64 seed = ((double) ((UINT64CONST(1) << 52) - 1)) * fseed;
pg_prng_seed(state, (uint64) seed);
}
/*
* Validate a PRNG seed value.
*/
bool
pg_prng_seed_check(pg_prng_state *state)
{
/*
* If the seeding mechanism chanced to produce all-zeroes, insert
* something nonzero. Anything would do; use Knuth's LCG parameters.
*/
if (unlikely(state->s0 == 0 && state->s1 == 0))
{
state->s0 = UINT64CONST(0x5851F42D4C957F2D);
state->s1 = UINT64CONST(0x14057B7EF767814F);
}
/* As a convenience for the pg_prng_strong_seed macro, return true */
return true;
}
/*
* Select a random uint64 uniformly from the range [0, PG_UINT64_MAX].
*/
uint64
pg_prng_uint64(pg_prng_state *state)
{
return xoroshiro128ss(state);
}
/*
* Select a random uint64 uniformly from the range [rmin, rmax].
* If the range is empty, rmin is always produced.
*/
uint64
pg_prng_uint64_range(pg_prng_state *state, uint64 rmin, uint64 rmax)
{
uint64 val;
if (likely(rmax > rmin))
{
/*
* Use bitmask rejection method to generate an offset in 0..range.
* Each generated val is less than twice "range", so on average we
* should not have to iterate more than twice.
*/
uint64 range = rmax - rmin;
uint32 rshift = 63 - pg_leftmost_one_pos64(range);
do
{
val = xoroshiro128ss(state) >> rshift;
} while (val > range);
}
else
val = 0;
return rmin + val;
}
/*
* Select a random int64 uniformly from the range [PG_INT64_MIN, PG_INT64_MAX].
*/
int64
pg_prng_int64(pg_prng_state *state)
{
return (int64) xoroshiro128ss(state);
}
/*
* Select a random int64 uniformly from the range [0, PG_INT64_MAX].
*/
int64
pg_prng_int64p(pg_prng_state *state)
{
return (int64) (xoroshiro128ss(state) & UINT64CONST(0x7FFFFFFFFFFFFFFF));
}
/*
* Select a random uint32 uniformly from the range [0, PG_UINT32_MAX].
*/
uint32
pg_prng_uint32(pg_prng_state *state)
{
/*
* Although xoroshiro128** is not known to have any weaknesses in
* randomness of low-order bits, we prefer to use the upper bits of its
* result here and below.
*/
uint64 v = xoroshiro128ss(state);
return (uint32) (v >> 32);
}
/*
* Select a random int32 uniformly from the range [PG_INT32_MIN, PG_INT32_MAX].
*/
int32
pg_prng_int32(pg_prng_state *state)
{
uint64 v = xoroshiro128ss(state);
return (int32) (v >> 32);
}
/*
* Select a random int32 uniformly from the range [0, PG_INT32_MAX].
*/
int32
pg_prng_int32p(pg_prng_state *state)
{
uint64 v = xoroshiro128ss(state);
return (int32) (v >> 33);
}
/*
* Select a random double uniformly from the range [0.0, 1.0).
*
* Note: if you want a result in the range (0.0, 1.0], the standard way
* to get that is "1.0 - pg_prng_double(state)".
*/
double
pg_prng_double(pg_prng_state *state)
{
uint64 v = xoroshiro128ss(state);
/*
* As above, assume there's 52 mantissa bits in a double. This result
* could round to 1.0 if double's precision is less than that; but we
* assume IEEE float arithmetic elsewhere in Postgres, so this seems OK.
*/
return ldexp((double) (v >> (64 - 52)), -52);
}
/*
* Select a random boolean value.
*/
bool
pg_prng_bool(pg_prng_state *state)
{
uint64 v = xoroshiro128ss(state);
return (bool) (v >> 63);
}

View File

@ -0,0 +1,60 @@
/*-------------------------------------------------------------------------
*
* Pseudo-Random Number Generator
*
* Copyright (c) 2021, PostgreSQL Global Development Group
*
* src/include/common/pg_prng.h
*
*-------------------------------------------------------------------------
*/
#ifndef PG_PRNG_H
#define PG_PRNG_H
/*
* State vector for PRNG generation. Callers should treat this as an
* opaque typedef, but we expose its definition to allow it to be
* embedded in other structs.
*/
typedef struct pg_prng_state
{
uint64 s0,
s1;
} pg_prng_state;
/*
* Callers not needing local PRNG series may use this global state vector,
* after initializing it with one of the pg_prng_...seed functions.
*/
extern PGDLLIMPORT pg_prng_state pg_global_prng_state;
extern void pg_prng_seed(pg_prng_state *state, uint64 seed);
extern void pg_prng_fseed(pg_prng_state *state, double fseed);
extern bool pg_prng_seed_check(pg_prng_state *state);
/*
* Initialize the PRNG state from the pg_strong_random source,
* taking care that we don't produce all-zeroes. If this returns false,
* caller should initialize the PRNG state from some other random seed,
* using pg_prng_[f]seed.
*
* We implement this as a macro, so that the pg_strong_random() call is
* in the caller. If it were in pg_prng.c, programs using pg_prng.c
* but not needing strong seeding would nonetheless be forced to pull in
* pg_strong_random.c and thence OpenSSL.
*/
#define pg_prng_strong_seed(state) \
(pg_strong_random((void *) (state), sizeof(pg_prng_state)) ? \
pg_prng_seed_check(state) : false)
extern uint64 pg_prng_uint64(pg_prng_state *state);
extern uint64 pg_prng_uint64_range(pg_prng_state *state, uint64 rmin, uint64 rmax);
extern int64 pg_prng_int64(pg_prng_state *state);
extern int64 pg_prng_int64p(pg_prng_state *state);
extern uint32 pg_prng_uint32(pg_prng_state *state);
extern int32 pg_prng_int32(pg_prng_state *state);
extern int32 pg_prng_int32p(pg_prng_state *state);
extern double pg_prng_double(pg_prng_state *state);
extern bool pg_prng_bool(pg_prng_state *state);
#endif /* PG_PRNG_H */

View File

@ -22,6 +22,7 @@
#ifndef GEQO_H #ifndef GEQO_H
#define GEQO_H #define GEQO_H
#include "common/pg_prng.h"
#include "nodes/pathnodes.h" #include "nodes/pathnodes.h"
#include "optimizer/geqo_gene.h" #include "optimizer/geqo_gene.h"
@ -73,7 +74,7 @@ extern double Geqo_seed; /* 0 .. 1 */
typedef struct typedef struct
{ {
List *initial_rels; /* the base relations we are joining */ List *initial_rels; /* the base relations we are joining */
unsigned short random_state[3]; /* state for pg_erand48() */ pg_prng_state random_state; /* PRNG state */
} GeqoPrivateData; } GeqoPrivateData;

View File

@ -31,11 +31,10 @@
extern void geqo_set_seed(PlannerInfo *root, double seed); extern void geqo_set_seed(PlannerInfo *root, double seed);
/* geqo_rand returns a random float value between 0 and 1 inclusive */ /* geqo_rand returns a random float value in the range [0.0, 1.0) */
extern double geqo_rand(PlannerInfo *root); extern double geqo_rand(PlannerInfo *root);
/* geqo_randint returns integer value between lower and upper inclusive */ /* geqo_randint returns integer value between lower and upper inclusive */
#define geqo_randint(root, upper, lower) \ extern int geqo_randint(PlannerInfo *root, int upper, int lower);
( (int) floor( geqo_rand(root)*(((upper)-(lower))+0.999999) ) + (lower) )
#endif /* GEQO_RANDOM_H */ #endif /* GEQO_RANDOM_H */

View File

@ -445,9 +445,6 @@
/* Define to 1 if you have the `pwrite' function. */ /* Define to 1 if you have the `pwrite' function. */
#undef HAVE_PWRITE #undef HAVE_PWRITE
/* Define to 1 if you have the `random' function. */
#undef HAVE_RANDOM
/* Define to 1 if you have the <readline.h> header file. */ /* Define to 1 if you have the <readline.h> header file. */
#undef HAVE_READLINE_H #undef HAVE_READLINE_H
@ -512,9 +509,6 @@
/* Define to 1 if you have spinlocks. */ /* Define to 1 if you have spinlocks. */
#undef HAVE_SPINLOCKS #undef HAVE_SPINLOCKS
/* Define to 1 if you have the `srandom' function. */
#undef HAVE_SRANDOM
/* Define to 1 if stdbool.h conforms to C99. */ /* Define to 1 if stdbool.h conforms to C99. */
#undef HAVE_STDBOOL_H #undef HAVE_STDBOOL_H

View File

@ -234,17 +234,6 @@
*/ */
#define DEFAULT_EVENT_SOURCE "PostgreSQL" #define DEFAULT_EVENT_SOURCE "PostgreSQL"
/*
* The random() function is expected to yield values between 0 and
* MAX_RANDOM_VALUE. Currently, all known implementations yield
* 0..2^31-1, so we just hardwire this constant. We could do a
* configure test if it proves to be necessary. CAUTION: Think not to
* replace this with RAND_MAX. RAND_MAX defines the maximum value of
* the older rand() function, which is often different from --- and
* considerably inferior to --- random().
*/
#define MAX_RANDOM_VALUE PG_INT32_MAX
/* /*
* On PPC machines, decide whether to use the mutex hint bit in LWARX * On PPC machines, decide whether to use the mutex hint bit in LWARX
* instructions. Setting the hint bit will slightly improve spinlock * instructions. Setting the hint bit will slightly improve spinlock

View File

@ -362,11 +362,6 @@ extern int gettimeofday(struct timeval *tp, struct timezone *tzp);
#define pgoff_t off_t #define pgoff_t off_t
#endif #endif
extern double pg_erand48(unsigned short xseed[3]);
extern long pg_lrand48(void);
extern long pg_jrand48(unsigned short xseed[3]);
extern void pg_srand48(long seed);
#ifndef HAVE_FLS #ifndef HAVE_FLS
extern int fls(int mask); extern int fls(int mask);
#endif #endif
@ -452,10 +447,6 @@ extern size_t strlcpy(char *dst, const char *src, size_t siz);
extern size_t strnlen(const char *str, size_t maxlen); extern size_t strnlen(const char *str, size_t maxlen);
#endif #endif
#if !defined(HAVE_RANDOM)
extern long random(void);
#endif
#ifndef HAVE_SETENV #ifndef HAVE_SETENV
extern int setenv(const char *name, const char *value, int overwrite); extern int setenv(const char *name, const char *value, int overwrite);
#endif #endif
@ -464,10 +455,6 @@ extern int setenv(const char *name, const char *value, int overwrite);
extern int unsetenv(const char *name); extern int unsetenv(const char *name);
#endif #endif
#ifndef HAVE_SRANDOM
extern void srandom(unsigned int seed);
#endif
#ifndef HAVE_DLOPEN #ifndef HAVE_DLOPEN
extern void *dlopen(const char *file, int mode); extern void *dlopen(const char *file, int mode);
extern void *dlsym(void *handle, const char *symbol); extern void *dlsym(void *handle, const char *symbol);

View File

@ -13,15 +13,14 @@
#ifndef SAMPLING_H #ifndef SAMPLING_H
#define SAMPLING_H #define SAMPLING_H
#include "common/pg_prng.h"
#include "storage/block.h" /* for typedef BlockNumber */ #include "storage/block.h" /* for typedef BlockNumber */
/* Random generator for sampling code */ /* Random generator for sampling code */
typedef unsigned short SamplerRandomState[3]; extern void sampler_random_init_state(uint32 seed,
pg_prng_state *randstate);
extern void sampler_random_init_state(long seed, extern double sampler_random_fract(pg_prng_state *randstate);
SamplerRandomState randstate);
extern double sampler_random_fract(SamplerRandomState randstate);
/* Block sampling methods */ /* Block sampling methods */
@ -32,13 +31,13 @@ typedef struct
int n; /* desired sample size */ int n; /* desired sample size */
BlockNumber t; /* current block number */ BlockNumber t; /* current block number */
int m; /* blocks selected so far */ int m; /* blocks selected so far */
SamplerRandomState randstate; /* random generator state */ pg_prng_state randstate; /* random generator state */
} BlockSamplerData; } BlockSamplerData;
typedef BlockSamplerData *BlockSampler; typedef BlockSamplerData *BlockSampler;
extern BlockNumber BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, extern BlockNumber BlockSampler_Init(BlockSampler bs, BlockNumber nblocks,
int samplesize, long randseed); int samplesize, uint32 randseed);
extern bool BlockSampler_HasMore(BlockSampler bs); extern bool BlockSampler_HasMore(BlockSampler bs);
extern BlockNumber BlockSampler_Next(BlockSampler bs); extern BlockNumber BlockSampler_Next(BlockSampler bs);
@ -47,7 +46,7 @@ extern BlockNumber BlockSampler_Next(BlockSampler bs);
typedef struct typedef struct
{ {
double W; double W;
SamplerRandomState randstate; /* random generator state */ pg_prng_state randstate; /* random generator state */
} ReservoirStateData; } ReservoirStateData;
typedef ReservoirStateData *ReservoirState; typedef ReservoirStateData *ReservoirState;

View File

@ -42,7 +42,6 @@ OBJS = \
$(PG_CRC32C_OBJS) \ $(PG_CRC32C_OBJS) \
bsearch_arg.o \ bsearch_arg.o \
chklocale.o \ chklocale.o \
erand48.o \
inet_net_ntop.o \ inet_net_ntop.o \
noblock.o \ noblock.o \
path.o \ path.o \

View File

@ -1,136 +0,0 @@
/*-------------------------------------------------------------------------
*
* erand48.c
*
* This file supplies pg_erand48() and related functions, which except
* for the names are just like the POSIX-standard erand48() family.
* (We don't supply the full set though, only the ones we have found use
* for in Postgres. In particular, we do *not* implement lcong48(), so
* that there is no need for the multiplier and addend to be variable.)
*
* We used to test for an operating system version rather than
* unconditionally using our own, but (1) some versions of Cygwin have a
* buggy erand48() that always returns zero and (2) as of 2011, glibc's
* erand48() is strangely coded to be almost-but-not-quite thread-safe,
* which doesn't matter for the backend but is important for pgbench.
*
* Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
*
* Portions Copyright (c) 1993 Martin Birgmeier
* All rights reserved.
*
* You may redistribute unmodified or modified versions of this source
* code provided that the above copyright notice and this and the
* following conditions are retained.
*
* This software is provided ``as is'', and comes with no warranties
* of any kind. I shall in no event be liable for anything that happens
* to anyone/anything when using this software.
*
* IDENTIFICATION
* src/port/erand48.c
*
*-------------------------------------------------------------------------
*/
#include "c.h"
#include <math.h>
/* These values are specified by POSIX */
#define RAND48_MULT UINT64CONST(0x0005deece66d)
#define RAND48_ADD UINT64CONST(0x000b)
/* POSIX specifies 0x330e's use in srand48, but the other bits are arbitrary */
#define RAND48_SEED_0 (0x330e)
#define RAND48_SEED_1 (0xabcd)
#define RAND48_SEED_2 (0x1234)
static unsigned short _rand48_seed[3] = {
RAND48_SEED_0,
RAND48_SEED_1,
RAND48_SEED_2
};
/*
* Advance the 48-bit value stored in xseed[] to the next "random" number.
*
* Also returns the value of that number --- without masking it to 48 bits.
* If caller uses the result, it must mask off the bits it wants.
*/
static uint64
_dorand48(unsigned short xseed[3])
{
/*
* We do the arithmetic in uint64; any type wider than 48 bits would work.
*/
uint64 in;
uint64 out;
in = (uint64) xseed[2] << 32 | (uint64) xseed[1] << 16 | (uint64) xseed[0];
out = in * RAND48_MULT + RAND48_ADD;
xseed[0] = out & 0xFFFF;
xseed[1] = (out >> 16) & 0xFFFF;
xseed[2] = (out >> 32) & 0xFFFF;
return out;
}
/*
* Generate a random floating-point value using caller-supplied state.
* Values are uniformly distributed over the interval [0.0, 1.0).
*/
double
pg_erand48(unsigned short xseed[3])
{
uint64 x = _dorand48(xseed);
return ldexp((double) (x & UINT64CONST(0xFFFFFFFFFFFF)), -48);
}
/*
* Generate a random non-negative integral value using internal state.
* Values are uniformly distributed over the interval [0, 2^31).
*/
long
pg_lrand48(void)
{
uint64 x = _dorand48(_rand48_seed);
return (x >> 17) & UINT64CONST(0x7FFFFFFF);
}
/*
* Generate a random signed integral value using caller-supplied state.
* Values are uniformly distributed over the interval [-2^31, 2^31).
*/
long
pg_jrand48(unsigned short xseed[3])
{
uint64 x = _dorand48(xseed);
return (int32) ((x >> 16) & UINT64CONST(0xFFFFFFFF));
}
/*
* Initialize the internal state using the given seed.
*
* Per POSIX, this uses only 32 bits from "seed" even if "long" is wider.
* Hence, the set of possible seed values is smaller than it could be.
* Better practice is to use caller-supplied state and initialize it with
* random bits obtained from a high-quality source of random bits.
*
* Note: POSIX specifies a function seed48() that allows all 48 bits
* of the internal state to be set, but we don't currently support that.
*/
void
pg_srand48(long seed)
{
_rand48_seed[0] = RAND48_SEED_0;
_rand48_seed[1] = (unsigned short) seed;
_rand48_seed[2] = (unsigned short) (seed >> 16);
}

View File

@ -1,25 +0,0 @@
/*-------------------------------------------------------------------------
*
* random.c
* random() wrapper
*
* Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/port/random.c
*
*-------------------------------------------------------------------------
*/
#include "c.h"
#include <math.h>
long
random(void)
{
return pg_lrand48();
}

View File

@ -1,25 +0,0 @@
/*-------------------------------------------------------------------------
*
* srandom.c
* srandom() wrapper
*
* Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/port/srandom.c
*
*-------------------------------------------------------------------------
*/
#include "c.h"
#include <math.h>
void
srandom(unsigned int seed)
{
pg_srand48((long int) seed);
}

View File

@ -12,6 +12,7 @@
*/ */
#include "postgres.h" #include "postgres.h"
#include "common/pg_prng.h"
#include "fmgr.h" #include "fmgr.h"
#include "lib/bloomfilter.h" #include "lib/bloomfilter.h"
#include "miscadmin.h" #include "miscadmin.h"
@ -83,9 +84,8 @@ create_and_test_bloom(int power, int64 nelements, int callerseed)
* Generate random seed, or use caller's. Seed should always be a * Generate random seed, or use caller's. Seed should always be a
* positive value less than or equal to PG_INT32_MAX, to ensure that any * positive value less than or equal to PG_INT32_MAX, to ensure that any
* random seed can be recreated through callerseed if the need arises. * random seed can be recreated through callerseed if the need arises.
* (Don't assume that RAND_MAX cannot exceed PG_INT32_MAX.)
*/ */
seed = callerseed < 0 ? random() % PG_INT32_MAX : callerseed; seed = callerseed < 0 ? pg_prng_int32p(&pg_global_prng_state) : callerseed;
/* Create Bloom filter, populate it, and report on false positive rate */ /* Create Bloom filter, populate it, and report on false positive rate */
filter = bloom_create(nelements, bloom_work_mem, seed); filter = bloom_create(nelements, bloom_work_mem, seed);

View File

@ -12,6 +12,7 @@
*/ */
#include "postgres.h" #include "postgres.h"
#include "common/pg_prng.h"
#include "fmgr.h" #include "fmgr.h"
#include "lib/integerset.h" #include "lib/integerset.h"
#include "miscadmin.h" #include "miscadmin.h"
@ -248,8 +249,7 @@ test_pattern(const test_spec *spec)
* only a small part of the integer space is used. We would very * only a small part of the integer space is used. We would very
* rarely hit values that are actually in the set. * rarely hit values that are actually in the set.
*/ */
x = (pg_lrand48() << 31) | pg_lrand48(); x = pg_prng_uint64_range(&pg_global_prng_state, 0, last_int + 1000);
x = x % (last_int + 1000);
/* Do we expect this value to be present in the set? */ /* Do we expect this value to be present in the set? */
if (x >= last_int) if (x >= last_int)
@ -571,7 +571,7 @@ test_huge_distances(void)
*/ */
while (num_values < 1000) while (num_values < 1000)
{ {
val += pg_lrand48(); val += pg_prng_uint32(&pg_global_prng_state);
values[num_values++] = val; values[num_values++] = val;
} }

View File

@ -13,6 +13,7 @@
#include "postgres.h" #include "postgres.h"
#include "common/pg_prng.h"
#include "fmgr.h" #include "fmgr.h"
#include "lib/rbtree.h" #include "lib/rbtree.h"
#include "utils/memutils.h" #include "utils/memutils.h"
@ -108,7 +109,7 @@ GetPermutation(int size)
*/ */
for (i = 1; i < size; i++) for (i = 1; i < size; i++)
{ {
int j = random() % (i + 1); int j = pg_prng_uint64_range(&pg_global_prng_state, 0, i);
if (j < i) /* avoid fetching undefined data if j=i */ if (j < i) /* avoid fetching undefined data if j=i */
permutation[i] = permutation[j]; permutation[i] = permutation[j];
@ -320,7 +321,7 @@ testdelete(int size, int delsize)
for (i = 0; i < delsize; i++) for (i = 0; i < delsize; i++)
{ {
int k = random() % size; int k = pg_prng_uint64_range(&pg_global_prng_state, 0, size - 1);
while (chosen[k]) while (chosen[k])
k = (k + 1) % size; k = (k + 1) % size;

View File

@ -99,9 +99,9 @@ sub mkvcbuild
$solution = CreateSolution($vsVersion, $config); $solution = CreateSolution($vsVersion, $config);
our @pgportfiles = qw( our @pgportfiles = qw(
chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c random.c chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c
srandom.c getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
erand48.c snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
dirent.c dlopen.c getopt.c getopt_long.c link.c dirent.c dlopen.c getopt.c getopt_long.c link.c
pread.c preadv.c pwrite.c pwritev.c pg_bitutils.c pread.c preadv.c pwrite.c pwritev.c pg_bitutils.c
pg_strong_random.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c pg_strong_random.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c
@ -127,9 +127,9 @@ sub mkvcbuild
config_info.c controldata_utils.c d2s.c encnames.c exec.c config_info.c controldata_utils.c d2s.c encnames.c exec.c
f2s.c file_perm.c file_utils.c hashfn.c ip.c jsonapi.c f2s.c file_perm.c file_utils.c hashfn.c ip.c jsonapi.c
keywords.c kwlookup.c link-canary.c md5_common.c keywords.c kwlookup.c link-canary.c md5_common.c
pg_get_line.c pg_lzcompress.c pgfnames.c psprintf.c relpath.c rmtree.c pg_get_line.c pg_lzcompress.c pg_prng.c pgfnames.c psprintf.c relpath.c
saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c username.c rmtree.c saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c
wait_error.c wchar.c); username.c wait_error.c wchar.c);
if ($solution->{options}->{openssl}) if ($solution->{options}->{openssl})
{ {

View File

@ -342,7 +342,6 @@ sub GenerateFiles
HAVE_PTHREAD_IS_THREADED_NP => undef, HAVE_PTHREAD_IS_THREADED_NP => undef,
HAVE_PTHREAD_PRIO_INHERIT => undef, HAVE_PTHREAD_PRIO_INHERIT => undef,
HAVE_PWRITE => undef, HAVE_PWRITE => undef,
HAVE_RANDOM => undef,
HAVE_READLINE_H => undef, HAVE_READLINE_H => undef,
HAVE_READLINE_HISTORY_H => undef, HAVE_READLINE_HISTORY_H => undef,
HAVE_READLINE_READLINE_H => undef, HAVE_READLINE_READLINE_H => undef,
@ -363,7 +362,6 @@ sub GenerateFiles
HAVE_SHM_OPEN => undef, HAVE_SHM_OPEN => undef,
HAVE_SOCKLEN_T => 1, HAVE_SOCKLEN_T => 1,
HAVE_SPINLOCKS => 1, HAVE_SPINLOCKS => 1,
HAVE_SRANDOM => undef,
HAVE_STDBOOL_H => 1, HAVE_STDBOOL_H => 1,
HAVE_STDINT_H => 1, HAVE_STDINT_H => 1,
HAVE_STDLIB_H => 1, HAVE_STDLIB_H => 1,

View File

@ -27,6 +27,7 @@
#endif #endif
#include "common/int128.h" #include "common/int128.h"
#include "common/pg_prng.h"
/* /*
* We assume the parts of this union are laid out compatibly. * We assume the parts of this union are laid out compatibly.
@ -61,27 +62,11 @@ my_int128_compare(int128 x, int128 y)
return 0; return 0;
} }
/*
* Get a random uint64 value.
* We don't assume random() is good for more than 16 bits.
*/
static uint64
get_random_uint64(void)
{
uint64 x;
x = (uint64) (random() & 0xFFFF) << 48;
x |= (uint64) (random() & 0xFFFF) << 32;
x |= (uint64) (random() & 0xFFFF) << 16;
x |= (uint64) (random() & 0xFFFF);
return x;
}
/* /*
* Main program. * Main program.
* *
* Generates a lot of random numbers and tests the implementation for each. * Generates a lot of random numbers and tests the implementation for each.
* The results should be reproducible, since we don't call srandom(). * The results should be reproducible, since we use a fixed PRNG seed.
* *
* You can give a loop count if you don't like the default 1B iterations. * You can give a loop count if you don't like the default 1B iterations.
*/ */
@ -90,6 +75,8 @@ main(int argc, char **argv)
{ {
long count; long count;
pg_prng_seed(&pg_global_prng_state, 0);
if (argc >= 2) if (argc >= 2)
count = strtol(argv[1], NULL, 0); count = strtol(argv[1], NULL, 0);
else else
@ -97,9 +84,9 @@ main(int argc, char **argv)
while (count-- > 0) while (count-- > 0)
{ {
int64 x = get_random_uint64(); int64 x = pg_prng_uint64(&pg_global_prng_state);
int64 y = get_random_uint64(); int64 y = pg_prng_uint64(&pg_global_prng_state);
int64 z = get_random_uint64(); int64 z = pg_prng_uint64(&pg_global_prng_state);
test128 t1; test128 t1;
test128 t2; test128 t2;
@ -151,7 +138,7 @@ main(int argc, char **argv)
t1.hl.hi = x; t1.hl.hi = x;
t1.hl.lo = y; t1.hl.lo = y;
t2.hl.hi = z; t2.hl.hi = z;
t2.hl.lo = get_random_uint64(); t2.hl.lo = pg_prng_uint64(&pg_global_prng_state);
if (my_int128_compare(t1.i128, t2.i128) != if (my_int128_compare(t1.i128, t2.i128) !=
int128_compare(t1.I128, t2.I128)) int128_compare(t1.I128, t2.I128))