Skip to content

Commit

Permalink
Replace random(), pg_erand48(), etc with a better PRNG API and algori…
Browse files Browse the repository at this point in the history
…thm.

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
  • Loading branch information
tglsfdc committed Nov 29, 2021
1 parent f44ceb4 commit 3804539
Show file tree
Hide file tree
Showing 50 changed files with 544 additions and 481 deletions.
26 changes: 0 additions & 26 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -16463,32 +16463,6 @@ esac

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"
if test "x$ac_cv_func_strlcat" = xyes; then :
$as_echo "#define HAVE_STRLCAT 1" >>confdefs.h
Expand Down
2 changes: 0 additions & 2 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -1858,8 +1858,6 @@ AC_REPLACE_FUNCS(m4_normalize([
mkdtemp
pread
pwrite
random
srandom
strlcat
strlcpy
strnlen
Expand Down
5 changes: 3 additions & 2 deletions contrib/amcheck/verify_nbtree.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "catalog/index.h"
#include "catalog/pg_am.h"
#include "commands/tablecmds.h"
#include "common/pg_prng.h"
#include "lib/bloomfilter.h"
#include "miscadmin.h"
#include "storage/lmgr.h"
Expand Down Expand Up @@ -466,8 +467,8 @@ bt_check_every_level(Relation rel, Relation heaprel, bool heapkeyspace,
total_pages = RelationGetNumberOfBlocks(rel);
total_elems = Max(total_pages * (MaxTIDsPerBTreePage / 3),
(int64) state->rel->rd_rel->reltuples);
/* Random seed relies on backend srandom() call to avoid repetition */
seed = random();
/* Generate a random seed to avoid repetition */
seed = pg_prng_uint64(&pg_global_prng_state);
/* Create Bloom filter to fingerprint index */
state->filter = bloom_create(total_elems, maintenance_work_mem, seed);
state->heaptuplespresent = 0;
Expand Down
4 changes: 2 additions & 2 deletions contrib/auto_explain/auto_explain.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include "access/parallel.h"
#include "commands/explain.h"
#include "common/pg_prng.h"
#include "executor/instrument.h"
#include "jit/jit.h"
#include "utils/guc.h"
Expand Down Expand Up @@ -275,8 +276,7 @@ explain_ExecutorStart(QueryDesc *queryDesc, int eflags)
if (nesting_level == 0)
{
if (auto_explain_log_min_duration >= 0 && !IsParallelWorker())
current_query_sampled = (random() < auto_explain_sample_rate *
((double) MAX_RANDOM_VALUE + 1));
current_query_sampled = (pg_prng_double(&pg_global_prng_state) < auto_explain_sample_rate);
else
current_query_sampled = false;
}
Expand Down
2 changes: 1 addition & 1 deletion contrib/file_fdw/file_fdw.c
Original file line number Diff line number Diff line change
Expand Up @@ -1188,7 +1188,7 @@ file_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]);
Expand Down
2 changes: 1 addition & 1 deletion contrib/postgres_fdw/postgres_fdw.c
Original file line number Diff line number Diff line change
Expand Up @@ -5158,7 +5158,7 @@ analyze_row_processor(PGresult *res, int row, PgFdwAnalyzeState *astate)
if (astate->rowstoskip <= 0)
{
/* 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);
heap_freetuple(astate->rows[pos]);
}
Expand Down
5 changes: 3 additions & 2 deletions contrib/tablefunc/tablefunc.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

#include "access/htup_details.h"
#include "catalog/pg_type.h"
#include "common/pg_prng.h"
#include "executor/spi.h"
#include "funcapi.h"
#include "lib/stringinfo.h"
Expand Down Expand Up @@ -290,8 +291,8 @@ get_normal_pair(float8 *x1, float8 *x2)

do
{
u1 = (float8) random() / (float8) MAX_RANDOM_VALUE;
u2 = (float8) random() / (float8) MAX_RANDOM_VALUE;
u1 = pg_prng_double(&pg_global_prng_state);
u2 = pg_prng_double(&pg_global_prng_state);

v1 = (2.0 * u1) - 1.0;
v2 = (2.0 * u2) - 1.0;
Expand Down
12 changes: 6 additions & 6 deletions contrib/tsm_system_rows/tsm_system_rows.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ static BlockNumber system_rows_nextsampleblock(SampleScanState *node, BlockNumbe
static OffsetNumber system_rows_nextsampletuple(SampleScanState *node,
BlockNumber blockno,
OffsetNumber maxoffset);
static uint32 random_relative_prime(uint32 n, SamplerRandomState randstate);
static uint32 random_relative_prime(uint32 n, pg_prng_state *randstate);


/*
Expand Down Expand Up @@ -213,25 +213,25 @@ system_rows_nextsampleblock(SampleScanState *node, BlockNumber nblocks)
if (sampler->step == 0)
{
/* Initialize now that we have scan descriptor */
SamplerRandomState randstate;
pg_prng_state randstate;

/* If relation is empty, there's nothing to scan */
if (nblocks == 0)
return InvalidBlockNumber;

/* 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 */
sampler->nblocks = nblocks;

/* Choose random starting block within the relation */
/* (Actually this is the predecessor of the first block visited) */
sampler->firstblock = sampler_random_fract(randstate) *
sampler->firstblock = sampler_random_fract(&randstate) *
sampler->nblocks;

/* 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 */
Expand Down Expand Up @@ -317,7 +317,7 @@ gcd(uint32 a, uint32 b)
* (else return 1).
*/
static uint32
random_relative_prime(uint32 n, SamplerRandomState randstate)
random_relative_prime(uint32 n, pg_prng_state *randstate)
{
uint32 r;

Expand Down
12 changes: 6 additions & 6 deletions contrib/tsm_system_time/tsm_system_time.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ static BlockNumber system_time_nextsampleblock(SampleScanState *node, BlockNumbe
static OffsetNumber system_time_nextsampletuple(SampleScanState *node,
BlockNumber blockno,
OffsetNumber maxoffset);
static uint32 random_relative_prime(uint32 n, SamplerRandomState randstate);
static uint32 random_relative_prime(uint32 n, pg_prng_state *randstate);


/*
Expand Down Expand Up @@ -224,25 +224,25 @@ system_time_nextsampleblock(SampleScanState *node, BlockNumber nblocks)
if (sampler->step == 0)
{
/* Initialize now that we have scan descriptor */
SamplerRandomState randstate;
pg_prng_state randstate;

/* If relation is empty, there's nothing to scan */
if (nblocks == 0)
return InvalidBlockNumber;

/* 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 */
sampler->nblocks = nblocks;

/* Choose random starting block within the relation */
/* (Actually this is the predecessor of the first block visited) */
sampler->firstblock = sampler_random_fract(randstate) *
sampler->firstblock = sampler_random_fract(&randstate) *
sampler->nblocks;

/* 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 */
Expand Down Expand Up @@ -330,7 +330,7 @@ gcd(uint32 a, uint32 b)
* (else return 1).
*/
static uint32
random_relative_prime(uint32 n, SamplerRandomState randstate)
random_relative_prime(uint32 n, pg_prng_state *randstate)
{
uint32 r;

Expand Down
3 changes: 2 additions & 1 deletion src/backend/access/gin/ginget.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)) )

/*
Expand Down
5 changes: 3 additions & 2 deletions src/backend/access/gist/gistutil.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
{
Expand All @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion src/backend/access/nbtree/nbtinsert.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
Expand Down
5 changes: 4 additions & 1 deletion src/backend/access/spgist/spgdoinsert.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion src/backend/access/transam/xact.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
7 changes: 4 additions & 3 deletions src/backend/commands/analyze.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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]);
Expand Down
3 changes: 2 additions & 1 deletion src/backend/executor/nodeSamplescan.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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.
Expand Down
3 changes: 1 addition & 2 deletions src/backend/lib/bloomfilter.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 3804539

Please sign in to comment.