rand48 replacement

Started by Fabien COELHOover 4 years ago59 messages
#1Fabien COELHO
coelho@cri.ensmp.fr
1 attachment(s)

Hello pg-devs,

I have given a go at proposing a replacement for rand48.

POSIX 1988 (?) rand48 is a LCG PRNG designed to generate 32 bits integers
or floats based on a 48 bits state on 16 or 32 bits architectures. LCG
cycles on the low bits, which can be quite annoying. Given that we run on
64 bits architectures and that we need to generate 64 bits ints or
doubles, IMHO it makes very little sense to stick to that.

We should (probably) want:
- one reasonable default PRNG for all pg internal uses.
- NOT to invent a new design!
- something fast, close to rand48 (which basically does 2 arithmetic
ops, so it is hard to compete)
no need for something cryptographic though, which would imply slow
- to produce 64 bits integers & doubles with a 52 bits mantissa,
so state size > 64 bits.
- a small state though, because we might generate quite a few of them
for different purposes so state size <= 256 or even <= 128 bits
- the state to be aligned to whatever => 128 bits
- 64 bits operations for efficiency on modern architectures,
but not 128 bits operations.
- not to depend on special hardware for speed (eg MMX/SSE/AES).
- not something with obvious known and relevant defects.
- not something with "rights" attached.

These constraints reduce drastically the available options from
https://en.wikipedia.org/wiki/List_of_random_number_generators

The attached patch removes "rand48" and adds a "pg_prng" implementation
based on xoroshiro128ss, and replaces it everywhere. In pgbench, the non
portable double-relying code is replaced by hopefully portable ints. The
interface makes it easy to replace the underlying PRNG if something else
is desired.

Thanks for your feedback.

--
Fabien.

Attachments:

prng-1.patchtext/x-diff; name=prng-1.patchDownload
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 2c2f149fb0..146b524076 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -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]);
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index c48a421e88..174859ad64 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -5094,7 +5094,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]);
 		}
diff --git a/contrib/tsm_system_rows/tsm_system_rows.c b/contrib/tsm_system_rows/tsm_system_rows.c
index 4996612902..1a46d4b143 100644
--- a/contrib/tsm_system_rows/tsm_system_rows.c
+++ b/contrib/tsm_system_rows/tsm_system_rows.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/contrib/tsm_system_time/tsm_system_time.c b/contrib/tsm_system_time/tsm_system_time.c
index 788d8f9a68..36acc6c106 100644
--- a/contrib/tsm_system_time/tsm_system_time.c
+++ b/contrib/tsm_system_time/tsm_system_time.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 426c1e6710..092d53d28a 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1294,7 +1294,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 					 * Found a suitable tuple, so save it, replacing one old
 					 * tuple at random
 					 */
-					int			k = (int) (targrows * sampler_random_fract(rstate.randstate));
+					int			k = (int) (targrows * sampler_random_fract(&rstate.randstate));
 
 					Assert(k >= 0 && k < targrows);
 					heap_freetuple(rows[k]);
diff --git a/src/backend/optimizer/geqo/geqo_random.c b/src/backend/optimizer/geqo/geqo_random.c
index f21bc047e6..8b42a9ffba 100644
--- a/src/backend/optimizer/geqo/geqo_random.c
+++ b/src/backend/optimizer/geqo/geqo_random.c
@@ -21,14 +21,7 @@ geqo_set_seed(PlannerInfo *root, double seed)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	/*
-	 * XXX. This seeding algorithm could certainly be improved - but it is not
-	 * critical to do so.
-	 */
-	memset(private->random_state, 0, sizeof(private->random_state));
-	memcpy(private->random_state,
-		   &seed,
-		   Min(sizeof(private->random_state), sizeof(seed)));
+	pg_prng_fseed(&private->random_state, seed);
 }
 
 double
@@ -36,5 +29,5 @@ geqo_rand(PlannerInfo *root)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	return pg_erand48(private->random_state);
+	return pg_prng_f64(&private->random_state);
 }
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 098bbb372b..37fcd645e0 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -65,7 +65,7 @@ float8		degree_c_one = 1.0;
 
 /* State for drandom() and setseed() */
 static bool drandom_seed_set = false;
-static unsigned short drandom_seed[3] = {0, 0, 0};
+static pg_prng_state drandom_seed = PG_PRNG_DEFAULT_STATE;
 
 /* Local function prototypes */
 static double sind_q1(double x);
@@ -2762,22 +2762,20 @@ drandom(PG_FUNCTION_ARGS)
 		 * Should that fail for some reason, we fall back on a lower-quality
 		 * seed based on current time and PID.
 		 */
-		if (!pg_strong_random(drandom_seed, sizeof(drandom_seed)))
+		if (!pg_strong_random(&drandom_seed, sizeof(drandom_seed)))
 		{
 			TimestampTz now = GetCurrentTimestamp();
 			uint64		iseed;
 
 			/* Mix the PID with the most predictable bits of the timestamp */
 			iseed = (uint64) now ^ ((uint64) MyProcPid << 32);
-			drandom_seed[0] = (unsigned short) iseed;
-			drandom_seed[1] = (unsigned short) (iseed >> 16);
-			drandom_seed[2] = (unsigned short) (iseed >> 32);
+			pg_prng_seed(&drandom_seed, iseed);
 		}
 		drandom_seed_set = true;
 	}
 
-	/* pg_erand48 produces desired result range [0.0 - 1.0) */
-	result = pg_erand48(drandom_seed);
+	/* produces desired result range [0.0 - 1.0) */
+	result = pg_prng_f64(&drandom_seed);
 
 	PG_RETURN_FLOAT8(result);
 }
@@ -2790,7 +2788,6 @@ Datum
 setseed(PG_FUNCTION_ARGS)
 {
 	float8		seed = PG_GETARG_FLOAT8(0);
-	uint64		iseed;
 
 	if (seed < -1 || seed > 1 || isnan(seed))
 		ereport(ERROR,
@@ -2798,11 +2795,7 @@ setseed(PG_FUNCTION_ARGS)
 				 errmsg("setseed parameter %g is out of allowed range [-1,1]",
 						seed)));
 
-	/* Use sign bit + 47 fractional bits to fill drandom_seed[] */
-	iseed = (int64) (seed * (float8) UINT64CONST(0x7FFFFFFFFFFF));
-	drandom_seed[0] = (unsigned short) iseed;
-	drandom_seed[1] = (unsigned short) (iseed >> 16);
-	drandom_seed[2] = (unsigned short) (iseed >> 32);
+	pg_prng_fseed(&drandom_seed, seed);
 	drandom_seed_set = true;
 
 	PG_RETURN_VOID();
diff --git a/src/backend/utils/misc/sampling.c b/src/backend/utils/misc/sampling.c
index 0c327e823f..7b59128878 100644
--- a/src/backend/utils/misc/sampling.c
+++ b/src/backend/utils/misc/sampling.c
@@ -49,7 +49,7 @@ BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize,
 	bs->t = 0;					/* blocks scanned so far */
 	bs->m = 0;					/* blocks selected so far */
 
-	sampler_random_init_state(randseed, bs->randstate);
+	sampler_random_init_state(randseed, &bs->randstate);
 
 	return Min(bs->n, bs->N);
 }
@@ -98,7 +98,7 @@ BlockSampler_Next(BlockSampler bs)
 	 * less than k, which means that we cannot fail to select enough blocks.
 	 *----------
 	 */
-	V = sampler_random_fract(bs->randstate);
+	V = sampler_random_fract(&bs->randstate);
 	p = 1.0 - (double) k / (double) K;
 	while (V < p)
 	{
@@ -136,10 +136,10 @@ reservoir_init_selection_state(ReservoirState rs, int n)
 	 * Reservoir sampling is not used anywhere where it would need to return
 	 * repeatable results so we can initialize it randomly.
 	 */
-	sampler_random_init_state(random(), rs->randstate);
+	sampler_random_init_state(random(), &rs->randstate);
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	rs->W = exp(-log(sampler_random_fract(rs->randstate)) / n);
+	rs->W = exp(-log(sampler_random_fract(&rs->randstate)) / n);
 }
 
 double
@@ -154,7 +154,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 		double		V,
 					quot;
 
-		V = sampler_random_fract(rs->randstate);	/* Generate V */
+		V = sampler_random_fract(&rs->randstate);	/* Generate V */
 		S = 0;
 		t += 1;
 		/* Note: "num" in Vitter's code is always equal to t - n */
@@ -186,7 +186,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 						tmp;
 
 			/* Generate U and X */
-			U = sampler_random_fract(rs->randstate);
+			U = sampler_random_fract(&rs->randstate);
 			X = t * (W - 1.0);
 			S = floor(X);		/* S is tentatively set to floor(X) */
 			/* Test if U <= h(S)/cg(X) in the manner of (6.3) */
@@ -215,7 +215,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 				y *= numer / denom;
 				denom -= 1;
 			}
-			W = exp(-log(sampler_random_fract(rs->randstate)) / n); /* Generate W in advance */
+			W = exp(-log(sampler_random_fract(&rs->randstate)) / n); /* Generate W in advance */
 			if (exp(log(y) / n) <= (t + X) / t)
 				break;
 		}
@@ -230,24 +230,22 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
  *----------
  */
 void
-sampler_random_init_state(long seed, SamplerRandomState randstate)
+sampler_random_init_state(long seed, pg_prng_state *randstate)
 {
-	randstate[0] = 0x330e;		/* same as pg_erand48, but could be anything */
-	randstate[1] = (unsigned short) seed;
-	randstate[2] = (unsigned short) (seed >> 16);
+	pg_prng_seed(randstate, (uint64_t) seed);
 }
 
 /* Select a random value R uniformly distributed in (0 - 1) */
 double
-sampler_random_fract(SamplerRandomState randstate)
+sampler_random_fract(pg_prng_state *randstate)
 {
 	double		res;
 
-	/* pg_erand48 returns a value in [0.0 - 1.0), so we must reject 0 */
+	/* pg_prng_f64 returned value in [0.0 - 1.0), so we must reject 0.0 */
 	do
 	{
-		res = pg_erand48(randstate);
-	} while (res == 0.0);
+		res = pg_prng_f64(randstate);
+	} while (unlikely(res == 0.0));
 	return res;
 }
 
@@ -266,22 +264,28 @@ double
 anl_random_fract(void)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(random(), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* and compute a random fraction */
-	return sampler_random_fract(oldrs.randstate);
+	return sampler_random_fract(&oldrs.randstate);
 }
 
 double
 anl_init_selection_state(int n)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(random(), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	return exp(-log(sampler_random_fract(oldrs.randstate)) / n);
+	return exp(-log(sampler_random_fract(&oldrs.randstate)) / n);
 }
 
 double
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index dc84b7b9b7..a459424a63 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -342,16 +342,8 @@ typedef struct StatsData
 	SimpleStats lag;
 } StatsData;
 
-/*
- * Struct to keep random state.
- */
-typedef struct RandomState
-{
-	unsigned short xseed[3];
-} RandomState;
-
 /* 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 */
 static THREAD_BARRIER_T barrier;
@@ -453,7 +445,7 @@ typedef struct
 	 * Separate randomness for each client. This is used for random functions
 	 * 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			command;		/* command number in script */
@@ -490,9 +482,9 @@ typedef struct
 	 * random state to make all of them independent of each other and
 	 * therefore deterministic at the thread level.
 	 */
-	RandomState ts_choose_rs;	/* random state for selecting a script */
-	RandomState ts_throttle_rs; /* random state for transaction throttling */
-	RandomState ts_sample_rs;	/* random state for log sampling */
+	pg_prng_state	ts_choose_rs;	/* random state for selecting a script */
+	pg_prng_state	ts_throttle_rs; /* random state for transaction throttling */
+	pg_prng_state	ts_sample_rs;	/* random state for log sampling */
 
 	int64		throttle_trigger;	/* previous/next throttling (us) */
 	FILE	   *logfile;		/* where to log, or NULL */
@@ -890,42 +882,34 @@ 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.
  */
 static void
-initRandomState(RandomState *random_state)
+initRandomState(pg_prng_state *state)
 {
-	random_state->xseed[0] = (unsigned short)
-		(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);
+	pg_prng_seed(state, pg_prng_u64(&base_random_sequence));
 }
 
+
 /*
  * Random number generator: uniform distribution from min to max inclusive.
  *
  * Although the limits are expressed as int64, you can't generate the full
  * int64 range in one call, because the difference of the limits mustn't
- * overflow int64.  In practice it's unwise to ask for more than an int32
- * range, because of the limited precision of pg_erand48().
+ * overflow int64.
  */
 static int64
-getrand(RandomState *random_state, int64 min, int64 max)
+getrand(pg_prng_state *state, int64 min, int64 max)
 {
 	/*
 	 * 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));
+	int64	size = max - min + 1,
+			val = pg_prng_u64(state) & UINT64CONST(0x7FFFFFFFFFFFFFFF);
+	return min + (int64) (val % size);
 }
 
 /*
@@ -934,8 +918,7 @@ getrand(RandomState *random_state, int64 min, int64 max)
  * value is exp(-parameter).
  */
 static int64
-getExponentialRand(RandomState *random_state, int64 min, int64 max,
-				   double parameter)
+getExponentialRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		cut,
 				uniform,
@@ -945,7 +928,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 	Assert(parameter > 0.0);
 	cut = exp(-parameter);
 	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	uniform = 1.0 - pg_prng_f64(state);
 
 	/*
 	 * inner expression in (cut, 1] (if parameter > 0), rand in [0, 1)
@@ -958,8 +941,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 
 /* random number generator: gaussian distribution from min to max inclusive */
 static int64
-getGaussianRand(RandomState *random_state, int64 min, int64 max,
-				double parameter)
+getGaussianRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		stdev;
 	double		rand;
@@ -982,13 +964,13 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
 	do
 	{
 		/*
-		 * pg_erand48 generates [0,1), but for the basic version of the
+		 * pg_prng_f64 generates [0,1), but for the basic version of the
 		 * Box-Muller transform the two uniformly distributed random numbers
 		 * are expected in (0, 1] (see
 		 * https://en.wikipedia.org/wiki/Box-Muller_transform)
 		 */
-		double		rand1 = 1.0 - pg_erand48(random_state->xseed);
-		double		rand2 = 1.0 - pg_erand48(random_state->xseed);
+		double		rand1 = 1.0 - pg_prng_f64(state);
+		double		rand2 = 1.0 - pg_prng_f64(state);
 
 		/* Box-Muller basic form transform */
 		double		var_sqrt = sqrt(-2.0 * log(rand1));
@@ -1018,7 +1000,7 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
  * not be one.
  */
 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
@@ -1026,8 +1008,8 @@ getPoissonRand(RandomState *random_state, double center)
 	 */
 	double		uniform;
 
-	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	/* pseudo-random value in [0, 1), uniform in (0, 1] */
+	uniform = 1.0 - pg_prng_f64(state);
 
 	return (int64) (-log(uniform) * center + 0.5);
 }
@@ -1040,7 +1022,7 @@ getPoissonRand(RandomState *random_state, double center)
  * This works for s > 1.0, but may perform badly for s very close to 1.0.
  */
 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		x,
@@ -1055,8 +1037,8 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 	while (true)
 	{
 		/* random variates */
-		u = pg_erand48(random_state->xseed);
-		v = pg_erand48(random_state->xseed);
+		u = pg_prng_f64(state);
+		v = pg_prng_f64(state);
 
 		x = floor(pow(u, -1.0 / (s - 1.0)));
 
@@ -1070,14 +1052,14 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 
 /* random number generator: zipfian distribution from min to max inclusive */
 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;
 
 	/* abort if parameter is invalid */
 	Assert(MIN_ZIPFIAN_PARAM <= s && s <= MAX_ZIPFIAN_PARAM);
 
-	return min - 1 + computeIterativeZipfian(random_state, n, s);
+	return min - 1 + computeIterativeZipfian(state, n, s);
 }
 
 /*
@@ -1134,7 +1116,7 @@ getHashMurmur2(int64 val, uint64 seed)
  * For small sizes, this generates each of the (size!) possible permutations
  * of integers in the range [0, size) with roughly equal probability.  Once
  * the size is larger than 20, the number of possible permutations exceeds the
- * number of distinct states of the internal pseudorandom number generators,
+ * number of distinct states of the internal pseudorandom number generator,
  * and so not all possible permutations can be generated, but the permutations
  * chosen should continue to give the appearance of being random.
  *
@@ -1144,25 +1126,19 @@ getHashMurmur2(int64 val, uint64 seed)
 static int64
 permute(const int64 val, const int64 isize, const int64 seed)
 {
-	RandomState random_state1;
-	RandomState random_state2;
-	uint64		size;
-	uint64		v;
-	int			masklen;
-	uint64		mask;
-	int			i;
+	/* using a high-end PRNG is probably overkill */
+	pg_prng_state	state;
+	uint64			size;
+	uint64			v;
+	int				masklen;
+	uint64			mask;
+	int				i;
 
 	if (isize < 2)
 		return 0;				/* nothing to permute */
 
-	/* Initialize a pair of random states using the seed */
-	random_state1.xseed[0] = seed & 0xFFFF;
-	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;
+	/* Initialize prng state using the seed */
+	pg_prng_seed(&state, (uint64) seed);
 
 	/* Computations are performed on unsigned values */
 	size = (uint64) isize;
@@ -1208,8 +1184,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 					t;
 
 		/* Random multiply (by an odd number), XOR and rotate of lower half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		if (v <= mask)
 		{
 			v = ((v * m) ^ r) & mask;
@@ -1217,8 +1193,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random multiply (by an odd number), XOR and rotate of upper half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		t = size - 1 - v;
 		if (t <= mask)
 		{
@@ -1228,7 +1204,7 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random offset */
-		r = (uint64) getrand(&random_state2, 0, size - 1);
+		r = pg_prng_u64(&state) % (size - 1);
 		v = (v + r) % size;
 	}
 
@@ -3787,7 +3763,7 @@ doLog(TState *thread, CState *st,
 	 * to the random sample.
 	 */
 	if (sample_rate != 0.0 &&
-		pg_erand48(thread->ts_sample_rs.xseed) > sample_rate)
+		pg_prng_f64(&thread->ts_sample_rs) > sample_rate)
 		return;
 
 	/* should we aggregate the results or not? */
@@ -5681,12 +5657,11 @@ set_random_seed(const char *seed)
 
 	if (seed != NULL)
 		pg_log_info("setting random seed to %llu", (unsigned long long) iseed);
+
 	random_seed = iseed;
 
 	/* Fill base_random_sequence with low-order bits of seed */
-	base_random_sequence.xseed[0] = iseed & 0xFFFF;
-	base_random_sequence.xseed[1] = (iseed >> 16) & 0xFFFF;
-	base_random_sequence.xseed[2] = (iseed >> 32) & 0xFFFF;
+	pg_prng_seed(&base_random_sequence, (uint64_t) iseed);
 
 	return true;
 }
@@ -6369,9 +6344,7 @@ main(int argc, char **argv)
 	/* set default seed for hash functions */
 	if (lookupVariable(&state[0], "default_seed") == NULL)
 	{
-		uint64		seed =
-		((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) |
-		(((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) << 32);
+		uint64		seed = pg_prng_u64(&base_random_sequence);
 
 		for (i = 0; i < nclients; i++)
 			if (!putVariableInt(&state[i], "startup", "default_seed", (int64) seed))
diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl
index 9cc5270e02..9f0eeef997 100644
--- a/src/bin/pgbench/t/001_pgbench_with_server.pl
+++ b/src/bin/pgbench/t/001_pgbench_with_server.pl
@@ -427,9 +427,9 @@ pgbench(
 
 		# After explicit seeding, the four random checks (1-3,20) are
 		# deterministic
-		qr{command=1.: int 13\b},      # uniform random
-		qr{command=2.: int 116\b},     # exponential random
-		qr{command=3.: int 1498\b},    # gaussian random
+		qr{command=1.: int 10\b},      # uniform random
+		qr{command=2.: int 100\b},     # exponential random
+		qr{command=3.: int 1526\b},    # gaussian random
 		qr{command=4.: int 4\b},
 		qr{command=5.: int 5\b},
 		qr{command=6.: int 6\b},
@@ -496,6 +496,7 @@ pgbench(
 		qr{command=109.: boolean true\b},
 		qr{command=110.: boolean true\b},
 		qr{command=111.: boolean true\b},
+		qr{command=113.: boolean true\b},
 	],
 	'pgbench expressions',
 	{
@@ -639,8 +640,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))
 -- actual values
 \set t debug(permute(:v, 1) = 0)
-\set t debug(permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1 and \
-             permute(0, 2, 5435) = 1 and permute(1, 2, 5435) = 0)
+\set t debug(permute(0, 2, 5431) = 1 and permute(1, 2, 5431) = 0 and \
+             permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1)
+-- 63 bits tests for checking permute portability across architectures
+\set size debug(:max - 10)
+\set t debug(permute(:size-1, :size, 5432) = 4352241160009873020 and \
+             permute(:size-2, :size, 5432) = 4990004119691638710 and \
+             permute(:size-3, :size, 5432) = 5868029867256127549 and \
+             permute(:size-4, :size, 5432) = 1238830324886341378 and \
+             permute(:size-5, :size, 5432) = 7415914367102906897 and \
+             permute(:size-6, :size, 5432) = 3214501037984818995 and \
+             permute(:size-7, :size, 5432) =  600660351261124695)
 }
 	});
 
diff --git a/src/include/optimizer/geqo.h b/src/include/optimizer/geqo.h
index 24dcdfb6cc..75bfada4a1 100644
--- a/src/include/optimizer/geqo.h
+++ b/src/include/optimizer/geqo.h
@@ -72,8 +72,8 @@ extern double Geqo_seed;		/* 0 .. 1 */
  */
 typedef struct
 {
-	List	   *initial_rels;	/* the base relations we are joining */
-	unsigned short random_state[3]; /* state for pg_erand48() */
+	List		   *initial_rels;	/* the base relations we are joining */
+	pg_prng_state	random_state;   /* PRNG state */
 } GeqoPrivateData;
 
 
diff --git a/src/include/port.h b/src/include/port.h
index 82f63de325..a73c944c91 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -356,10 +356,24 @@ extern int	gettimeofday(struct timeval *tp, struct timezone *tzp);
 #define pgoff_t off_t
 #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);
+/* PRNG: Pseudo-Random Number Generator */
+typedef struct pg_prng_state {
+	uint64_t	s0, s1;
+} pg_prng_state;
+
+/* use Donald Knuth's LCG constants */
+#define PG_PRNG_DEFAULT_STATE \
+	{ UINT64CONST(0x5851F42D4C957F2D), UINT64CONST(0x14057B7EF767814F) }
+
+extern void pg_prng_seed(pg_prng_state *state, uint64_t seed);
+extern void pg_prng_fseed(pg_prng_state *state, double fseed);
+extern void pg_prng_strong_seed(pg_prng_state *state);
+extern uint64_t pg_prng_u64(pg_prng_state *state);
+extern int64_t pg_prng_i64(pg_prng_state *state);
+extern uint32_t pg_prng_u32(pg_prng_state *state);
+extern int32_t pg_prng_i32(pg_prng_state *state);
+extern double pg_prng_f64(pg_prng_state *state);
+extern bool pg_prng_bool(pg_prng_state *state);
 
 #ifndef HAVE_FLS
 extern int	fls(int mask);
diff --git a/src/include/utils/sampling.h b/src/include/utils/sampling.h
index a58d14281b..97cd9b7aef 100644
--- a/src/include/utils/sampling.h
+++ b/src/include/utils/sampling.h
@@ -17,11 +17,9 @@
 
 
 /* Random generator for sampling code */
-typedef unsigned short SamplerRandomState[3];
-
 extern void sampler_random_init_state(long seed,
-									  SamplerRandomState randstate);
-extern double sampler_random_fract(SamplerRandomState randstate);
+									  pg_prng_state *randstate);
+extern double sampler_random_fract(pg_prng_state *randstate);
 
 /* Block sampling methods */
 
@@ -32,7 +30,7 @@ typedef struct
 	int			n;				/* desired sample size */
 	BlockNumber t;				/* current block number */
 	int			m;				/* blocks selected so far */
-	SamplerRandomState randstate;	/* random generator state */
+	pg_prng_state randstate;	/* random generator state */
 } BlockSamplerData;
 
 typedef BlockSamplerData *BlockSampler;
@@ -46,8 +44,9 @@ extern BlockNumber BlockSampler_Next(BlockSampler bs);
 
 typedef struct
 {
-	double		W;
-	SamplerRandomState randstate;	/* random generator state */
+	double			W;
+	pg_prng_state	randstate;	/* random generator state */
+	bool			randstate_initialized;
 } ReservoirStateData;
 
 typedef ReservoirStateData *ReservoirState;
diff --git a/src/port/Makefile b/src/port/Makefile
index 52dbf5783f..9ff2056688 100644
--- a/src/port/Makefile
+++ b/src/port/Makefile
@@ -42,11 +42,11 @@ OBJS = \
 	$(PG_CRC32C_OBJS) \
 	bsearch_arg.o \
 	chklocale.o \
-	erand48.o \
 	inet_net_ntop.o \
 	noblock.o \
 	path.o \
 	pg_bitutils.o \
+	pg_prng.o \
 	pg_strong_random.o \
 	pgcheckdir.o \
 	pgmkdirp.o \
diff --git a/src/port/erand48.c b/src/port/erand48.c
deleted file mode 100644
index a82ea94220..0000000000
--- a/src/port/erand48.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/port/pg_prng.c b/src/port/pg_prng.c
new file mode 100644
index 0000000000..0ac5692e6a
--- /dev/null
+++ b/src/port/pg_prng.c
@@ -0,0 +1,179 @@
+/*
+ * Pseudo-Random Number Generator
+ *
+ * Previous version was the 1988 (?) rand48 standard with 48 bits state LCG,
+ * designed for running on 32 bits or even 16 bits architectures, and to produce
+ * 32 bit ints and floats (i.e. *not* doubles).
+ * The implied 16-bits unpacking/packing may have a detrimental performance impact
+ * on modern 64 bits architectures.
+ *
+ * We (probably) want:
+ * - NOT to invent a new design!
+ * - one reasonable default PRNG for all pg internal uses, currently this is
+ *   POSIX rand48.
+ * - something fast, close to rand48 (which basically does 2 arithmetic ops)
+ *   no need for something cryptographic though, which would imply slow
+ * - to produce 64 bits integers & doubles with a 52 bits mantissa
+ *   so state size > 64 bits.
+ * - a small state though, because we might generate quite a few of them
+ *   for different purposes so state size <= 256 or even <= 128 bits
+ * - the state to be aligned to whatever => 128 bits
+ * - 64 bits operations for efficiency on modern architectures,
+ *   but not 128 bits operations (yet:-).
+ * - not to depend on special hardware for speed (eg MMX/SSE/AES).
+ * - not something with obvious known and relevant defects.
+ * - not something with "rights" attached.
+ *
+ * These constraints reduce a lot the available options from
+ * https://en.wikipedia.org/wiki/List_of_random_number_generators
+ *
+ * LCG
+ * - POSIX rand48: m = 2⁴⁸, a = 25214903917, c = 11, extract 47…16
+ * - MMIX by Donald Knuth: m = 2⁶⁴, a = 6364136223846793005, c = 1442695040888963407
+ * - very bad on low bits, which MUST NOT BE USED
+ *   a 128 bits would do, but no clear standards, and it would imply 128-bit ops.
+ *
+ * Xor-Shift-Rotate PRNGs:
+ * Xoroshiro128+
+ * - fails statistical linearity tests in the low bits.
+ * Xoroshiro128**
+ *   https://prng.di.unimi.it/xoroshiro128starstar.c
+ * - mild and irrelevant issues: close to zero state, stats issues after some transforms
+ * Xoshiro256**
+ * - it looks good, but some issues (no big deal for pg usage IMO)
+ *   https://www.pcg-random.org/posts/a-quick-look-at-xoshiro256.html
+ *   mild problems getting out of closes-to-zero state
+ *
+ * MT19937-64
+ * + default in many places, standard
+ * - huge 2.5 KiB state (but TinyMT, 127 bits state space)
+ * - slow (but SFMT, twice as fast)
+ * - vulnerabilities on tinyMT?
+ *
+ * WELL
+ * - large state, 32-bit oriented
+ *
+ * ACORN Family
+ *   https://en.wikipedia.org/wiki/ACORN_(PRNG)
+ * - Fortran / double oriented, no C implementation found?!
+ * - medium large state "In practice we recommend using k > 10, M = 2^60 (for general application)
+ *   or M = 2^120 (for demanding applications requiring high-quality pseudo-random numbers
+ *   that will consistently pass all the tests in standard test packages such as TestU01)
+ *   and choose any odd value less than M for the seed."
+ *   k = 11 => 88 bytes/704-bits state, too large:-(
+ *
+ * PCG Family
+ * - https://www.pcg-random.org/
+ * - 32-bits design? while??
+ *
+ * Splitmix?
+ * Others?
+ */
+
+#include "c.h"
+#include <math.h>
+
+#define FIRST_BIT_MASK UINT64CONST(0x8000000000000000)
+#define HALF_BITS_MASK UINT64CONST(0x00000000FFFFFFFF)
+#define DMANTISSA_MASK UINT64CONST(0x000FFFFFFFFFFFFF)
+
+/* 64-bits rotate left */
+static inline uint64_t rotl(const uint64_t x, const int bits)
+{
+	return (x << bits) | (x >> (64 - bits));
+}
+
+/* another 64-bits generator is used for state initialization */
+static uint64_t splitmix64(uint64_t * state)
+{
+	/* state update */
+	uint64_t	val = (*state += UINT64CONST(0x9E3779B97f4A7C15));
+
+	/* value extraction */
+	val = (val ^ (val >> 30)) * UINT64CONST(0xBF58476D1CE4E5B9);
+	val = (val ^ (val >> 27)) * UINT64CONST(0x94D049BB133111EB);
+
+	return val ^ (val >> 31);
+}
+
+/* seed prng state from a 64 bits integer, ensuring non zero */
+void pg_prng_seed(pg_prng_state *state, uint64_t seed)
+{
+	state->s0 = splitmix64(&seed);
+	state->s1 = splitmix64(&seed);
+}
+
+/* seed with 53 bits (mantissa & sign) from a float */
+void pg_prng_fseed(pg_prng_state *state, double fseed)
+{
+	uint64 seed = (int64) (((double) DMANTISSA_MASK) * fseed);
+	pg_prng_seed(state, seed);
+}
+
+/* strong random seeding */
+void pg_prng_strong_seed(pg_prng_state *state)
+{
+	pg_strong_random((void *) state, sizeof(pg_prng_state));
+
+	/* avoid zero with Donald Knuth's LCG parameters */
+	if (unlikely(state->s0 == 0 && state->s1 == 0))
+	{
+		/* should it warn that something is amiss if we get there? */
+		pg_prng_state def = PG_PRNG_DEFAULT_STATE;
+		*state = def;
+	}
+}
+
+/* generator & state update */
+static uint64_t xoroshiro128ss(pg_prng_state *state)
+{
+	const uint64_t	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;
+}
+
+/* u64 generator */
+uint64_t pg_prng_u64(pg_prng_state *state)
+{
+	return xoroshiro128ss(state);
+}
+
+/* i64 generator */
+int64_t pg_prng_i64(pg_prng_state *state)
+{
+	return (int64_t) xoroshiro128ss(state);
+}
+
+/* u32 generator */
+uint32_t pg_prng_u32(pg_prng_state *state)
+{
+	const uint64_t v = xoroshiro128ss(state);
+	return (uint32_t) (((v >> 32) ^ v) & HALF_BITS_MASK);
+}
+
+/* i32 generator */
+int32_t pg_prng_i32(pg_prng_state *state)
+{
+	const uint64_t v = xoroshiro128ss(state);
+	return (int32_t) (((v >> 32) ^ v) & HALF_BITS_MASK);
+}
+
+/* double generator */
+double pg_prng_f64(pg_prng_state *state)
+{
+	uint64_t v = xoroshiro128ss(state);
+	return ldexp((double) (v & DMANTISSA_MASK), -52);
+}
+
+/* bool generator */
+bool pg_prng_bool(pg_prng_state *state)
+{
+	uint64_t v = xoroshiro128ss(state);
+	return (v & FIRST_BIT_MASK) == FIRST_BIT_MASK;
+}
diff --git a/src/port/random.c b/src/port/random.c
index 2dd59a0829..136ed9dd8a 100644
--- a/src/port/random.c
+++ b/src/port/random.c
@@ -1,7 +1,7 @@
 /*-------------------------------------------------------------------------
  *
  * random.c
- *	  random() wrapper
+ *	  random() and srandom() wrapper
  *
  * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
@@ -15,11 +15,17 @@
 
 #include "c.h"
 
-#include <math.h>
+/* global shared state: not thread safe! */
+static pg_prng_state state = PG_PRNG_DEFAULT_SEED;
 
+void
+srandom(unsigned int seed)
+{
+	pg_prng_seed(&state, (uint64_t) seed);
+}
 
 long
 random(void)
 {
-	return pg_lrand48();
+	return pg_prng_i32(&state);
 }
diff --git a/src/port/srandom.c b/src/port/srandom.c
deleted file mode 100644
index cf1007b2ef..0000000000
--- a/src/port/srandom.c
+++ /dev/null
@@ -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);
-}
#2Tomas Vondra
tomas.vondra@enterprisedb.com
In reply to: Fabien COELHO (#1)
Re: rand48 replacement

Hi,

On 5/24/21 12:31 PM, Fabien COELHO wrote:

Hello pg-devs,

I have given a go at proposing a replacement for rand48.

So what is the motivation for replacing rand48? Speed, quality of
produced random numbers, features rand48 can't provide, or what?

POSIX 1988 (?) rand48 is a LCG PRNG designed to generate 32 bits
integers or floats based on a 48 bits state on 16 or 32 bits
architectures. LCG cycles on the low bits, which can be quite annoying.
Given that we run on 64 bits architectures and that we need to generate
64 bits ints or doubles, IMHO it makes very little sense to stick to that.

We should (probably) want:
�- one reasonable default PRNG for all pg internal uses.
�- NOT to invent a new design!
�- something fast, close to rand48 (which basically does 2 arithmetic
�� ops, so it is hard to compete)
�� no need for something cryptographic though, which would imply slow
�- to produce 64 bits integers & doubles with a 52 bits mantissa,
�� so state size > 64 bits.
�- a small state though, because we might generate quite a few of them
�� for different purposes so state size <= 256 or even <= 128 bits
�- the state to be aligned to whatever => 128 bits
�- 64 bits operations for efficiency on modern architectures,
�� but not 128 bits operations.
�- not to depend on special hardware for speed (eg MMX/SSE/AES).
�- not something with obvious known and relevant defects.
�- not something with "rights" attached.

These constraints reduce drastically the available options from
https://en.wikipedia.org/wiki/List_of_random_number_generators

The attached patch removes "rand48" and adds a "pg_prng" implementation
based on xoroshiro128ss, and replaces it everywhere. In pgbench, the non
portable double-relying code is replaced by hopefully portable ints. The
interface makes it easy to replace the underlying PRNG if something else
is desired.

xoroshiro seems reasonable. How does it compare to rand48? Does it need
much less/more state, is it faster/slower, etc.? I'd expect that it
produces better random sequence, considering rand48 is a LCG, which is
fairly simple decades old design.

regards

--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#3Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Tomas Vondra (#2)
Re: rand48 replacement

Hello Tomas,

I have given a go at proposing a replacement for rand48.

So what is the motivation for replacing rand48? Speed, quality of produced
random numbers, features rand48 can't provide, or what?

Speed can only be near rand48, see below. Quality (eg no trivial cycles,
does not fail too quickly on statistical tests) and soundness (the point
of generating 52-64 bits of data out of 48 bits, which means that only a
small part of the target space is covered, fails me). Also, I like having
a implementation independent interface (current rand48 tells the name of
the algorithm everywhere and the "uint16[3]" state type is hardcoded in
several places).

POSIX 1988 (?) rand48 is a LCG PRNG designed to generate 32 bits integers
or floats based on a 48 bits state on 16 or 32 bits architectures. LCG
cycles on the low bits, which can be quite annoying. Given that we run on
64 bits architectures and that we need to generate 64 bits ints or doubles,
IMHO it makes very little sense to stick to that.

We should (probably) want:
�- one reasonable default PRNG for all pg internal uses.
�- NOT to invent a new design!
�- something fast, close to rand48 (which basically does 2 arithmetic
�� ops, so it is hard to compete)
�� no need for something cryptographic though, which would imply slow
�- to produce 64 bits integers & doubles with a 52 bits mantissa,
�� so state size > 64 bits.
�- a small state though, because we might generate quite a few of them
�� for different purposes so state size <= 256 or even <= 128 bits
�- the state to be aligned to whatever => 128 bits
�- 64 bits operations for efficiency on modern architectures,
�� but not 128 bits operations.
�- not to depend on special hardware for speed (eg MMX/SSE/AES).
�- not something with obvious known and relevant defects.
�- not something with "rights" attached.

These constraints reduce drastically the available options from
https://en.wikipedia.org/wiki/List_of_random_number_generators

The attached patch removes "rand48" and adds a "pg_prng" implementation
based on xoroshiro128ss, and replaces it everywhere. In pgbench, the non
portable double-relying code is replaced by hopefully portable ints. The
interface makes it easy to replace the underlying PRNG if something else is
desired.

xoroshiro seems reasonable. How does it compare to rand48? Does it need much
less/more state, is it faster/slower, etc.?

Basically any PRNG should be slower/comparable than rand48 because it only
does 2 arithmetic ops, you cannot do much less when trying to steer bits.
However because of the 16-bits unpacking/packing on 64 bits architecture
there is some room for additional useful ops, so in the end from the end
user the performance is only about 5% loweR.

State is 16 bytes vs 6 bytes for rand48. This is ok for generating 8 bytes
per round and is still quite small.

I'd expect that it produces better random sequence, considering rand48
is a LCG, which is fairly simple decades old design.

Yep, it does not cycle trivially on low bits compared to an LCG (eg odd ->
even -> odd -> even -> ...), e.g. if you have the bad idea to do "% 2" on
an LCG to extract a bool you just alternate.

To summarize:
- better software engineering
- similar speed (slightly slower)
- better statistical quality
- quite small state
- soundness

--
Fabien.

#4Andrey Borodin
x4mmm@yandex-team.ru
In reply to: Fabien COELHO (#1)
Re: rand48 replacement

24 мая 2021 г., в 15:31, Fabien COELHO <coelho@cri.ensmp.fr> написал(а):

- NOT to invent a new design!

Radical version of this argument would be to use de-facto standard and ubiquitous MT19937.
Though, I suspect, it's not optimal solution to the date.

Best regards, Andrey Borodin.

#5Aleksander Alekseev
aleksander@timescale.com
In reply to: Fabien COELHO (#3)
1 attachment(s)
Re: rand48 replacement

Hi Fabien,

To summarize:
- better software engineering
- similar speed (slightly slower)
- better statistical quality
- quite small state
- soundness

Personally, I think your patch is great. Speaking of the speed I
believe we should consider the performance of the entire DBMS in
typical scenarios, not the performance of the single procedure. I'm
pretty sure in these terms the impact of your patch is neglectable
now, and almost certainly beneficial in the long term because of
better randomness.

While reviewing your patch I noticed that you missed
test_integerset.c. Here is an updated patch.

--
Best regards,
Aleksander Alekseev

Attachments:

prng-2.patchapplication/x-patch; name=prng-2.patchDownload
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 2c2f149fb0..146b524076 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -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]);
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index c48a421e88..174859ad64 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -5094,7 +5094,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]);
 		}
diff --git a/contrib/tsm_system_rows/tsm_system_rows.c b/contrib/tsm_system_rows/tsm_system_rows.c
index 4996612902..1a46d4b143 100644
--- a/contrib/tsm_system_rows/tsm_system_rows.c
+++ b/contrib/tsm_system_rows/tsm_system_rows.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/contrib/tsm_system_time/tsm_system_time.c b/contrib/tsm_system_time/tsm_system_time.c
index 788d8f9a68..36acc6c106 100644
--- a/contrib/tsm_system_time/tsm_system_time.c
+++ b/contrib/tsm_system_time/tsm_system_time.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 426c1e6710..092d53d28a 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1294,7 +1294,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 					 * Found a suitable tuple, so save it, replacing one old
 					 * tuple at random
 					 */
-					int			k = (int) (targrows * sampler_random_fract(rstate.randstate));
+					int			k = (int) (targrows * sampler_random_fract(&rstate.randstate));
 
 					Assert(k >= 0 && k < targrows);
 					heap_freetuple(rows[k]);
diff --git a/src/backend/optimizer/geqo/geqo_random.c b/src/backend/optimizer/geqo/geqo_random.c
index f21bc047e6..8b42a9ffba 100644
--- a/src/backend/optimizer/geqo/geqo_random.c
+++ b/src/backend/optimizer/geqo/geqo_random.c
@@ -21,14 +21,7 @@ geqo_set_seed(PlannerInfo *root, double seed)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	/*
-	 * XXX. This seeding algorithm could certainly be improved - but it is not
-	 * critical to do so.
-	 */
-	memset(private->random_state, 0, sizeof(private->random_state));
-	memcpy(private->random_state,
-		   &seed,
-		   Min(sizeof(private->random_state), sizeof(seed)));
+	pg_prng_fseed(&private->random_state, seed);
 }
 
 double
@@ -36,5 +29,5 @@ geqo_rand(PlannerInfo *root)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	return pg_erand48(private->random_state);
+	return pg_prng_f64(&private->random_state);
 }
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 098bbb372b..37fcd645e0 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -65,7 +65,7 @@ float8		degree_c_one = 1.0;
 
 /* State for drandom() and setseed() */
 static bool drandom_seed_set = false;
-static unsigned short drandom_seed[3] = {0, 0, 0};
+static pg_prng_state drandom_seed = PG_PRNG_DEFAULT_STATE;
 
 /* Local function prototypes */
 static double sind_q1(double x);
@@ -2762,22 +2762,20 @@ drandom(PG_FUNCTION_ARGS)
 		 * Should that fail for some reason, we fall back on a lower-quality
 		 * seed based on current time and PID.
 		 */
-		if (!pg_strong_random(drandom_seed, sizeof(drandom_seed)))
+		if (!pg_strong_random(&drandom_seed, sizeof(drandom_seed)))
 		{
 			TimestampTz now = GetCurrentTimestamp();
 			uint64		iseed;
 
 			/* Mix the PID with the most predictable bits of the timestamp */
 			iseed = (uint64) now ^ ((uint64) MyProcPid << 32);
-			drandom_seed[0] = (unsigned short) iseed;
-			drandom_seed[1] = (unsigned short) (iseed >> 16);
-			drandom_seed[2] = (unsigned short) (iseed >> 32);
+			pg_prng_seed(&drandom_seed, iseed);
 		}
 		drandom_seed_set = true;
 	}
 
-	/* pg_erand48 produces desired result range [0.0 - 1.0) */
-	result = pg_erand48(drandom_seed);
+	/* produces desired result range [0.0 - 1.0) */
+	result = pg_prng_f64(&drandom_seed);
 
 	PG_RETURN_FLOAT8(result);
 }
@@ -2790,7 +2788,6 @@ Datum
 setseed(PG_FUNCTION_ARGS)
 {
 	float8		seed = PG_GETARG_FLOAT8(0);
-	uint64		iseed;
 
 	if (seed < -1 || seed > 1 || isnan(seed))
 		ereport(ERROR,
@@ -2798,11 +2795,7 @@ setseed(PG_FUNCTION_ARGS)
 				 errmsg("setseed parameter %g is out of allowed range [-1,1]",
 						seed)));
 
-	/* Use sign bit + 47 fractional bits to fill drandom_seed[] */
-	iseed = (int64) (seed * (float8) UINT64CONST(0x7FFFFFFFFFFF));
-	drandom_seed[0] = (unsigned short) iseed;
-	drandom_seed[1] = (unsigned short) (iseed >> 16);
-	drandom_seed[2] = (unsigned short) (iseed >> 32);
+	pg_prng_fseed(&drandom_seed, seed);
 	drandom_seed_set = true;
 
 	PG_RETURN_VOID();
diff --git a/src/backend/utils/misc/sampling.c b/src/backend/utils/misc/sampling.c
index 0c327e823f..7b59128878 100644
--- a/src/backend/utils/misc/sampling.c
+++ b/src/backend/utils/misc/sampling.c
@@ -49,7 +49,7 @@ BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize,
 	bs->t = 0;					/* blocks scanned so far */
 	bs->m = 0;					/* blocks selected so far */
 
-	sampler_random_init_state(randseed, bs->randstate);
+	sampler_random_init_state(randseed, &bs->randstate);
 
 	return Min(bs->n, bs->N);
 }
@@ -98,7 +98,7 @@ BlockSampler_Next(BlockSampler bs)
 	 * less than k, which means that we cannot fail to select enough blocks.
 	 *----------
 	 */
-	V = sampler_random_fract(bs->randstate);
+	V = sampler_random_fract(&bs->randstate);
 	p = 1.0 - (double) k / (double) K;
 	while (V < p)
 	{
@@ -136,10 +136,10 @@ reservoir_init_selection_state(ReservoirState rs, int n)
 	 * Reservoir sampling is not used anywhere where it would need to return
 	 * repeatable results so we can initialize it randomly.
 	 */
-	sampler_random_init_state(random(), rs->randstate);
+	sampler_random_init_state(random(), &rs->randstate);
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	rs->W = exp(-log(sampler_random_fract(rs->randstate)) / n);
+	rs->W = exp(-log(sampler_random_fract(&rs->randstate)) / n);
 }
 
 double
@@ -154,7 +154,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 		double		V,
 					quot;
 
-		V = sampler_random_fract(rs->randstate);	/* Generate V */
+		V = sampler_random_fract(&rs->randstate);	/* Generate V */
 		S = 0;
 		t += 1;
 		/* Note: "num" in Vitter's code is always equal to t - n */
@@ -186,7 +186,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 						tmp;
 
 			/* Generate U and X */
-			U = sampler_random_fract(rs->randstate);
+			U = sampler_random_fract(&rs->randstate);
 			X = t * (W - 1.0);
 			S = floor(X);		/* S is tentatively set to floor(X) */
 			/* Test if U <= h(S)/cg(X) in the manner of (6.3) */
@@ -215,7 +215,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 				y *= numer / denom;
 				denom -= 1;
 			}
-			W = exp(-log(sampler_random_fract(rs->randstate)) / n); /* Generate W in advance */
+			W = exp(-log(sampler_random_fract(&rs->randstate)) / n); /* Generate W in advance */
 			if (exp(log(y) / n) <= (t + X) / t)
 				break;
 		}
@@ -230,24 +230,22 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
  *----------
  */
 void
-sampler_random_init_state(long seed, SamplerRandomState randstate)
+sampler_random_init_state(long seed, pg_prng_state *randstate)
 {
-	randstate[0] = 0x330e;		/* same as pg_erand48, but could be anything */
-	randstate[1] = (unsigned short) seed;
-	randstate[2] = (unsigned short) (seed >> 16);
+	pg_prng_seed(randstate, (uint64_t) seed);
 }
 
 /* Select a random value R uniformly distributed in (0 - 1) */
 double
-sampler_random_fract(SamplerRandomState randstate)
+sampler_random_fract(pg_prng_state *randstate)
 {
 	double		res;
 
-	/* pg_erand48 returns a value in [0.0 - 1.0), so we must reject 0 */
+	/* pg_prng_f64 returned value in [0.0 - 1.0), so we must reject 0.0 */
 	do
 	{
-		res = pg_erand48(randstate);
-	} while (res == 0.0);
+		res = pg_prng_f64(randstate);
+	} while (unlikely(res == 0.0));
 	return res;
 }
 
@@ -266,22 +264,28 @@ double
 anl_random_fract(void)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(random(), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* and compute a random fraction */
-	return sampler_random_fract(oldrs.randstate);
+	return sampler_random_fract(&oldrs.randstate);
 }
 
 double
 anl_init_selection_state(int n)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(random(), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	return exp(-log(sampler_random_fract(oldrs.randstate)) / n);
+	return exp(-log(sampler_random_fract(&oldrs.randstate)) / n);
 }
 
 double
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index dc84b7b9b7..a459424a63 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -342,16 +342,8 @@ typedef struct StatsData
 	SimpleStats lag;
 } StatsData;
 
-/*
- * Struct to keep random state.
- */
-typedef struct RandomState
-{
-	unsigned short xseed[3];
-} RandomState;
-
 /* 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 */
 static THREAD_BARRIER_T barrier;
@@ -453,7 +445,7 @@ typedef struct
 	 * Separate randomness for each client. This is used for random functions
 	 * 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			command;		/* command number in script */
@@ -490,9 +482,9 @@ typedef struct
 	 * random state to make all of them independent of each other and
 	 * therefore deterministic at the thread level.
 	 */
-	RandomState ts_choose_rs;	/* random state for selecting a script */
-	RandomState ts_throttle_rs; /* random state for transaction throttling */
-	RandomState ts_sample_rs;	/* random state for log sampling */
+	pg_prng_state	ts_choose_rs;	/* random state for selecting a script */
+	pg_prng_state	ts_throttle_rs; /* random state for transaction throttling */
+	pg_prng_state	ts_sample_rs;	/* random state for log sampling */
 
 	int64		throttle_trigger;	/* previous/next throttling (us) */
 	FILE	   *logfile;		/* where to log, or NULL */
@@ -890,42 +882,34 @@ 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.
  */
 static void
-initRandomState(RandomState *random_state)
+initRandomState(pg_prng_state *state)
 {
-	random_state->xseed[0] = (unsigned short)
-		(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);
+	pg_prng_seed(state, pg_prng_u64(&base_random_sequence));
 }
 
+
 /*
  * Random number generator: uniform distribution from min to max inclusive.
  *
  * Although the limits are expressed as int64, you can't generate the full
  * int64 range in one call, because the difference of the limits mustn't
- * overflow int64.  In practice it's unwise to ask for more than an int32
- * range, because of the limited precision of pg_erand48().
+ * overflow int64.
  */
 static int64
-getrand(RandomState *random_state, int64 min, int64 max)
+getrand(pg_prng_state *state, int64 min, int64 max)
 {
 	/*
 	 * 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));
+	int64	size = max - min + 1,
+			val = pg_prng_u64(state) & UINT64CONST(0x7FFFFFFFFFFFFFFF);
+	return min + (int64) (val % size);
 }
 
 /*
@@ -934,8 +918,7 @@ getrand(RandomState *random_state, int64 min, int64 max)
  * value is exp(-parameter).
  */
 static int64
-getExponentialRand(RandomState *random_state, int64 min, int64 max,
-				   double parameter)
+getExponentialRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		cut,
 				uniform,
@@ -945,7 +928,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 	Assert(parameter > 0.0);
 	cut = exp(-parameter);
 	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	uniform = 1.0 - pg_prng_f64(state);
 
 	/*
 	 * inner expression in (cut, 1] (if parameter > 0), rand in [0, 1)
@@ -958,8 +941,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 
 /* random number generator: gaussian distribution from min to max inclusive */
 static int64
-getGaussianRand(RandomState *random_state, int64 min, int64 max,
-				double parameter)
+getGaussianRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		stdev;
 	double		rand;
@@ -982,13 +964,13 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
 	do
 	{
 		/*
-		 * pg_erand48 generates [0,1), but for the basic version of the
+		 * pg_prng_f64 generates [0,1), but for the basic version of the
 		 * Box-Muller transform the two uniformly distributed random numbers
 		 * are expected in (0, 1] (see
 		 * https://en.wikipedia.org/wiki/Box-Muller_transform)
 		 */
-		double		rand1 = 1.0 - pg_erand48(random_state->xseed);
-		double		rand2 = 1.0 - pg_erand48(random_state->xseed);
+		double		rand1 = 1.0 - pg_prng_f64(state);
+		double		rand2 = 1.0 - pg_prng_f64(state);
 
 		/* Box-Muller basic form transform */
 		double		var_sqrt = sqrt(-2.0 * log(rand1));
@@ -1018,7 +1000,7 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
  * not be one.
  */
 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
@@ -1026,8 +1008,8 @@ getPoissonRand(RandomState *random_state, double center)
 	 */
 	double		uniform;
 
-	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	/* pseudo-random value in [0, 1), uniform in (0, 1] */
+	uniform = 1.0 - pg_prng_f64(state);
 
 	return (int64) (-log(uniform) * center + 0.5);
 }
@@ -1040,7 +1022,7 @@ getPoissonRand(RandomState *random_state, double center)
  * This works for s > 1.0, but may perform badly for s very close to 1.0.
  */
 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		x,
@@ -1055,8 +1037,8 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 	while (true)
 	{
 		/* random variates */
-		u = pg_erand48(random_state->xseed);
-		v = pg_erand48(random_state->xseed);
+		u = pg_prng_f64(state);
+		v = pg_prng_f64(state);
 
 		x = floor(pow(u, -1.0 / (s - 1.0)));
 
@@ -1070,14 +1052,14 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 
 /* random number generator: zipfian distribution from min to max inclusive */
 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;
 
 	/* abort if parameter is invalid */
 	Assert(MIN_ZIPFIAN_PARAM <= s && s <= MAX_ZIPFIAN_PARAM);
 
-	return min - 1 + computeIterativeZipfian(random_state, n, s);
+	return min - 1 + computeIterativeZipfian(state, n, s);
 }
 
 /*
@@ -1134,7 +1116,7 @@ getHashMurmur2(int64 val, uint64 seed)
  * For small sizes, this generates each of the (size!) possible permutations
  * of integers in the range [0, size) with roughly equal probability.  Once
  * the size is larger than 20, the number of possible permutations exceeds the
- * number of distinct states of the internal pseudorandom number generators,
+ * number of distinct states of the internal pseudorandom number generator,
  * and so not all possible permutations can be generated, but the permutations
  * chosen should continue to give the appearance of being random.
  *
@@ -1144,25 +1126,19 @@ getHashMurmur2(int64 val, uint64 seed)
 static int64
 permute(const int64 val, const int64 isize, const int64 seed)
 {
-	RandomState random_state1;
-	RandomState random_state2;
-	uint64		size;
-	uint64		v;
-	int			masklen;
-	uint64		mask;
-	int			i;
+	/* using a high-end PRNG is probably overkill */
+	pg_prng_state	state;
+	uint64			size;
+	uint64			v;
+	int				masklen;
+	uint64			mask;
+	int				i;
 
 	if (isize < 2)
 		return 0;				/* nothing to permute */
 
-	/* Initialize a pair of random states using the seed */
-	random_state1.xseed[0] = seed & 0xFFFF;
-	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;
+	/* Initialize prng state using the seed */
+	pg_prng_seed(&state, (uint64) seed);
 
 	/* Computations are performed on unsigned values */
 	size = (uint64) isize;
@@ -1208,8 +1184,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 					t;
 
 		/* Random multiply (by an odd number), XOR and rotate of lower half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		if (v <= mask)
 		{
 			v = ((v * m) ^ r) & mask;
@@ -1217,8 +1193,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random multiply (by an odd number), XOR and rotate of upper half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		t = size - 1 - v;
 		if (t <= mask)
 		{
@@ -1228,7 +1204,7 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random offset */
-		r = (uint64) getrand(&random_state2, 0, size - 1);
+		r = pg_prng_u64(&state) % (size - 1);
 		v = (v + r) % size;
 	}
 
@@ -3787,7 +3763,7 @@ doLog(TState *thread, CState *st,
 	 * to the random sample.
 	 */
 	if (sample_rate != 0.0 &&
-		pg_erand48(thread->ts_sample_rs.xseed) > sample_rate)
+		pg_prng_f64(&thread->ts_sample_rs) > sample_rate)
 		return;
 
 	/* should we aggregate the results or not? */
@@ -5681,12 +5657,11 @@ set_random_seed(const char *seed)
 
 	if (seed != NULL)
 		pg_log_info("setting random seed to %llu", (unsigned long long) iseed);
+
 	random_seed = iseed;
 
 	/* Fill base_random_sequence with low-order bits of seed */
-	base_random_sequence.xseed[0] = iseed & 0xFFFF;
-	base_random_sequence.xseed[1] = (iseed >> 16) & 0xFFFF;
-	base_random_sequence.xseed[2] = (iseed >> 32) & 0xFFFF;
+	pg_prng_seed(&base_random_sequence, (uint64_t) iseed);
 
 	return true;
 }
@@ -6369,9 +6344,7 @@ main(int argc, char **argv)
 	/* set default seed for hash functions */
 	if (lookupVariable(&state[0], "default_seed") == NULL)
 	{
-		uint64		seed =
-		((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) |
-		(((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) << 32);
+		uint64		seed = pg_prng_u64(&base_random_sequence);
 
 		for (i = 0; i < nclients; i++)
 			if (!putVariableInt(&state[i], "startup", "default_seed", (int64) seed))
diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl
index 9cc5270e02..9f0eeef997 100644
--- a/src/bin/pgbench/t/001_pgbench_with_server.pl
+++ b/src/bin/pgbench/t/001_pgbench_with_server.pl
@@ -427,9 +427,9 @@ pgbench(
 
 		# After explicit seeding, the four random checks (1-3,20) are
 		# deterministic
-		qr{command=1.: int 13\b},      # uniform random
-		qr{command=2.: int 116\b},     # exponential random
-		qr{command=3.: int 1498\b},    # gaussian random
+		qr{command=1.: int 10\b},      # uniform random
+		qr{command=2.: int 100\b},     # exponential random
+		qr{command=3.: int 1526\b},    # gaussian random
 		qr{command=4.: int 4\b},
 		qr{command=5.: int 5\b},
 		qr{command=6.: int 6\b},
@@ -496,6 +496,7 @@ pgbench(
 		qr{command=109.: boolean true\b},
 		qr{command=110.: boolean true\b},
 		qr{command=111.: boolean true\b},
+		qr{command=113.: boolean true\b},
 	],
 	'pgbench expressions',
 	{
@@ -639,8 +640,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))
 -- actual values
 \set t debug(permute(:v, 1) = 0)
-\set t debug(permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1 and \
-             permute(0, 2, 5435) = 1 and permute(1, 2, 5435) = 0)
+\set t debug(permute(0, 2, 5431) = 1 and permute(1, 2, 5431) = 0 and \
+             permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1)
+-- 63 bits tests for checking permute portability across architectures
+\set size debug(:max - 10)
+\set t debug(permute(:size-1, :size, 5432) = 4352241160009873020 and \
+             permute(:size-2, :size, 5432) = 4990004119691638710 and \
+             permute(:size-3, :size, 5432) = 5868029867256127549 and \
+             permute(:size-4, :size, 5432) = 1238830324886341378 and \
+             permute(:size-5, :size, 5432) = 7415914367102906897 and \
+             permute(:size-6, :size, 5432) = 3214501037984818995 and \
+             permute(:size-7, :size, 5432) =  600660351261124695)
 }
 	});
 
diff --git a/src/include/optimizer/geqo.h b/src/include/optimizer/geqo.h
index 24dcdfb6cc..75bfada4a1 100644
--- a/src/include/optimizer/geqo.h
+++ b/src/include/optimizer/geqo.h
@@ -72,8 +72,8 @@ extern double Geqo_seed;		/* 0 .. 1 */
  */
 typedef struct
 {
-	List	   *initial_rels;	/* the base relations we are joining */
-	unsigned short random_state[3]; /* state for pg_erand48() */
+	List		   *initial_rels;	/* the base relations we are joining */
+	pg_prng_state	random_state;   /* PRNG state */
 } GeqoPrivateData;
 
 
diff --git a/src/include/port.h b/src/include/port.h
index 82f63de325..a73c944c91 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -356,10 +356,24 @@ extern int	gettimeofday(struct timeval *tp, struct timezone *tzp);
 #define pgoff_t off_t
 #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);
+/* PRNG: Pseudo-Random Number Generator */
+typedef struct pg_prng_state {
+	uint64_t	s0, s1;
+} pg_prng_state;
+
+/* use Donald Knuth's LCG constants */
+#define PG_PRNG_DEFAULT_STATE \
+	{ UINT64CONST(0x5851F42D4C957F2D), UINT64CONST(0x14057B7EF767814F) }
+
+extern void pg_prng_seed(pg_prng_state *state, uint64_t seed);
+extern void pg_prng_fseed(pg_prng_state *state, double fseed);
+extern void pg_prng_strong_seed(pg_prng_state *state);
+extern uint64_t pg_prng_u64(pg_prng_state *state);
+extern int64_t pg_prng_i64(pg_prng_state *state);
+extern uint32_t pg_prng_u32(pg_prng_state *state);
+extern int32_t pg_prng_i32(pg_prng_state *state);
+extern double pg_prng_f64(pg_prng_state *state);
+extern bool pg_prng_bool(pg_prng_state *state);
 
 #ifndef HAVE_FLS
 extern int	fls(int mask);
diff --git a/src/include/utils/sampling.h b/src/include/utils/sampling.h
index a58d14281b..97cd9b7aef 100644
--- a/src/include/utils/sampling.h
+++ b/src/include/utils/sampling.h
@@ -17,11 +17,9 @@
 
 
 /* Random generator for sampling code */
-typedef unsigned short SamplerRandomState[3];
-
 extern void sampler_random_init_state(long seed,
-									  SamplerRandomState randstate);
-extern double sampler_random_fract(SamplerRandomState randstate);
+									  pg_prng_state *randstate);
+extern double sampler_random_fract(pg_prng_state *randstate);
 
 /* Block sampling methods */
 
@@ -32,7 +30,7 @@ typedef struct
 	int			n;				/* desired sample size */
 	BlockNumber t;				/* current block number */
 	int			m;				/* blocks selected so far */
-	SamplerRandomState randstate;	/* random generator state */
+	pg_prng_state randstate;	/* random generator state */
 } BlockSamplerData;
 
 typedef BlockSamplerData *BlockSampler;
@@ -46,8 +44,9 @@ extern BlockNumber BlockSampler_Next(BlockSampler bs);
 
 typedef struct
 {
-	double		W;
-	SamplerRandomState randstate;	/* random generator state */
+	double			W;
+	pg_prng_state	randstate;	/* random generator state */
+	bool			randstate_initialized;
 } ReservoirStateData;
 
 typedef ReservoirStateData *ReservoirState;
diff --git a/src/port/Makefile b/src/port/Makefile
index 52dbf5783f..9ff2056688 100644
--- a/src/port/Makefile
+++ b/src/port/Makefile
@@ -42,11 +42,11 @@ OBJS = \
 	$(PG_CRC32C_OBJS) \
 	bsearch_arg.o \
 	chklocale.o \
-	erand48.o \
 	inet_net_ntop.o \
 	noblock.o \
 	path.o \
 	pg_bitutils.o \
+	pg_prng.o \
 	pg_strong_random.o \
 	pgcheckdir.o \
 	pgmkdirp.o \
diff --git a/src/port/erand48.c b/src/port/erand48.c
deleted file mode 100644
index a82ea94220..0000000000
--- a/src/port/erand48.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/port/pg_prng.c b/src/port/pg_prng.c
new file mode 100644
index 0000000000..0ac5692e6a
--- /dev/null
+++ b/src/port/pg_prng.c
@@ -0,0 +1,179 @@
+/*
+ * Pseudo-Random Number Generator
+ *
+ * Previous version was the 1988 (?) rand48 standard with 48 bits state LCG,
+ * designed for running on 32 bits or even 16 bits architectures, and to produce
+ * 32 bit ints and floats (i.e. *not* doubles).
+ * The implied 16-bits unpacking/packing may have a detrimental performance impact
+ * on modern 64 bits architectures.
+ *
+ * We (probably) want:
+ * - NOT to invent a new design!
+ * - one reasonable default PRNG for all pg internal uses, currently this is
+ *   POSIX rand48.
+ * - something fast, close to rand48 (which basically does 2 arithmetic ops)
+ *   no need for something cryptographic though, which would imply slow
+ * - to produce 64 bits integers & doubles with a 52 bits mantissa
+ *   so state size > 64 bits.
+ * - a small state though, because we might generate quite a few of them
+ *   for different purposes so state size <= 256 or even <= 128 bits
+ * - the state to be aligned to whatever => 128 bits
+ * - 64 bits operations for efficiency on modern architectures,
+ *   but not 128 bits operations (yet:-).
+ * - not to depend on special hardware for speed (eg MMX/SSE/AES).
+ * - not something with obvious known and relevant defects.
+ * - not something with "rights" attached.
+ *
+ * These constraints reduce a lot the available options from
+ * https://en.wikipedia.org/wiki/List_of_random_number_generators
+ *
+ * LCG
+ * - POSIX rand48: m = 2⁴⁸, a = 25214903917, c = 11, extract 47…16
+ * - MMIX by Donald Knuth: m = 2⁶⁴, a = 6364136223846793005, c = 1442695040888963407
+ * - very bad on low bits, which MUST NOT BE USED
+ *   a 128 bits would do, but no clear standards, and it would imply 128-bit ops.
+ *
+ * Xor-Shift-Rotate PRNGs:
+ * Xoroshiro128+
+ * - fails statistical linearity tests in the low bits.
+ * Xoroshiro128**
+ *   https://prng.di.unimi.it/xoroshiro128starstar.c
+ * - mild and irrelevant issues: close to zero state, stats issues after some transforms
+ * Xoshiro256**
+ * - it looks good, but some issues (no big deal for pg usage IMO)
+ *   https://www.pcg-random.org/posts/a-quick-look-at-xoshiro256.html
+ *   mild problems getting out of closes-to-zero state
+ *
+ * MT19937-64
+ * + default in many places, standard
+ * - huge 2.5 KiB state (but TinyMT, 127 bits state space)
+ * - slow (but SFMT, twice as fast)
+ * - vulnerabilities on tinyMT?
+ *
+ * WELL
+ * - large state, 32-bit oriented
+ *
+ * ACORN Family
+ *   https://en.wikipedia.org/wiki/ACORN_(PRNG)
+ * - Fortran / double oriented, no C implementation found?!
+ * - medium large state "In practice we recommend using k > 10, M = 2^60 (for general application)
+ *   or M = 2^120 (for demanding applications requiring high-quality pseudo-random numbers
+ *   that will consistently pass all the tests in standard test packages such as TestU01)
+ *   and choose any odd value less than M for the seed."
+ *   k = 11 => 88 bytes/704-bits state, too large:-(
+ *
+ * PCG Family
+ * - https://www.pcg-random.org/
+ * - 32-bits design? while??
+ *
+ * Splitmix?
+ * Others?
+ */
+
+#include "c.h"
+#include <math.h>
+
+#define FIRST_BIT_MASK UINT64CONST(0x8000000000000000)
+#define HALF_BITS_MASK UINT64CONST(0x00000000FFFFFFFF)
+#define DMANTISSA_MASK UINT64CONST(0x000FFFFFFFFFFFFF)
+
+/* 64-bits rotate left */
+static inline uint64_t rotl(const uint64_t x, const int bits)
+{
+	return (x << bits) | (x >> (64 - bits));
+}
+
+/* another 64-bits generator is used for state initialization */
+static uint64_t splitmix64(uint64_t * state)
+{
+	/* state update */
+	uint64_t	val = (*state += UINT64CONST(0x9E3779B97f4A7C15));
+
+	/* value extraction */
+	val = (val ^ (val >> 30)) * UINT64CONST(0xBF58476D1CE4E5B9);
+	val = (val ^ (val >> 27)) * UINT64CONST(0x94D049BB133111EB);
+
+	return val ^ (val >> 31);
+}
+
+/* seed prng state from a 64 bits integer, ensuring non zero */
+void pg_prng_seed(pg_prng_state *state, uint64_t seed)
+{
+	state->s0 = splitmix64(&seed);
+	state->s1 = splitmix64(&seed);
+}
+
+/* seed with 53 bits (mantissa & sign) from a float */
+void pg_prng_fseed(pg_prng_state *state, double fseed)
+{
+	uint64 seed = (int64) (((double) DMANTISSA_MASK) * fseed);
+	pg_prng_seed(state, seed);
+}
+
+/* strong random seeding */
+void pg_prng_strong_seed(pg_prng_state *state)
+{
+	pg_strong_random((void *) state, sizeof(pg_prng_state));
+
+	/* avoid zero with Donald Knuth's LCG parameters */
+	if (unlikely(state->s0 == 0 && state->s1 == 0))
+	{
+		/* should it warn that something is amiss if we get there? */
+		pg_prng_state def = PG_PRNG_DEFAULT_STATE;
+		*state = def;
+	}
+}
+
+/* generator & state update */
+static uint64_t xoroshiro128ss(pg_prng_state *state)
+{
+	const uint64_t	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;
+}
+
+/* u64 generator */
+uint64_t pg_prng_u64(pg_prng_state *state)
+{
+	return xoroshiro128ss(state);
+}
+
+/* i64 generator */
+int64_t pg_prng_i64(pg_prng_state *state)
+{
+	return (int64_t) xoroshiro128ss(state);
+}
+
+/* u32 generator */
+uint32_t pg_prng_u32(pg_prng_state *state)
+{
+	const uint64_t v = xoroshiro128ss(state);
+	return (uint32_t) (((v >> 32) ^ v) & HALF_BITS_MASK);
+}
+
+/* i32 generator */
+int32_t pg_prng_i32(pg_prng_state *state)
+{
+	const uint64_t v = xoroshiro128ss(state);
+	return (int32_t) (((v >> 32) ^ v) & HALF_BITS_MASK);
+}
+
+/* double generator */
+double pg_prng_f64(pg_prng_state *state)
+{
+	uint64_t v = xoroshiro128ss(state);
+	return ldexp((double) (v & DMANTISSA_MASK), -52);
+}
+
+/* bool generator */
+bool pg_prng_bool(pg_prng_state *state)
+{
+	uint64_t v = xoroshiro128ss(state);
+	return (v & FIRST_BIT_MASK) == FIRST_BIT_MASK;
+}
diff --git a/src/port/random.c b/src/port/random.c
index 2dd59a0829..136ed9dd8a 100644
--- a/src/port/random.c
+++ b/src/port/random.c
@@ -1,7 +1,7 @@
 /*-------------------------------------------------------------------------
  *
  * random.c
- *	  random() wrapper
+ *	  random() and srandom() wrapper
  *
  * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
@@ -15,11 +15,17 @@
 
 #include "c.h"
 
-#include <math.h>
+/* global shared state: not thread safe! */
+static pg_prng_state state = PG_PRNG_DEFAULT_SEED;
 
+void
+srandom(unsigned int seed)
+{
+	pg_prng_seed(&state, (uint64_t) seed);
+}
 
 long
 random(void)
 {
-	return pg_lrand48();
+	return pg_prng_i32(&state);
 }
diff --git a/src/port/srandom.c b/src/port/srandom.c
deleted file mode 100644
index cf1007b2ef..0000000000
--- a/src/port/srandom.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/test/modules/test_integerset/test_integerset.c b/src/test/modules/test_integerset/test_integerset.c
index 21c6f49b37..c0923e35c5 100644
--- a/src/test/modules/test_integerset/test_integerset.c
+++ b/src/test/modules/test_integerset/test_integerset.c
@@ -248,7 +248,7 @@ test_pattern(const test_spec *spec)
 		 * only a small part of the integer space is used.  We would very
 		 * rarely hit values that are actually in the set.
 		 */
-		x = (pg_lrand48() << 31) | pg_lrand48();
+		x = (random() << 31) | random();
 		x = x % (last_int + 1000);
 
 		/* Do we expect this value to be present in the set? */
@@ -571,7 +571,7 @@ test_huge_distances(void)
 	 */
 	while (num_values < 1000)
 	{
-		val += pg_lrand48();
+		val += random();
 		values[num_values++] = val;
 	}
 
#6Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Andrey Borodin (#4)
Re: rand48 replacement

Hello Andrey,

- NOT to invent a new design!

Radical version of this argument would be to use de-facto standard and
ubiquitous MT19937.

Indeed, I started considering this one for this reason, obviously.

Though, I suspect, it's not optimal solution to the date.

"not optimal" does not do justice to the issues.

The main one is the huge 2.5 KB state of MT19937 which makes it quite
impractical for plenty of internal and temporary uses. In pgbench there
are many PRNG needed for reproducibility (eg one global, 3 per thread, one
per client) plus a temporary one internal to a function call (permute)
which is expected to be reasonably fast, so should not start by
initializing 2.5 KB of data. In postgres there are 2 permanent ones (sql
random, C double random) plus some in a geqo and in sampling internal
structures.

So standard MT19937 is basically out of the equation. It also happens to
fail some statistical tests and not be very fast. It has a insanely huge
cycle, but pg does not need that, and probably nobody does. The only good
point is that it is a standard, which IMO is not enough to fix the other
issues.

--
Fabien.

#7Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Aleksander Alekseev (#5)
Re: rand48 replacement

Hello Aleksander,

- better software engineering
- similar speed (slightly slower)
- better statistical quality
- quite small state
- soundness

Personally, I think your patch is great.

Thanks for having a look!

Speaking of the speed I believe we should consider the performance of
the entire DBMS in typical scenarios, not the performance of the single
procedure.

Sure. I tested a worst-case pgbench script with only "\set i random(1,
100000000)" on a loop, the slowdown was a few percents (IFAICR < 5%).

I'm pretty sure in these terms the impact of your patch is neglectable
now, and almost certainly beneficial in the long term because of better
randomness.

While reviewing your patch I noticed that you missed test_integerset.c.
Here is an updated patch.

Indeed. Thanks for the catch & the v2 patch!

--
Fabien.

#8Aleksander Alekseev
aleksander@timescale.com
In reply to: Fabien COELHO (#7)
Re: rand48 replacement

Sorry for a duplicate entry on CF web application

#9Aleksander Alekseev
aleksander@timescale.com
In reply to: Aleksander Alekseev (#8)
Re: rand48 replacement

The following review has been posted through the commitfest application:
make installcheck-world: tested, passed
Implements feature: tested, passed
Spec compliant: tested, passed
Documentation: tested, passed

Although the patch looks OK I would like to keep the status "Needs review" for now in case someone would like to join the discussion.

#10Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Aleksander Alekseev (#9)
Re: rand48 replacement

The following review has been posted through the commitfest application:
make installcheck-world: tested, passed
Implements feature: tested, passed
Spec compliant: tested, passed
Documentation: tested, passed

Although the patch looks OK I would like to keep the status "Needs review" for now in case someone would like to join the discussion.

Ok, fine with me.

--
Fabien.

#11Tom Lane
tgl@sss.pgh.pa.us
In reply to: Fabien COELHO (#7)
Re: rand48 replacement

Although this patch is marked RFC, the cfbot shows it doesn't
even compile on Windows. I think you missed updating Mkvcbuild.pm.

regards, tom lane

#12Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Tom Lane (#11)
1 attachment(s)
Re: rand48 replacement

Although this patch is marked RFC, the cfbot shows it doesn't
even compile on Windows. I think you missed updating Mkvcbuild.pm.

Indeed. Here is a blind attempt at fixing the build, I'll check later to
see whether it works. It would help me if the cfbot results were
integrated into the cf app.

--
Fabien.

Attachments:

prng-3.patchtext/x-diff; name=prng-3.patchDownload
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 2c2f149fb0..146b524076 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -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]);
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index fafbab6b02..3009861e45 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -5152,7 +5152,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]);
 		}
diff --git a/contrib/tsm_system_rows/tsm_system_rows.c b/contrib/tsm_system_rows/tsm_system_rows.c
index 4996612902..1a46d4b143 100644
--- a/contrib/tsm_system_rows/tsm_system_rows.c
+++ b/contrib/tsm_system_rows/tsm_system_rows.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/contrib/tsm_system_time/tsm_system_time.c b/contrib/tsm_system_time/tsm_system_time.c
index 788d8f9a68..36acc6c106 100644
--- a/contrib/tsm_system_time/tsm_system_time.c
+++ b/contrib/tsm_system_time/tsm_system_time.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 426c1e6710..092d53d28a 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1294,7 +1294,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 					 * Found a suitable tuple, so save it, replacing one old
 					 * tuple at random
 					 */
-					int			k = (int) (targrows * sampler_random_fract(rstate.randstate));
+					int			k = (int) (targrows * sampler_random_fract(&rstate.randstate));
 
 					Assert(k >= 0 && k < targrows);
 					heap_freetuple(rows[k]);
diff --git a/src/backend/optimizer/geqo/geqo_random.c b/src/backend/optimizer/geqo/geqo_random.c
index f21bc047e6..8b42a9ffba 100644
--- a/src/backend/optimizer/geqo/geqo_random.c
+++ b/src/backend/optimizer/geqo/geqo_random.c
@@ -21,14 +21,7 @@ geqo_set_seed(PlannerInfo *root, double seed)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	/*
-	 * XXX. This seeding algorithm could certainly be improved - but it is not
-	 * critical to do so.
-	 */
-	memset(private->random_state, 0, sizeof(private->random_state));
-	memcpy(private->random_state,
-		   &seed,
-		   Min(sizeof(private->random_state), sizeof(seed)));
+	pg_prng_fseed(&private->random_state, seed);
 }
 
 double
@@ -36,5 +29,5 @@ geqo_rand(PlannerInfo *root)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	return pg_erand48(private->random_state);
+	return pg_prng_f64(&private->random_state);
 }
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 098bbb372b..37fcd645e0 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -65,7 +65,7 @@ float8		degree_c_one = 1.0;
 
 /* State for drandom() and setseed() */
 static bool drandom_seed_set = false;
-static unsigned short drandom_seed[3] = {0, 0, 0};
+static pg_prng_state drandom_seed = PG_PRNG_DEFAULT_STATE;
 
 /* Local function prototypes */
 static double sind_q1(double x);
@@ -2762,22 +2762,20 @@ drandom(PG_FUNCTION_ARGS)
 		 * Should that fail for some reason, we fall back on a lower-quality
 		 * seed based on current time and PID.
 		 */
-		if (!pg_strong_random(drandom_seed, sizeof(drandom_seed)))
+		if (!pg_strong_random(&drandom_seed, sizeof(drandom_seed)))
 		{
 			TimestampTz now = GetCurrentTimestamp();
 			uint64		iseed;
 
 			/* Mix the PID with the most predictable bits of the timestamp */
 			iseed = (uint64) now ^ ((uint64) MyProcPid << 32);
-			drandom_seed[0] = (unsigned short) iseed;
-			drandom_seed[1] = (unsigned short) (iseed >> 16);
-			drandom_seed[2] = (unsigned short) (iseed >> 32);
+			pg_prng_seed(&drandom_seed, iseed);
 		}
 		drandom_seed_set = true;
 	}
 
-	/* pg_erand48 produces desired result range [0.0 - 1.0) */
-	result = pg_erand48(drandom_seed);
+	/* produces desired result range [0.0 - 1.0) */
+	result = pg_prng_f64(&drandom_seed);
 
 	PG_RETURN_FLOAT8(result);
 }
@@ -2790,7 +2788,6 @@ Datum
 setseed(PG_FUNCTION_ARGS)
 {
 	float8		seed = PG_GETARG_FLOAT8(0);
-	uint64		iseed;
 
 	if (seed < -1 || seed > 1 || isnan(seed))
 		ereport(ERROR,
@@ -2798,11 +2795,7 @@ setseed(PG_FUNCTION_ARGS)
 				 errmsg("setseed parameter %g is out of allowed range [-1,1]",
 						seed)));
 
-	/* Use sign bit + 47 fractional bits to fill drandom_seed[] */
-	iseed = (int64) (seed * (float8) UINT64CONST(0x7FFFFFFFFFFF));
-	drandom_seed[0] = (unsigned short) iseed;
-	drandom_seed[1] = (unsigned short) (iseed >> 16);
-	drandom_seed[2] = (unsigned short) (iseed >> 32);
+	pg_prng_fseed(&drandom_seed, seed);
 	drandom_seed_set = true;
 
 	PG_RETURN_VOID();
diff --git a/src/backend/utils/misc/sampling.c b/src/backend/utils/misc/sampling.c
index 0c327e823f..7b59128878 100644
--- a/src/backend/utils/misc/sampling.c
+++ b/src/backend/utils/misc/sampling.c
@@ -49,7 +49,7 @@ BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize,
 	bs->t = 0;					/* blocks scanned so far */
 	bs->m = 0;					/* blocks selected so far */
 
-	sampler_random_init_state(randseed, bs->randstate);
+	sampler_random_init_state(randseed, &bs->randstate);
 
 	return Min(bs->n, bs->N);
 }
@@ -98,7 +98,7 @@ BlockSampler_Next(BlockSampler bs)
 	 * less than k, which means that we cannot fail to select enough blocks.
 	 *----------
 	 */
-	V = sampler_random_fract(bs->randstate);
+	V = sampler_random_fract(&bs->randstate);
 	p = 1.0 - (double) k / (double) K;
 	while (V < p)
 	{
@@ -136,10 +136,10 @@ reservoir_init_selection_state(ReservoirState rs, int n)
 	 * Reservoir sampling is not used anywhere where it would need to return
 	 * repeatable results so we can initialize it randomly.
 	 */
-	sampler_random_init_state(random(), rs->randstate);
+	sampler_random_init_state(random(), &rs->randstate);
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	rs->W = exp(-log(sampler_random_fract(rs->randstate)) / n);
+	rs->W = exp(-log(sampler_random_fract(&rs->randstate)) / n);
 }
 
 double
@@ -154,7 +154,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 		double		V,
 					quot;
 
-		V = sampler_random_fract(rs->randstate);	/* Generate V */
+		V = sampler_random_fract(&rs->randstate);	/* Generate V */
 		S = 0;
 		t += 1;
 		/* Note: "num" in Vitter's code is always equal to t - n */
@@ -186,7 +186,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 						tmp;
 
 			/* Generate U and X */
-			U = sampler_random_fract(rs->randstate);
+			U = sampler_random_fract(&rs->randstate);
 			X = t * (W - 1.0);
 			S = floor(X);		/* S is tentatively set to floor(X) */
 			/* Test if U <= h(S)/cg(X) in the manner of (6.3) */
@@ -215,7 +215,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 				y *= numer / denom;
 				denom -= 1;
 			}
-			W = exp(-log(sampler_random_fract(rs->randstate)) / n); /* Generate W in advance */
+			W = exp(-log(sampler_random_fract(&rs->randstate)) / n); /* Generate W in advance */
 			if (exp(log(y) / n) <= (t + X) / t)
 				break;
 		}
@@ -230,24 +230,22 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
  *----------
  */
 void
-sampler_random_init_state(long seed, SamplerRandomState randstate)
+sampler_random_init_state(long seed, pg_prng_state *randstate)
 {
-	randstate[0] = 0x330e;		/* same as pg_erand48, but could be anything */
-	randstate[1] = (unsigned short) seed;
-	randstate[2] = (unsigned short) (seed >> 16);
+	pg_prng_seed(randstate, (uint64_t) seed);
 }
 
 /* Select a random value R uniformly distributed in (0 - 1) */
 double
-sampler_random_fract(SamplerRandomState randstate)
+sampler_random_fract(pg_prng_state *randstate)
 {
 	double		res;
 
-	/* pg_erand48 returns a value in [0.0 - 1.0), so we must reject 0 */
+	/* pg_prng_f64 returned value in [0.0 - 1.0), so we must reject 0.0 */
 	do
 	{
-		res = pg_erand48(randstate);
-	} while (res == 0.0);
+		res = pg_prng_f64(randstate);
+	} while (unlikely(res == 0.0));
 	return res;
 }
 
@@ -266,22 +264,28 @@ double
 anl_random_fract(void)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(random(), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* and compute a random fraction */
-	return sampler_random_fract(oldrs.randstate);
+	return sampler_random_fract(&oldrs.randstate);
 }
 
 double
 anl_init_selection_state(int n)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(random(), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	return exp(-log(sampler_random_fract(oldrs.randstate)) / n);
+	return exp(-log(sampler_random_fract(&oldrs.randstate)) / n);
 }
 
 double
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 4aeccd93af..442059aa8b 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -343,16 +343,8 @@ typedef struct StatsData
 	SimpleStats lag;
 } StatsData;
 
-/*
- * Struct to keep random state.
- */
-typedef struct RandomState
-{
-	unsigned short xseed[3];
-} RandomState;
-
 /* 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 */
 static THREAD_BARRIER_T barrier;
@@ -454,7 +446,7 @@ typedef struct
 	 * Separate randomness for each client. This is used for random functions
 	 * 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			command;		/* command number in script */
@@ -491,9 +483,9 @@ typedef struct
 	 * random state to make all of them independent of each other and
 	 * therefore deterministic at the thread level.
 	 */
-	RandomState ts_choose_rs;	/* random state for selecting a script */
-	RandomState ts_throttle_rs; /* random state for transaction throttling */
-	RandomState ts_sample_rs;	/* random state for log sampling */
+	pg_prng_state	ts_choose_rs;	/* random state for selecting a script */
+	pg_prng_state	ts_throttle_rs; /* random state for transaction throttling */
+	pg_prng_state	ts_sample_rs;	/* random state for log sampling */
 
 	int64		throttle_trigger;	/* previous/next throttling (us) */
 	FILE	   *logfile;		/* where to log, or NULL */
@@ -891,42 +883,34 @@ 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.
  */
 static void
-initRandomState(RandomState *random_state)
+initRandomState(pg_prng_state *state)
 {
-	random_state->xseed[0] = (unsigned short)
-		(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);
+	pg_prng_seed(state, pg_prng_u64(&base_random_sequence));
 }
 
+
 /*
  * Random number generator: uniform distribution from min to max inclusive.
  *
  * Although the limits are expressed as int64, you can't generate the full
  * int64 range in one call, because the difference of the limits mustn't
- * overflow int64.  In practice it's unwise to ask for more than an int32
- * range, because of the limited precision of pg_erand48().
+ * overflow int64.
  */
 static int64
-getrand(RandomState *random_state, int64 min, int64 max)
+getrand(pg_prng_state *state, int64 min, int64 max)
 {
 	/*
 	 * 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));
+	int64	size = max - min + 1,
+			val = pg_prng_u64(state) & UINT64CONST(0x7FFFFFFFFFFFFFFF);
+	return min + (int64) (val % size);
 }
 
 /*
@@ -935,8 +919,7 @@ getrand(RandomState *random_state, int64 min, int64 max)
  * value is exp(-parameter).
  */
 static int64
-getExponentialRand(RandomState *random_state, int64 min, int64 max,
-				   double parameter)
+getExponentialRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		cut,
 				uniform,
@@ -946,7 +929,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 	Assert(parameter > 0.0);
 	cut = exp(-parameter);
 	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	uniform = 1.0 - pg_prng_f64(state);
 
 	/*
 	 * inner expression in (cut, 1] (if parameter > 0), rand in [0, 1)
@@ -959,8 +942,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 
 /* random number generator: gaussian distribution from min to max inclusive */
 static int64
-getGaussianRand(RandomState *random_state, int64 min, int64 max,
-				double parameter)
+getGaussianRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		stdev;
 	double		rand;
@@ -983,13 +965,13 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
 	do
 	{
 		/*
-		 * pg_erand48 generates [0,1), but for the basic version of the
+		 * pg_prng_f64 generates [0,1), but for the basic version of the
 		 * Box-Muller transform the two uniformly distributed random numbers
 		 * are expected in (0, 1] (see
 		 * https://en.wikipedia.org/wiki/Box-Muller_transform)
 		 */
-		double		rand1 = 1.0 - pg_erand48(random_state->xseed);
-		double		rand2 = 1.0 - pg_erand48(random_state->xseed);
+		double		rand1 = 1.0 - pg_prng_f64(state);
+		double		rand2 = 1.0 - pg_prng_f64(state);
 
 		/* Box-Muller basic form transform */
 		double		var_sqrt = sqrt(-2.0 * log(rand1));
@@ -1019,7 +1001,7 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
  * not be one.
  */
 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
@@ -1027,8 +1009,8 @@ getPoissonRand(RandomState *random_state, double center)
 	 */
 	double		uniform;
 
-	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	/* pseudo-random value in [0, 1), uniform in (0, 1] */
+	uniform = 1.0 - pg_prng_f64(state);
 
 	return (int64) (-log(uniform) * center + 0.5);
 }
@@ -1041,7 +1023,7 @@ getPoissonRand(RandomState *random_state, double center)
  * This works for s > 1.0, but may perform badly for s very close to 1.0.
  */
 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		x,
@@ -1056,8 +1038,8 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 	while (true)
 	{
 		/* random variates */
-		u = pg_erand48(random_state->xseed);
-		v = pg_erand48(random_state->xseed);
+		u = pg_prng_f64(state);
+		v = pg_prng_f64(state);
 
 		x = floor(pow(u, -1.0 / (s - 1.0)));
 
@@ -1071,14 +1053,14 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 
 /* random number generator: zipfian distribution from min to max inclusive */
 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;
 
 	/* abort if parameter is invalid */
 	Assert(MIN_ZIPFIAN_PARAM <= s && s <= MAX_ZIPFIAN_PARAM);
 
-	return min - 1 + computeIterativeZipfian(random_state, n, s);
+	return min - 1 + computeIterativeZipfian(state, n, s);
 }
 
 /*
@@ -1135,7 +1117,7 @@ getHashMurmur2(int64 val, uint64 seed)
  * For small sizes, this generates each of the (size!) possible permutations
  * of integers in the range [0, size) with roughly equal probability.  Once
  * the size is larger than 20, the number of possible permutations exceeds the
- * number of distinct states of the internal pseudorandom number generators,
+ * number of distinct states of the internal pseudorandom number generator,
  * and so not all possible permutations can be generated, but the permutations
  * chosen should continue to give the appearance of being random.
  *
@@ -1145,25 +1127,19 @@ getHashMurmur2(int64 val, uint64 seed)
 static int64
 permute(const int64 val, const int64 isize, const int64 seed)
 {
-	RandomState random_state1;
-	RandomState random_state2;
-	uint64		size;
-	uint64		v;
-	int			masklen;
-	uint64		mask;
-	int			i;
+	/* using a high-end PRNG is probably overkill */
+	pg_prng_state	state;
+	uint64			size;
+	uint64			v;
+	int				masklen;
+	uint64			mask;
+	int				i;
 
 	if (isize < 2)
 		return 0;				/* nothing to permute */
 
-	/* Initialize a pair of random states using the seed */
-	random_state1.xseed[0] = seed & 0xFFFF;
-	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;
+	/* Initialize prng state using the seed */
+	pg_prng_seed(&state, (uint64) seed);
 
 	/* Computations are performed on unsigned values */
 	size = (uint64) isize;
@@ -1209,8 +1185,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 					t;
 
 		/* Random multiply (by an odd number), XOR and rotate of lower half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		if (v <= mask)
 		{
 			v = ((v * m) ^ r) & mask;
@@ -1218,8 +1194,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random multiply (by an odd number), XOR and rotate of upper half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		t = size - 1 - v;
 		if (t <= mask)
 		{
@@ -1229,7 +1205,7 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random offset */
-		r = (uint64) getrand(&random_state2, 0, size - 1);
+		r = pg_prng_u64(&state) % (size - 1);
 		v = (v + r) % size;
 	}
 
@@ -3790,7 +3766,7 @@ doLog(TState *thread, CState *st,
 	 * to the random sample.
 	 */
 	if (sample_rate != 0.0 &&
-		pg_erand48(thread->ts_sample_rs.xseed) > sample_rate)
+		pg_prng_f64(&thread->ts_sample_rs) > sample_rate)
 		return;
 
 	/* should we aggregate the results or not? */
@@ -5714,12 +5690,11 @@ set_random_seed(const char *seed)
 
 	if (seed != NULL)
 		pg_log_info("setting random seed to %llu", (unsigned long long) iseed);
+
 	random_seed = iseed;
 
 	/* Fill base_random_sequence with low-order bits of seed */
-	base_random_sequence.xseed[0] = iseed & 0xFFFF;
-	base_random_sequence.xseed[1] = (iseed >> 16) & 0xFFFF;
-	base_random_sequence.xseed[2] = (iseed >> 32) & 0xFFFF;
+	pg_prng_seed(&base_random_sequence, (uint64_t) iseed);
 
 	return true;
 }
@@ -6405,9 +6380,7 @@ main(int argc, char **argv)
 	/* set default seed for hash functions */
 	if (lookupVariable(&state[0], "default_seed") == NULL)
 	{
-		uint64		seed =
-		((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) |
-		(((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) << 32);
+		uint64		seed = pg_prng_u64(&base_random_sequence);
 
 		for (i = 0; i < nclients; i++)
 			if (!putVariableInt(&state[i], "startup", "default_seed", (int64) seed))
diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl
index 3aa9d5d753..ac2c86158a 100644
--- a/src/bin/pgbench/t/001_pgbench_with_server.pl
+++ b/src/bin/pgbench/t/001_pgbench_with_server.pl
@@ -427,9 +427,9 @@ pgbench(
 
 		# After explicit seeding, the four random checks (1-3,20) are
 		# deterministic
-		qr{command=1.: int 13\b},      # uniform random
-		qr{command=2.: int 116\b},     # exponential random
-		qr{command=3.: int 1498\b},    # gaussian random
+		qr{command=1.: int 10\b},      # uniform random
+		qr{command=2.: int 100\b},     # exponential random
+		qr{command=3.: int 1526\b},    # gaussian random
 		qr{command=4.: int 4\b},
 		qr{command=5.: int 5\b},
 		qr{command=6.: int 6\b},
@@ -496,6 +496,7 @@ pgbench(
 		qr{command=109.: boolean true\b},
 		qr{command=110.: boolean true\b},
 		qr{command=111.: boolean true\b},
+		qr{command=113.: boolean true\b},
 	],
 	'pgbench expressions',
 	{
@@ -639,8 +640,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))
 -- actual values
 \set t debug(permute(:v, 1) = 0)
-\set t debug(permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1 and \
-             permute(0, 2, 5435) = 1 and permute(1, 2, 5435) = 0)
+\set t debug(permute(0, 2, 5431) = 1 and permute(1, 2, 5431) = 0 and \
+             permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1)
+-- 63 bits tests for checking permute portability across architectures
+\set size debug(:max - 10)
+\set t debug(permute(:size-1, :size, 5432) = 4352241160009873020 and \
+             permute(:size-2, :size, 5432) = 4990004119691638710 and \
+             permute(:size-3, :size, 5432) = 5868029867256127549 and \
+             permute(:size-4, :size, 5432) = 1238830324886341378 and \
+             permute(:size-5, :size, 5432) = 7415914367102906897 and \
+             permute(:size-6, :size, 5432) = 3214501037984818995 and \
+             permute(:size-7, :size, 5432) =  600660351261124695)
 }
 	});
 
diff --git a/src/include/optimizer/geqo.h b/src/include/optimizer/geqo.h
index 24dcdfb6cc..75bfada4a1 100644
--- a/src/include/optimizer/geqo.h
+++ b/src/include/optimizer/geqo.h
@@ -72,8 +72,8 @@ extern double Geqo_seed;		/* 0 .. 1 */
  */
 typedef struct
 {
-	List	   *initial_rels;	/* the base relations we are joining */
-	unsigned short random_state[3]; /* state for pg_erand48() */
+	List		   *initial_rels;	/* the base relations we are joining */
+	pg_prng_state	random_state;   /* PRNG state */
 } GeqoPrivateData;
 
 
diff --git a/src/include/port.h b/src/include/port.h
index 82f63de325..a73c944c91 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -356,10 +356,24 @@ extern int	gettimeofday(struct timeval *tp, struct timezone *tzp);
 #define pgoff_t off_t
 #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);
+/* PRNG: Pseudo-Random Number Generator */
+typedef struct pg_prng_state {
+	uint64_t	s0, s1;
+} pg_prng_state;
+
+/* use Donald Knuth's LCG constants */
+#define PG_PRNG_DEFAULT_STATE \
+	{ UINT64CONST(0x5851F42D4C957F2D), UINT64CONST(0x14057B7EF767814F) }
+
+extern void pg_prng_seed(pg_prng_state *state, uint64_t seed);
+extern void pg_prng_fseed(pg_prng_state *state, double fseed);
+extern void pg_prng_strong_seed(pg_prng_state *state);
+extern uint64_t pg_prng_u64(pg_prng_state *state);
+extern int64_t pg_prng_i64(pg_prng_state *state);
+extern uint32_t pg_prng_u32(pg_prng_state *state);
+extern int32_t pg_prng_i32(pg_prng_state *state);
+extern double pg_prng_f64(pg_prng_state *state);
+extern bool pg_prng_bool(pg_prng_state *state);
 
 #ifndef HAVE_FLS
 extern int	fls(int mask);
diff --git a/src/include/utils/sampling.h b/src/include/utils/sampling.h
index a58d14281b..97cd9b7aef 100644
--- a/src/include/utils/sampling.h
+++ b/src/include/utils/sampling.h
@@ -17,11 +17,9 @@
 
 
 /* Random generator for sampling code */
-typedef unsigned short SamplerRandomState[3];
-
 extern void sampler_random_init_state(long seed,
-									  SamplerRandomState randstate);
-extern double sampler_random_fract(SamplerRandomState randstate);
+									  pg_prng_state *randstate);
+extern double sampler_random_fract(pg_prng_state *randstate);
 
 /* Block sampling methods */
 
@@ -32,7 +30,7 @@ typedef struct
 	int			n;				/* desired sample size */
 	BlockNumber t;				/* current block number */
 	int			m;				/* blocks selected so far */
-	SamplerRandomState randstate;	/* random generator state */
+	pg_prng_state randstate;	/* random generator state */
 } BlockSamplerData;
 
 typedef BlockSamplerData *BlockSampler;
@@ -46,8 +44,9 @@ extern BlockNumber BlockSampler_Next(BlockSampler bs);
 
 typedef struct
 {
-	double		W;
-	SamplerRandomState randstate;	/* random generator state */
+	double			W;
+	pg_prng_state	randstate;	/* random generator state */
+	bool			randstate_initialized;
 } ReservoirStateData;
 
 typedef ReservoirStateData *ReservoirState;
diff --git a/src/port/Makefile b/src/port/Makefile
index 52dbf5783f..9ff2056688 100644
--- a/src/port/Makefile
+++ b/src/port/Makefile
@@ -42,11 +42,11 @@ OBJS = \
 	$(PG_CRC32C_OBJS) \
 	bsearch_arg.o \
 	chklocale.o \
-	erand48.o \
 	inet_net_ntop.o \
 	noblock.o \
 	path.o \
 	pg_bitutils.o \
+	pg_prng.o \
 	pg_strong_random.o \
 	pgcheckdir.o \
 	pgmkdirp.o \
diff --git a/src/port/erand48.c b/src/port/erand48.c
deleted file mode 100644
index a82ea94220..0000000000
--- a/src/port/erand48.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/port/pg_prng.c b/src/port/pg_prng.c
new file mode 100644
index 0000000000..6f4c4e84b0
--- /dev/null
+++ b/src/port/pg_prng.c
@@ -0,0 +1,189 @@
+/*
+ * Pseudo-Random Number Generator
+ *
+ * Previous version was the 1988 (?) rand48 standard with 48 bits state LCG,
+ * designed for running on 32 bits or even 16 bits architectures, and to produce
+ * 32 bit ints and floats (i.e. *not* doubles).
+ * The implied 16-bits unpacking/packing may have a detrimental performance impact
+ * on modern 64 bits architectures.
+ *
+ * We (probably) want:
+ * - NOT to invent a new design!
+ * - one reasonable default PRNG for all pg internal uses, currently this is
+ *   POSIX rand48.
+ * - something fast, close to rand48 (which basically does 2 arithmetic ops)
+ *   no need for something cryptographic though, which would imply slow
+ * - to produce 64 bits integers & doubles with a 52 bits mantissa
+ *   so state size > 64 bits.
+ * - a small state though, because we might generate quite a few of them
+ *   for different purposes so state size <= 256 or even <= 128 bits
+ * - the state to be aligned to whatever, so state size = 128 bits
+ * - 64 bits operations for efficiency on modern architectures,
+ *   but NOT 128 bits operations (?)
+ * - not to depend on special hardware for speed (eg MMX/SSE/AES).
+ * - not something with obvious known and relevant defects.
+ * - not something with "rights" attached.
+ *
+ * These constraints reduce a lot the available options from
+ * https://en.wikipedia.org/wiki/List_of_random_number_generators
+ *
+ * LCG
+ * - POSIX rand48: m = 2⁴⁸, a = 25214903917, c = 11, extract 47…16
+ * - MMIX by Donald Knuth: m = 2⁶⁴, a = 6364136223846793005, c = 1442695040888963407
+ * - very bad on low bits, which MUST NOT BE USED
+ *   a 128 bits would do, but no clear standards, and it would imply 128-bit ops,
+ *   see PCG below.
+ *
+ * PCG Family (https://www.pcg-random.org/)
+ * - this is basically an LCG with an improved outpout function
+ *   so as to avoid typical LCG issues and pass stats tests.
+ * - recommands state size to be twice the output size,
+ *   which seems a reasonable requirement.
+ * - this requires 128 bit ops for our target parameters,
+ *   otherwise it is a fairly good candidate for simplicity,
+ *   efficiency and quality.
+ *
+ * Xor-Shift-Rotate PRNGs:
+ * - a generic annoying feature of this generator class is that there is one
+ *   special all-zero state which must not be used, inducing a constraint on
+ *   initialization, and when the generator get close to it (i.e. many zeros
+ *   in the state) it takes some iterations to recover, with small lumps
+ *   of close/related/bad numbers generated in the process.
+ * Xoroshiro128+
+ * - fails statistical linearity tests in the low bits.
+ * Xoroshiro128**
+ *   https://prng.di.unimi.it/xoroshiro128starstar.c
+ * - mild and irrelevant issues: close to zero state, stats issues after some transforms
+ * Xoshiro256**
+ * - it looks good, but some issues (no big deal for pg usage IMO)
+ *   https://www.pcg-random.org/posts/a-quick-look-at-xoshiro256.html
+ *   mild problems getting out of closes-to-zero state
+ *
+ * MT19937-64
+ * + default in many places, standard
+ * - huge 2.5 KiB state (but TinyMT, 127 bits state space)
+ * - slow (but SFMT, twice as fast)
+ * - vulnerabilities on tinyMT?
+ *
+ * WELL
+ * - large state, 32-bit oriented
+ *
+ * ACORN Family (https://en.wikipedia.org/wiki/ACORN_(PRNG))
+ * - Fortran / double oriented, no C implementation found?!
+ * - medium large state "In practice we recommend using k > 10, M = 2^60 (for general application)
+ *   or M = 2^120 (for demanding applications requiring high-quality pseudo-random numbers
+ *   that will consistently pass all the tests in standard test packages such as TestU01)
+ *   and choose any odd value less than M for the seed."
+ *   k = 11 => 88 bytes/704-bits state, too large:-(
+ *
+ * Splitmix?
+ * Others?
+ */
+
+#include "c.h"
+#include <math.h>
+
+#define FIRST_BIT_MASK UINT64CONST(0x8000000000000000)
+#define HALF_BITS_MASK UINT64CONST(0x00000000FFFFFFFF)
+#define DMANTISSA_MASK UINT64CONST(0x000FFFFFFFFFFFFF)
+
+/* 64-bits rotate left */
+static inline uint64_t rotl(const uint64_t x, const int bits)
+{
+	return (x << bits) | (x >> (64 - bits));
+}
+
+/* another 64-bits generator is used for state initialization */
+static uint64_t splitmix64(uint64_t * state)
+{
+	/* state update */
+	uint64_t	val = (*state += UINT64CONST(0x9E3779B97f4A7C15));
+
+	/* value extraction */
+	val = (val ^ (val >> 30)) * UINT64CONST(0xBF58476D1CE4E5B9);
+	val = (val ^ (val >> 27)) * UINT64CONST(0x94D049BB133111EB);
+
+	return val ^ (val >> 31);
+}
+
+/* seed prng state from a 64 bits integer, ensuring non zero */
+void pg_prng_seed(pg_prng_state *state, uint64_t seed)
+{
+	state->s0 = splitmix64(&seed);
+	state->s1 = splitmix64(&seed);
+}
+
+/* seed with 53 bits (mantissa & sign) from a float */
+void pg_prng_fseed(pg_prng_state *state, double fseed)
+{
+	uint64 seed = (int64) (((double) DMANTISSA_MASK) * fseed);
+	pg_prng_seed(state, seed);
+}
+
+/* strong random seeding */
+void pg_prng_strong_seed(pg_prng_state *state)
+{
+	pg_strong_random((void *) state, sizeof(pg_prng_state));
+
+	/* avoid zero with Donald Knuth's LCG parameters */
+	if (unlikely(state->s0 == 0 && state->s1 == 0))
+	{
+		/* should it warn that something is amiss if we get there? */
+		pg_prng_state def = PG_PRNG_DEFAULT_STATE;
+		*state = def;
+	}
+}
+
+/* generator & state update */
+static uint64_t xoroshiro128ss(pg_prng_state *state)
+{
+	const uint64_t	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;
+}
+
+/* u64 generator */
+uint64_t pg_prng_u64(pg_prng_state *state)
+{
+	return xoroshiro128ss(state);
+}
+
+/* i64 generator */
+int64_t pg_prng_i64(pg_prng_state *state)
+{
+	return (int64_t) xoroshiro128ss(state);
+}
+
+/* u32 generator */
+uint32_t pg_prng_u32(pg_prng_state *state)
+{
+	const uint64_t v = xoroshiro128ss(state);
+	return (uint32_t) (((v >> 32) ^ v) & HALF_BITS_MASK);
+}
+
+/* i32 generator */
+int32_t pg_prng_i32(pg_prng_state *state)
+{
+	const uint64_t v = xoroshiro128ss(state);
+	return (int32_t) (((v >> 32) ^ v) & HALF_BITS_MASK);
+}
+
+/* double generator */
+double pg_prng_f64(pg_prng_state *state)
+{
+	uint64_t v = xoroshiro128ss(state);
+	return ldexp((double) (v & DMANTISSA_MASK), -52);
+}
+
+/* bool generator */
+bool pg_prng_bool(pg_prng_state *state)
+{
+	uint64_t v = xoroshiro128ss(state);
+	return (v & FIRST_BIT_MASK) == FIRST_BIT_MASK;
+}
diff --git a/src/port/random.c b/src/port/random.c
index 2dd59a0829..136ed9dd8a 100644
--- a/src/port/random.c
+++ b/src/port/random.c
@@ -1,7 +1,7 @@
 /*-------------------------------------------------------------------------
  *
  * random.c
- *	  random() wrapper
+ *	  random() and srandom() wrapper
  *
  * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
@@ -15,11 +15,17 @@
 
 #include "c.h"
 
-#include <math.h>
+/* global shared state: not thread safe! */
+static pg_prng_state state = PG_PRNG_DEFAULT_SEED;
 
+void
+srandom(unsigned int seed)
+{
+	pg_prng_seed(&state, (uint64_t) seed);
+}
 
 long
 random(void)
 {
-	return pg_lrand48();
+	return pg_prng_i32(&state);
 }
diff --git a/src/port/srandom.c b/src/port/srandom.c
deleted file mode 100644
index cf1007b2ef..0000000000
--- a/src/port/srandom.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/test/modules/test_integerset/test_integerset.c b/src/test/modules/test_integerset/test_integerset.c
index 21c6f49b37..c0923e35c5 100644
--- a/src/test/modules/test_integerset/test_integerset.c
+++ b/src/test/modules/test_integerset/test_integerset.c
@@ -248,7 +248,7 @@ test_pattern(const test_spec *spec)
 		 * only a small part of the integer space is used.  We would very
 		 * rarely hit values that are actually in the set.
 		 */
-		x = (pg_lrand48() << 31) | pg_lrand48();
+		x = (random() << 31) | random();
 		x = x % (last_int + 1000);
 
 		/* Do we expect this value to be present in the set? */
@@ -571,7 +571,7 @@ test_huge_distances(void)
 	 */
 	while (num_values < 1000)
 	{
-		val += pg_lrand48();
+		val += random();
 		values[num_values++] = val;
 	}
 
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 233ddbf4c2..a0d1664a31 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -105,7 +105,7 @@ sub mkvcbuild
 	our @pgportfiles = qw(
 	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c random.c
 	  srandom.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
+	  pg_prng.c snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
 	  dirent.c dlopen.c getopt.c getopt_long.c link.c
 	  pread.c preadv.c pwrite.c pwritev.c pg_bitutils.c
 	  pg_strong_random.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c
#13Tom Lane
tgl@sss.pgh.pa.us
In reply to: Fabien COELHO (#12)
Re: rand48 replacement

Fabien COELHO <coelho@cri.ensmp.fr> writes:

Although this patch is marked RFC, the cfbot shows it doesn't
even compile on Windows. I think you missed updating Mkvcbuild.pm.

Indeed. Here is a blind attempt at fixing the build, I'll check later to
see whether it works. It would help me if the cfbot results were
integrated into the cf app.

Hmm, not there yet per cfbot, not sure why not.

Anyway, after taking a very quick look at the patch itself, I've
got just one main objection: I don't approve of putting this in
port.h or src/port/. erand48.c is there because we envisioned it
originally as an occasionally-used substitute for libc facilities.
But this is most certainly not that, so it belongs in src/common/
instead. I'd also be inclined to invent a new single-purpose .h
file for it.

I see that you probably did that because random.c and srandom.c
depend on it, but I wonder why we don't make an effort to flush
those altogether. It's surely pretty confusing to newbies that
what appears to be a call of the libc primitives is no such thing.

regards, tom lane

#14Dean Rasheed
dean.a.rasheed@gmail.com
In reply to: Tom Lane (#13)
Re: rand48 replacement

On Thu, 1 Jul 2021 at 19:41, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Anyway, after taking a very quick look at the patch itself, I've
got just one main objection: I don't approve of putting this in
port.h or src/port/.

I haven't looked at the patch in detail, but one thing I object to is
the code to choose a random integer in an arbitrary range.

Currently, this is done in pgbench by getrand(), which has its
problems. However, this patch seems to be replacing that with a simple
modulo operation, which is perhaps the worst possible way to do it.
There's plenty of research out there on how to do it better -- see,
for example, [1]https://www.pcg-random.org/posts/bounded-rands.html for a nice summary.

Also, I'd say that functions to choose random integers in an arbitrary
range ought to be part of the common API, as they are in almost every
language's random API.

Regards,
Dean

[1]: https://www.pcg-random.org/posts/bounded-rands.html

#15Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Tom Lane (#13)
Re: rand48 replacement

Hello Tom,

Indeed. Here is a blind attempt at fixing the build, I'll check later to
see whether it works. It would help me if the cfbot results were
integrated into the cf app.

Hmm, not there yet per cfbot, not sure why not.

I'll investigate.

Anyway, after taking a very quick look at the patch itself, I've
got just one main objection: I don't approve of putting this in
port.h or src/port/. erand48.c is there because we envisioned it
originally as an occasionally-used substitute for libc facilities.
But this is most certainly not that, so it belongs in src/common/
instead.

Ok, this would make sense.

I'd also be inclined to invent a new single-purpose .h
file for it.

Hmmm. Why not.

I see that you probably did that because random.c and srandom.c
depend on it, but I wonder why we don't make an effort to flush
those altogether.

Ok for removing them. They are used in contrib where they can be replaced.
I hope that extensions would not depend on that, though.

It's surely pretty confusing to newbies that what appears to be a call
of the libc primitives is no such thing.

I do not understand your point.

If people believe the current random() implementation to be *the* libc
primitive, then my linux doc says "The random() function uses a nonlinear
additive feedback random number generator employing a default table of
size 31 long integers to return successive pseudo-random numbers in the
range from 0 to RAND_MAX. The period of this random number generator is
very large, approximately 16 * ((2^31) - 1).", which is pretty far from
the rand48 implementation provided in port, so ISTM that the confusion is
already there?

--
Fabien.

#16Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Dean Rasheed (#14)
Re: rand48 replacement

Hello Dean,

I haven't looked at the patch in detail, but one thing I object to is
the code to choose a random integer in an arbitrary range.

Thanks for bringing up this interesting question!

Currently, this is done in pgbench by getrand(), which has its
problems.

Yes. That is one of the motivation for providing something hopefully
better.

However, this patch seems to be replacing that with a simple
modulo operation, which is perhaps the worst possible way to do it.

I did it knowing this issue. Here is why:

The modulo operation is biased for large ranges close to the limit, sure.
Also, the bias is somehow of the same magnitude as the FP multiplication
approach used previously, so the "worst" has not changed much, it is
really the same as before.

I thought it is not such an issue because for typical uses we are unlikely
to be in these conditions, so the one-operation no branching approach
seemed like a good precision vs performance compromise: I'd expect the
typical largest ranges to be well below 40 bits (eg a key in a pretty
large table in pgbench), which makes the bias well under 1/2**24 and ISTM
that I can live with that. With the initial 48 bits state, obviously the
situation was not the same.

There's plenty of research out there on how to do it better -- see,
for example, [1] for a nice summary.

Rejection methods include branches, thus may cost significantly more, as
show by the performance figures in blog.

Also, it somehow breaks the sequence determinism when using range, which I
found quite annoying: ISTM desirable that when generating a number the
state advances once, and just once.

Also some methods have higher costs depending on the actual range, eg the
bitmask approach: for range 129 the bitmask is 0xff and you have a nearly
50% probability of iterating once, nearly 25% of iterating twice, and so
on… I like performance to be uniform, not to depend on actual values.

Given these arguments I'd be inclined to keep the bias, but I'm open to
more discussion.

Also, I'd say that functions to choose random integers in an arbitrary
range ought to be part of the common API, as they are in almost every
language's random API.

That is a good point.

--
Fabien.

#17Dean Rasheed
dean.a.rasheed@gmail.com
In reply to: Fabien COELHO (#16)
Re: rand48 replacement

On Thu, 1 Jul 2021 at 22:18, Fabien COELHO <coelho@cri.ensmp.fr> wrote:

However, this patch seems to be replacing that with a simple
modulo operation, which is perhaps the worst possible way to do it.

The modulo operation is biased for large ranges close to the limit, sure.
Also, the bias is somehow of the same magnitude as the FP multiplication
approach used previously, so the "worst" has not changed much, it is
really the same as before.

It may be true that the bias is of the same magnitude as FP multiply,
but it is not of the same nature. With FP multiply, the
more-likely-to-be-chosen values are more-or-less evenly distributed
across the range, whereas modulo concentrates them all at one end,
making it more likely to bias test results.

It's worth paying attention to how other languages/libraries implement
this, and basically no one chooses the modulo method, which ought to
raise alarm bells. Of the biased methods, it has the worst kind of
bias and the worst performance.

If a biased method is OK, then the biased integer multiply method
seems to be the clear winner. This requires the high part of a
64x64-bit product, which is trivial if 128-bit integers are available,
but would need a little more work otherwise. There's some code in
common/d2s that might be suitable.

Most other implementations tend to use an unbiased method though, and
I think it's worth doing the same. It might be a bit slower, or even
faster depending on implementation and platform, but in the context of
the DB as a whole, I don't think a few extra cycles matters either
way. The method recommended at the very end of that blog seems to be
pretty good all round, but any of the other commonly used unbiased
methods would probably be OK too.

Regards,
Dean

#18Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Dean Rasheed (#17)
Re: rand48 replacement

Hello Dean,

It may be true that the bias is of the same magnitude as FP multiply,
but it is not of the same nature. With FP multiply, the
more-likely-to-be-chosen values are more-or-less evenly distributed
across the range, whereas modulo concentrates them all at one end,
making it more likely to bias test results.

Yes, that is true.

It's worth paying attention to how other languages/libraries implement
this, and basically no one chooses the modulo method, which ought to
raise alarm bells. Of the biased methods, it has the worst kind of
bias and the worst performance.

Hmmm. That is not exactly how I interpreted the figures in the blog.

If a biased method is OK, then the biased integer multiply method
seems to be the clear winner. This requires the high part of a
64x64-bit product, which is trivial if 128-bit integers are available,
but would need a little more work otherwise. There's some code in
common/d2s that might be suitable.

And yes, modulo is expensive. If we allow 128 bits integers operations, I
would not choose this RNPG in the first place, I'd take PCG with a 128 bits state.
That does not change the discussion about bias, though.

Most other implementations tend to use an unbiased method though, and I
think it's worth doing the same. It might be a bit slower, or even
faster depending on implementation and platform, but in the context of
the DB as a whole, I don't think a few extra cycles matters either way.

Ok ok ok, I surrender!

The method recommended at the very end of that blog seems to be pretty
good all round, but any of the other commonly used unbiased methods
would probably be OK too.

That does not address my other issues with the proposed methods, in
particular the fact that the generated sequence is less deterministic, but
I think I have a simple way around that. I'm hesitating to skip to the the
bitmask method, and give up performance uniformity. I'll try to come up
with something over the week-end, and also address Tom's comments in
passing.

--
Fabien.

#19Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Fabien COELHO (#18)
1 attachment(s)
Re: rand48 replacement

Hello Dean & Tom,

Here is a v4, which:

- moves the stuff to common and fully removes random/srandom (Tom)
- includes a range generation function based on the bitmask method (Dean)
but iterates with splitmix so that the state always advances once (Me)

--
Fabien.

Attachments:

prng-4.patchtext/x-diff; name=prng-4.patchDownload
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 2c2f149fb0..146b524076 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -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]);
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index fafbab6b02..3009861e45 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -5152,7 +5152,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]);
 		}
diff --git a/contrib/tsm_system_rows/tsm_system_rows.c b/contrib/tsm_system_rows/tsm_system_rows.c
index 4996612902..1a46d4b143 100644
--- a/contrib/tsm_system_rows/tsm_system_rows.c
+++ b/contrib/tsm_system_rows/tsm_system_rows.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/contrib/tsm_system_time/tsm_system_time.c b/contrib/tsm_system_time/tsm_system_time.c
index 788d8f9a68..36acc6c106 100644
--- a/contrib/tsm_system_time/tsm_system_time.c
+++ b/contrib/tsm_system_time/tsm_system_time.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 0c9591415e..1ddbeed2dc 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1308,7 +1308,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 					 * Found a suitable tuple, so save it, replacing one old
 					 * tuple at random
 					 */
-					int			k = (int) (targrows * sampler_random_fract(rstate.randstate));
+					int			k = (int) (targrows * sampler_random_fract(&rstate.randstate));
 
 					Assert(k >= 0 && k < targrows);
 					heap_freetuple(rows[k]);
diff --git a/src/backend/optimizer/geqo/geqo_random.c b/src/backend/optimizer/geqo/geqo_random.c
index f21bc047e6..8b42a9ffba 100644
--- a/src/backend/optimizer/geqo/geqo_random.c
+++ b/src/backend/optimizer/geqo/geqo_random.c
@@ -21,14 +21,7 @@ geqo_set_seed(PlannerInfo *root, double seed)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	/*
-	 * XXX. This seeding algorithm could certainly be improved - but it is not
-	 * critical to do so.
-	 */
-	memset(private->random_state, 0, sizeof(private->random_state));
-	memcpy(private->random_state,
-		   &seed,
-		   Min(sizeof(private->random_state), sizeof(seed)));
+	pg_prng_fseed(&private->random_state, seed);
 }
 
 double
@@ -36,5 +29,5 @@ geqo_rand(PlannerInfo *root)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	return pg_erand48(private->random_state);
+	return pg_prng_f64(&private->random_state);
 }
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 098bbb372b..9fc0b108dd 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -21,6 +21,7 @@
 
 #include "catalog/pg_type.h"
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/shortest_dec.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
@@ -65,7 +66,7 @@ float8		degree_c_one = 1.0;
 
 /* State for drandom() and setseed() */
 static bool drandom_seed_set = false;
-static unsigned short drandom_seed[3] = {0, 0, 0};
+static pg_prng_state drandom_seed = PG_PRNG_DEFAULT_STATE;
 
 /* Local function prototypes */
 static double sind_q1(double x);
@@ -2762,22 +2763,20 @@ drandom(PG_FUNCTION_ARGS)
 		 * Should that fail for some reason, we fall back on a lower-quality
 		 * seed based on current time and PID.
 		 */
-		if (!pg_strong_random(drandom_seed, sizeof(drandom_seed)))
+		if (!pg_strong_random(&drandom_seed, sizeof(drandom_seed)))
 		{
 			TimestampTz now = GetCurrentTimestamp();
 			uint64		iseed;
 
 			/* Mix the PID with the most predictable bits of the timestamp */
 			iseed = (uint64) now ^ ((uint64) MyProcPid << 32);
-			drandom_seed[0] = (unsigned short) iseed;
-			drandom_seed[1] = (unsigned short) (iseed >> 16);
-			drandom_seed[2] = (unsigned short) (iseed >> 32);
+			pg_prng_seed(&drandom_seed, iseed);
 		}
 		drandom_seed_set = true;
 	}
 
-	/* pg_erand48 produces desired result range [0.0 - 1.0) */
-	result = pg_erand48(drandom_seed);
+	/* produces desired result range [0.0 - 1.0) */
+	result = pg_prng_f64(&drandom_seed);
 
 	PG_RETURN_FLOAT8(result);
 }
@@ -2790,7 +2789,6 @@ Datum
 setseed(PG_FUNCTION_ARGS)
 {
 	float8		seed = PG_GETARG_FLOAT8(0);
-	uint64		iseed;
 
 	if (seed < -1 || seed > 1 || isnan(seed))
 		ereport(ERROR,
@@ -2798,11 +2796,7 @@ setseed(PG_FUNCTION_ARGS)
 				 errmsg("setseed parameter %g is out of allowed range [-1,1]",
 						seed)));
 
-	/* Use sign bit + 47 fractional bits to fill drandom_seed[] */
-	iseed = (int64) (seed * (float8) UINT64CONST(0x7FFFFFFFFFFF));
-	drandom_seed[0] = (unsigned short) iseed;
-	drandom_seed[1] = (unsigned short) (iseed >> 16);
-	drandom_seed[2] = (unsigned short) (iseed >> 32);
+	pg_prng_fseed(&drandom_seed, seed);
 	drandom_seed_set = true;
 
 	PG_RETURN_VOID();
diff --git a/src/backend/utils/misc/sampling.c b/src/backend/utils/misc/sampling.c
index 0c327e823f..7b59128878 100644
--- a/src/backend/utils/misc/sampling.c
+++ b/src/backend/utils/misc/sampling.c
@@ -49,7 +49,7 @@ BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize,
 	bs->t = 0;					/* blocks scanned so far */
 	bs->m = 0;					/* blocks selected so far */
 
-	sampler_random_init_state(randseed, bs->randstate);
+	sampler_random_init_state(randseed, &bs->randstate);
 
 	return Min(bs->n, bs->N);
 }
@@ -98,7 +98,7 @@ BlockSampler_Next(BlockSampler bs)
 	 * less than k, which means that we cannot fail to select enough blocks.
 	 *----------
 	 */
-	V = sampler_random_fract(bs->randstate);
+	V = sampler_random_fract(&bs->randstate);
 	p = 1.0 - (double) k / (double) K;
 	while (V < p)
 	{
@@ -136,10 +136,10 @@ reservoir_init_selection_state(ReservoirState rs, int n)
 	 * Reservoir sampling is not used anywhere where it would need to return
 	 * repeatable results so we can initialize it randomly.
 	 */
-	sampler_random_init_state(random(), rs->randstate);
+	sampler_random_init_state(random(), &rs->randstate);
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	rs->W = exp(-log(sampler_random_fract(rs->randstate)) / n);
+	rs->W = exp(-log(sampler_random_fract(&rs->randstate)) / n);
 }
 
 double
@@ -154,7 +154,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 		double		V,
 					quot;
 
-		V = sampler_random_fract(rs->randstate);	/* Generate V */
+		V = sampler_random_fract(&rs->randstate);	/* Generate V */
 		S = 0;
 		t += 1;
 		/* Note: "num" in Vitter's code is always equal to t - n */
@@ -186,7 +186,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 						tmp;
 
 			/* Generate U and X */
-			U = sampler_random_fract(rs->randstate);
+			U = sampler_random_fract(&rs->randstate);
 			X = t * (W - 1.0);
 			S = floor(X);		/* S is tentatively set to floor(X) */
 			/* Test if U <= h(S)/cg(X) in the manner of (6.3) */
@@ -215,7 +215,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 				y *= numer / denom;
 				denom -= 1;
 			}
-			W = exp(-log(sampler_random_fract(rs->randstate)) / n); /* Generate W in advance */
+			W = exp(-log(sampler_random_fract(&rs->randstate)) / n); /* Generate W in advance */
 			if (exp(log(y) / n) <= (t + X) / t)
 				break;
 		}
@@ -230,24 +230,22 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
  *----------
  */
 void
-sampler_random_init_state(long seed, SamplerRandomState randstate)
+sampler_random_init_state(long seed, pg_prng_state *randstate)
 {
-	randstate[0] = 0x330e;		/* same as pg_erand48, but could be anything */
-	randstate[1] = (unsigned short) seed;
-	randstate[2] = (unsigned short) (seed >> 16);
+	pg_prng_seed(randstate, (uint64_t) seed);
 }
 
 /* Select a random value R uniformly distributed in (0 - 1) */
 double
-sampler_random_fract(SamplerRandomState randstate)
+sampler_random_fract(pg_prng_state *randstate)
 {
 	double		res;
 
-	/* pg_erand48 returns a value in [0.0 - 1.0), so we must reject 0 */
+	/* pg_prng_f64 returned value in [0.0 - 1.0), so we must reject 0.0 */
 	do
 	{
-		res = pg_erand48(randstate);
-	} while (res == 0.0);
+		res = pg_prng_f64(randstate);
+	} while (unlikely(res == 0.0));
 	return res;
 }
 
@@ -266,22 +264,28 @@ double
 anl_random_fract(void)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(random(), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* and compute a random fraction */
-	return sampler_random_fract(oldrs.randstate);
+	return sampler_random_fract(&oldrs.randstate);
 }
 
 double
 anl_init_selection_state(int n)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(random(), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	return exp(-log(sampler_random_fract(oldrs.randstate)) / n);
+	return exp(-log(sampler_random_fract(&oldrs.randstate)) / n);
 }
 
 double
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 4aeccd93af..6d749bbbb6 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -58,6 +58,7 @@
 #endif
 
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/logging.h"
 #include "common/string.h"
 #include "common/username.h"
@@ -343,16 +344,8 @@ typedef struct StatsData
 	SimpleStats lag;
 } StatsData;
 
-/*
- * Struct to keep random state.
- */
-typedef struct RandomState
-{
-	unsigned short xseed[3];
-} RandomState;
-
 /* 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 */
 static THREAD_BARRIER_T barrier;
@@ -454,7 +447,7 @@ typedef struct
 	 * Separate randomness for each client. This is used for random functions
 	 * 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			command;		/* command number in script */
@@ -491,9 +484,9 @@ typedef struct
 	 * random state to make all of them independent of each other and
 	 * therefore deterministic at the thread level.
 	 */
-	RandomState ts_choose_rs;	/* random state for selecting a script */
-	RandomState ts_throttle_rs; /* random state for transaction throttling */
-	RandomState ts_sample_rs;	/* random state for log sampling */
+	pg_prng_state	ts_choose_rs;	/* random state for selecting a script */
+	pg_prng_state	ts_throttle_rs; /* random state for transaction throttling */
+	pg_prng_state	ts_sample_rs;	/* random state for log sampling */
 
 	int64		throttle_trigger;	/* previous/next throttling (us) */
 	FILE	   *logfile;		/* where to log, or NULL */
@@ -891,42 +884,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.
  */
 static void
-initRandomState(RandomState *random_state)
+initRandomState(pg_prng_state *state)
 {
-	random_state->xseed[0] = (unsigned short)
-		(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);
+	pg_prng_seed(state, pg_prng_u64(&base_random_sequence));
 }
 
+
 /*
- * Random number generator: uniform distribution from min to max inclusive.
+ * PRNG: uniform distribution from min to max inclusive.
  *
  * Although the limits are expressed as int64, you can't generate the full
  * int64 range in one call, because the difference of the limits mustn't
- * overflow int64.  In practice it's unwise to ask for more than an int32
- * range, because of the limited precision of pg_erand48().
+ * overflow int64.
  */
 static int64
-getrand(RandomState *random_state, int64 min, int64 max)
+getrand(pg_prng_state *state, int64 min, int64 max)
 {
-	/*
-	 * 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));
+	return min + (int64) pg_prng_u64_range(state, max - min + 1);
 }
 
 /*
@@ -935,8 +914,7 @@ getrand(RandomState *random_state, int64 min, int64 max)
  * value is exp(-parameter).
  */
 static int64
-getExponentialRand(RandomState *random_state, int64 min, int64 max,
-				   double parameter)
+getExponentialRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		cut,
 				uniform,
@@ -946,7 +924,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 	Assert(parameter > 0.0);
 	cut = exp(-parameter);
 	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	uniform = 1.0 - pg_prng_f64(state);
 
 	/*
 	 * inner expression in (cut, 1] (if parameter > 0), rand in [0, 1)
@@ -959,8 +937,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 
 /* random number generator: gaussian distribution from min to max inclusive */
 static int64
-getGaussianRand(RandomState *random_state, int64 min, int64 max,
-				double parameter)
+getGaussianRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		stdev;
 	double		rand;
@@ -983,13 +960,13 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
 	do
 	{
 		/*
-		 * pg_erand48 generates [0,1), but for the basic version of the
+		 * pg_prng_f64 generates [0,1), but for the basic version of the
 		 * Box-Muller transform the two uniformly distributed random numbers
 		 * are expected in (0, 1] (see
 		 * https://en.wikipedia.org/wiki/Box-Muller_transform)
 		 */
-		double		rand1 = 1.0 - pg_erand48(random_state->xseed);
-		double		rand2 = 1.0 - pg_erand48(random_state->xseed);
+		double		rand1 = 1.0 - pg_prng_f64(state);
+		double		rand2 = 1.0 - pg_prng_f64(state);
 
 		/* Box-Muller basic form transform */
 		double		var_sqrt = sqrt(-2.0 * log(rand1));
@@ -1019,7 +996,7 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
  * not be one.
  */
 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
@@ -1027,8 +1004,8 @@ getPoissonRand(RandomState *random_state, double center)
 	 */
 	double		uniform;
 
-	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	/* pseudo-random value in [0, 1), uniform in (0, 1] */
+	uniform = 1.0 - pg_prng_f64(state);
 
 	return (int64) (-log(uniform) * center + 0.5);
 }
@@ -1041,7 +1018,7 @@ getPoissonRand(RandomState *random_state, double center)
  * This works for s > 1.0, but may perform badly for s very close to 1.0.
  */
 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		x,
@@ -1056,8 +1033,8 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 	while (true)
 	{
 		/* random variates */
-		u = pg_erand48(random_state->xseed);
-		v = pg_erand48(random_state->xseed);
+		u = pg_prng_f64(state);
+		v = pg_prng_f64(state);
 
 		x = floor(pow(u, -1.0 / (s - 1.0)));
 
@@ -1071,14 +1048,14 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 
 /* random number generator: zipfian distribution from min to max inclusive */
 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;
 
 	/* abort if parameter is invalid */
 	Assert(MIN_ZIPFIAN_PARAM <= s && s <= MAX_ZIPFIAN_PARAM);
 
-	return min - 1 + computeIterativeZipfian(random_state, n, s);
+	return min - 1 + computeIterativeZipfian(state, n, s);
 }
 
 /*
@@ -1135,7 +1112,7 @@ getHashMurmur2(int64 val, uint64 seed)
  * For small sizes, this generates each of the (size!) possible permutations
  * of integers in the range [0, size) with roughly equal probability.  Once
  * the size is larger than 20, the number of possible permutations exceeds the
- * number of distinct states of the internal pseudorandom number generators,
+ * number of distinct states of the internal pseudorandom number generator,
  * and so not all possible permutations can be generated, but the permutations
  * chosen should continue to give the appearance of being random.
  *
@@ -1145,25 +1122,19 @@ getHashMurmur2(int64 val, uint64 seed)
 static int64
 permute(const int64 val, const int64 isize, const int64 seed)
 {
-	RandomState random_state1;
-	RandomState random_state2;
-	uint64		size;
-	uint64		v;
-	int			masklen;
-	uint64		mask;
-	int			i;
+	/* using a high-end PRNG is probably overkill */
+	pg_prng_state	state;
+	uint64			size;
+	uint64			v;
+	int				masklen;
+	uint64			mask;
+	int				i;
 
 	if (isize < 2)
 		return 0;				/* nothing to permute */
 
-	/* Initialize a pair of random states using the seed */
-	random_state1.xseed[0] = seed & 0xFFFF;
-	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;
+	/* Initialize prng state using the seed */
+	pg_prng_seed(&state, (uint64) seed);
 
 	/* Computations are performed on unsigned values */
 	size = (uint64) isize;
@@ -1209,8 +1180,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 					t;
 
 		/* Random multiply (by an odd number), XOR and rotate of lower half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		if (v <= mask)
 		{
 			v = ((v * m) ^ r) & mask;
@@ -1218,8 +1189,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random multiply (by an odd number), XOR and rotate of upper half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		t = size - 1 - v;
 		if (t <= mask)
 		{
@@ -1229,7 +1200,7 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random offset */
-		r = (uint64) getrand(&random_state2, 0, size - 1);
+		r = pg_prng_u64(&state) % (size - 1);
 		v = (v + r) % size;
 	}
 
@@ -3790,7 +3761,7 @@ doLog(TState *thread, CState *st,
 	 * to the random sample.
 	 */
 	if (sample_rate != 0.0 &&
-		pg_erand48(thread->ts_sample_rs.xseed) > sample_rate)
+		pg_prng_f64(&thread->ts_sample_rs) > sample_rate)
 		return;
 
 	/* should we aggregate the results or not? */
@@ -5714,12 +5685,11 @@ set_random_seed(const char *seed)
 
 	if (seed != NULL)
 		pg_log_info("setting random seed to %llu", (unsigned long long) iseed);
+
 	random_seed = iseed;
 
 	/* Fill base_random_sequence with low-order bits of seed */
-	base_random_sequence.xseed[0] = iseed & 0xFFFF;
-	base_random_sequence.xseed[1] = (iseed >> 16) & 0xFFFF;
-	base_random_sequence.xseed[2] = (iseed >> 32) & 0xFFFF;
+	pg_prng_seed(&base_random_sequence, (uint64_t) iseed);
 
 	return true;
 }
@@ -6405,9 +6375,7 @@ main(int argc, char **argv)
 	/* set default seed for hash functions */
 	if (lookupVariable(&state[0], "default_seed") == NULL)
 	{
-		uint64		seed =
-		((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) |
-		(((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) << 32);
+		uint64		seed = pg_prng_u64(&base_random_sequence);
 
 		for (i = 0; i < nclients; i++)
 			if (!putVariableInt(&state[i], "startup", "default_seed", (int64) seed))
diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl
index 3aa9d5d753..a5a9bcbd56 100644
--- a/src/bin/pgbench/t/001_pgbench_with_server.pl
+++ b/src/bin/pgbench/t/001_pgbench_with_server.pl
@@ -427,9 +427,9 @@ pgbench(
 
 		# After explicit seeding, the four random checks (1-3,20) are
 		# deterministic
-		qr{command=1.: int 13\b},      # uniform random
-		qr{command=2.: int 116\b},     # exponential random
-		qr{command=3.: int 1498\b},    # gaussian random
+		qr{command=1.: int 18\b},      # uniform random
+		qr{command=2.: int 100\b},     # exponential random
+		qr{command=3.: int 1526\b},    # gaussian random
 		qr{command=4.: int 4\b},
 		qr{command=5.: int 5\b},
 		qr{command=6.: int 6\b},
@@ -496,6 +496,7 @@ pgbench(
 		qr{command=109.: boolean true\b},
 		qr{command=110.: boolean true\b},
 		qr{command=111.: boolean true\b},
+		qr{command=113.: boolean true\b},
 	],
 	'pgbench expressions',
 	{
@@ -639,8 +640,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))
 -- actual values
 \set t debug(permute(:v, 1) = 0)
-\set t debug(permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1 and \
-             permute(0, 2, 5435) = 1 and permute(1, 2, 5435) = 0)
+\set t debug(permute(0, 2, 5431) = 1 and permute(1, 2, 5431) = 0 and \
+             permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1)
+-- 63 bits tests for checking permute portability across architectures
+\set size debug(:max - 10)
+\set t debug(permute(:size-1, :size, 5432) = 4352241160009873020 and \
+             permute(:size-2, :size, 5432) = 4990004119691638710 and \
+             permute(:size-3, :size, 5432) = 5868029867256127549 and \
+             permute(:size-4, :size, 5432) = 1238830324886341378 and \
+             permute(:size-5, :size, 5432) = 7415914367102906897 and \
+             permute(:size-6, :size, 5432) = 3214501037984818995 and \
+             permute(:size-7, :size, 5432) =  600660351261124695)
 }
 	});
 
diff --git a/src/common/Makefile b/src/common/Makefile
index 38a8599337..9aee26f694 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -67,6 +67,7 @@ OBJS_COMMON = \
 	md5_common.o \
 	pg_get_line.o \
 	pg_lzcompress.o \
+	pg_prng.o \
 	pgfnames.o \
 	psprintf.o \
 	relpath.o \
diff --git a/src/common/pg_prng.c b/src/common/pg_prng.c
new file mode 100644
index 0000000000..fac7a04a49
--- /dev/null
+++ b/src/common/pg_prng.c
@@ -0,0 +1,222 @@
+/*
+ * Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * Previous version was the 1988 (?) rand48 standard with 48 bits state LCG,
+ * designed for running on 32 bits or even 16 bits architectures, and to produce
+ * 32 bit ints and floats (i.e. *not* doubles).
+ *
+ * The implied 16-bits unpacking/packing may have a detrimental performance impact
+ * on modern 64 bits architectures.
+ *
+ * We (probably) want:
+ * - one reasonable default PRNG for all pg internal uses.
+ * - NOT to invent a new design!
+ * - something fast, close to rand48 (which basically does 2 arithmetic ops)
+ *   no need for something cryptographic though, which would imply slow
+ * - to produce 64 bits integers & doubles with a 52 bits mantissa
+ *   so state size > 64 bits.
+ * - a small state though, because we might generate quite a few of them
+ *   for different purposes so state size <= 256 or even <= 128 bits
+ * - the state to be aligned to whatever, so state size = 128 bits
+ * - 64 bits operations for efficiency on modern architectures,
+ *   but NOT 128 bits operations.
+ * - not to depend on special hardware for speed (eg MMX/SSE/AES).
+ * - not something with obvious known and relevant defects.
+ * - not something with "rights" attached.
+ *
+ * These constraints reduce a lot the available options from
+ * https://en.wikipedia.org/wiki/List_of_random_number_generators
+ *
+ * LCG
+ * - POSIX rand48: m = 2^48, a = 25214903917, c = 11, extract 47...16
+ * - MMIX by Donald Knuth: m = 2^64, a = 6364136223846793005, c = 1442695040888963407
+ * - very bad on low bits, which MUST NOT BE USED
+ *   a 128 bits would do, but no clear standards, and it would imply 128-bit ops,
+ *   see PCG below.
+ *
+ * PCG Family (https://www.pcg-random.org/)
+ * - this is basically an LCG with an improved outpout function
+ *   so as to avoid typical LCG issues and pass stats tests.
+ * - recommands state size to be twice the output size,
+ *   which seems a reasonable requirement.
+ * - this implies 128 bit ops for our target parameters,
+ *   otherwise it is a fairly good candidate for simplicity,
+ *   efficiency and quality.
+ *
+ * Xor-Shift-Rotate PRNGs:
+ * - a generic annoying feature of this generator class is that there is one
+ *   special all-zero state which must not be used, inducing a constraint on
+ *   initialization, and when the generator get close to it (i.e. many zeros
+ *   in the state) it takes some iterations to recover, with small lumps
+ *   of close/related/bad numbers generated in the process.
+ * Xoroshiro128+
+ * - fails statistical linearity tests in the low bits.
+ * Xoroshiro128**
+ *   https://prng.di.unimi.it/xoroshiro128starstar.c
+ * - mild and irrelevant issues: close to zero state, stats issues after some transforms
+ * Xoshiro256**
+ * - it looks good, but some issues (no big deal for pg usage IMO)
+ *   https://www.pcg-random.org/posts/a-quick-look-at-xoshiro256.html
+ *   mild problems getting out of closes-to-zero state
+ *
+ * MT19937-64
+ * + default in many places, standard
+ * - huge 2.5 KiB state (but TinyMT, 127 bits state space)
+ * - slow (but SFMT, twice as fast)
+ * - vulnerabilities on tinyMT?
+ *
+ * WELL
+ * - large state, 32-bit oriented
+ *
+ * ACORN Family (https://en.wikipedia.org/wiki/ACORN_(PRNG))
+ * - Fortran / double oriented, no C implementation found?!
+ * - medium large state "In practice we recommend using k > 10, M = 2^60 (for general application)
+ *   or M = 2^120 (for demanding applications requiring high-quality pseudo-random numbers
+ *   that will consistently pass all the tests in standard test packages such as TestU01)
+ *   and choose any odd value less than M for the seed."
+ *   k = 11 => 88 bytes/704-bits state, too large:-(
+ *
+ * Splitmix?
+ * Others?
+ *
+ * The conclusion is that we use xor-shift-rotate generator below.
+ */
+
+#include "c.h"
+#include "common/pg_prng.h"
+#include "port/pg_bitutils.h"
+#include <math.h>
+
+#define FIRST_BIT_MASK UINT64CONST(0x8000000000000000)
+#define RIGHT_HALF_MASK UINT64CONST(0x00000000FFFFFFFF)
+#define DMANTISSA_MASK UINT64CONST(0x000FFFFFFFFFFFFF)
+
+/* 64-bits rotate left */
+static inline uint64_t rotl(const uint64_t x, const int bits)
+{
+	return (x << bits) | (x >> (64 - bits));
+}
+
+/* smallest mask holding a value */
+static inline uint64_t mask_u64(uint64_t u)
+{
+	return (((uint64_t) 1) << (pg_leftmost_one_pos64(u) + 1)) - 1;
+}
+
+/* another 64-bits generator is used for state initialization or iterations */
+static uint64_t splitmix64(uint64_t * state)
+{
+	/* state update */
+	uint64_t	val = (*state += UINT64CONST(0x9E3779B97f4A7C15));
+
+	/* value extraction */
+	val = (val ^ (val >> 30)) * UINT64CONST(0xBF58476D1CE4E5B9);
+	val = (val ^ (val >> 27)) * UINT64CONST(0x94D049BB133111EB);
+
+	return val ^ (val >> 31);
+}
+
+/* seed prng state from a 64 bits integer, ensuring non zero */
+void pg_prng_seed(pg_prng_state *state, uint64_t seed)
+{
+	state->s0 = splitmix64(&seed);
+	state->s1 = splitmix64(&seed);
+}
+
+/* seed with 53 bits (mantissa & sign) from a float */
+void pg_prng_fseed(pg_prng_state *state, double fseed)
+{
+	uint64 seed = (int64) (((double) DMANTISSA_MASK) * fseed);
+	pg_prng_seed(state, seed);
+}
+
+/* strong random seeding */
+void pg_prng_strong_seed(pg_prng_state *state)
+{
+	pg_strong_random((void *) state, sizeof(pg_prng_state));
+
+	/* avoid zero with Donald Knuth's LCG parameters */
+	if (unlikely(state->s0 == 0 && state->s1 == 0))
+	{
+		/* should it warn that something is amiss if we get there? */
+		pg_prng_state def = PG_PRNG_DEFAULT_STATE;
+		*state = def;
+	}
+}
+
+/* generator & state update */
+static uint64_t xoroshiro128ss(pg_prng_state *state)
+{
+	const uint64_t	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;
+}
+
+/* u64 generator */
+uint64_t pg_prng_u64(pg_prng_state *state)
+{
+	return xoroshiro128ss(state);
+}
+
+/* select in a range with bitmask rejection */
+uint64_t pg_prng_u64_range(pg_prng_state *state, uint64_t range)
+{
+	/* we always iterate over the state once, for determinism */
+	uint64_t next = xoroshiro128ss(state);
+	uint64_t val = next;
+
+	if (range >= 2)
+	{
+		uint64_t mask = mask_u64(range-1);
+
+		/* iterate over splitmix till val in [0, range) */
+		while ((val = val & mask) >= range)
+			val = splitmix64(&next);
+	}
+	else
+		val = 0;
+
+	return val;
+}
+
+/* i64 generator */
+int64_t pg_prng_i64(pg_prng_state *state)
+{
+	return (int64_t) xoroshiro128ss(state);
+}
+
+/* u32 generator */
+uint32_t pg_prng_u32(pg_prng_state *state)
+{
+	const uint64_t v = xoroshiro128ss(state);
+	return (uint32_t) (((v >> 32) ^ v) & RIGHT_HALF_MASK);
+}
+
+/* i32 generator */
+int32_t pg_prng_i32(pg_prng_state *state)
+{
+	const uint64_t v = xoroshiro128ss(state);
+	return (int32_t) (((v >> 32) ^ v) & RIGHT_HALF_MASK);
+}
+
+/* double generator */
+double pg_prng_f64(pg_prng_state *state)
+{
+	uint64_t v = xoroshiro128ss(state);
+	return ldexp((double) (v & DMANTISSA_MASK), -52);
+}
+
+/* bool generator */
+bool pg_prng_bool(pg_prng_state *state)
+{
+	uint64_t v = xoroshiro128ss(state);
+	return (v & FIRST_BIT_MASK) == FIRST_BIT_MASK;
+}
diff --git a/src/include/common/pg_prng.h b/src/include/common/pg_prng.h
new file mode 100644
index 0000000000..6d582cc3dc
--- /dev/null
+++ b/src/include/common/pg_prng.h
@@ -0,0 +1,34 @@
+/*-------------------------------------------------------------------------
+ *
+ * PRNG: internal Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * src/include/common/pg_prng.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_PRNG_H
+#define PG_PRNG_H
+
+/* 128 bits state */
+typedef struct pg_prng_state {
+	uint64_t	s0, s1;
+} pg_prng_state;
+
+/* use Donald Knuth's LCG constants for default state */
+#define PG_PRNG_DEFAULT_STATE \
+	{ UINT64CONST(0x5851F42D4C957F2D), UINT64CONST(0x14057B7EF767814F) }
+
+extern void pg_prng_seed(pg_prng_state *state, uint64_t seed);
+extern void pg_prng_fseed(pg_prng_state *state, double fseed);
+extern void pg_prng_strong_seed(pg_prng_state *state);
+extern uint64_t pg_prng_u64(pg_prng_state *state);
+extern uint64_t pg_prng_u64_range(pg_prng_state *state, uint64_t range);
+extern int64_t pg_prng_i64(pg_prng_state *state);
+extern uint32_t pg_prng_u32(pg_prng_state *state);
+extern int32_t pg_prng_i32(pg_prng_state *state);
+extern double pg_prng_f64(pg_prng_state *state);
+extern bool pg_prng_bool(pg_prng_state *state);
+
+#endif							/* PG_PRNG_H */
diff --git a/src/include/optimizer/geqo.h b/src/include/optimizer/geqo.h
index 24dcdfb6cc..b28e4f084a 100644
--- a/src/include/optimizer/geqo.h
+++ b/src/include/optimizer/geqo.h
@@ -22,6 +22,7 @@
 #ifndef GEQO_H
 #define GEQO_H
 
+#include "common/pg_prng.h"
 #include "nodes/pathnodes.h"
 #include "optimizer/geqo_gene.h"
 
@@ -72,8 +73,8 @@ extern double Geqo_seed;		/* 0 .. 1 */
  */
 typedef struct
 {
-	List	   *initial_rels;	/* the base relations we are joining */
-	unsigned short random_state[3]; /* state for pg_erand48() */
+	List		   *initial_rels;	/* the base relations we are joining */
+	pg_prng_state	random_state;   /* PRNG state */
 } GeqoPrivateData;
 
 
diff --git a/src/include/port.h b/src/include/port.h
index 82f63de325..eb18665196 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -356,11 +356,6 @@ extern int	gettimeofday(struct timeval *tp, struct timezone *tzp);
 #define pgoff_t off_t
 #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
 extern int	fls(int mask);
 #endif
diff --git a/src/include/utils/sampling.h b/src/include/utils/sampling.h
index a58d14281b..c0b1c6a645 100644
--- a/src/include/utils/sampling.h
+++ b/src/include/utils/sampling.h
@@ -13,15 +13,14 @@
 #ifndef SAMPLING_H
 #define SAMPLING_H
 
+#include "common/pg_prng.h"
 #include "storage/block.h"		/* for typedef BlockNumber */
 
 
 /* Random generator for sampling code */
-typedef unsigned short SamplerRandomState[3];
-
 extern void sampler_random_init_state(long seed,
-									  SamplerRandomState randstate);
-extern double sampler_random_fract(SamplerRandomState randstate);
+									  pg_prng_state *randstate);
+extern double sampler_random_fract(pg_prng_state *randstate);
 
 /* Block sampling methods */
 
@@ -32,7 +31,7 @@ typedef struct
 	int			n;				/* desired sample size */
 	BlockNumber t;				/* current block number */
 	int			m;				/* blocks selected so far */
-	SamplerRandomState randstate;	/* random generator state */
+	pg_prng_state randstate;	/* random generator state */
 } BlockSamplerData;
 
 typedef BlockSamplerData *BlockSampler;
@@ -46,8 +45,9 @@ extern BlockNumber BlockSampler_Next(BlockSampler bs);
 
 typedef struct
 {
-	double		W;
-	SamplerRandomState randstate;	/* random generator state */
+	double			W;
+	pg_prng_state	randstate;	/* random generator state */
+	bool			randstate_initialized;
 } ReservoirStateData;
 
 typedef ReservoirStateData *ReservoirState;
diff --git a/src/port/Makefile b/src/port/Makefile
index 52dbf5783f..b3754d8940 100644
--- a/src/port/Makefile
+++ b/src/port/Makefile
@@ -42,7 +42,6 @@ OBJS = \
 	$(PG_CRC32C_OBJS) \
 	bsearch_arg.o \
 	chklocale.o \
-	erand48.o \
 	inet_net_ntop.o \
 	noblock.o \
 	path.o \
diff --git a/src/port/erand48.c b/src/port/erand48.c
deleted file mode 100644
index a82ea94220..0000000000
--- a/src/port/erand48.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/port/random.c b/src/port/random.c
deleted file mode 100644
index 2dd59a0829..0000000000
--- a/src/port/random.c
+++ /dev/null
@@ -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();
-}
diff --git a/src/port/srandom.c b/src/port/srandom.c
deleted file mode 100644
index cf1007b2ef..0000000000
--- a/src/port/srandom.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/test/modules/test_integerset/test_integerset.c b/src/test/modules/test_integerset/test_integerset.c
index 21c6f49b37..7fe411435d 100644
--- a/src/test/modules/test_integerset/test_integerset.c
+++ b/src/test/modules/test_integerset/test_integerset.c
@@ -99,12 +99,16 @@ static void check_with_filler(IntegerSet *intset, uint64 x, uint64 value, uint64
 static void test_single_value_and_filler(uint64 value, uint64 filler_min, uint64 filler_max);
 static void test_huge_distances(void);
 
+static pg_prng_state	pr_state;
+
 /*
  * SQL-callable entry point to perform all tests.
  */
 Datum
 test_integerset(PG_FUNCTION_ARGS)
 {
+	pg_prng_strong_seed(&pr_state);
+
 	/* Tests for various corner cases */
 	test_empty();
 	test_huge_distances();
@@ -248,7 +252,7 @@ test_pattern(const test_spec *spec)
 		 * only a small part of the integer space is used.  We would very
 		 * rarely hit values that are actually in the set.
 		 */
-		x = (pg_lrand48() << 31) | pg_lrand48();
+		x = pg_prng_u64(&pr_state);
 		x = x % (last_int + 1000);
 
 		/* Do we expect this value to be present in the set? */
@@ -571,7 +575,7 @@ test_huge_distances(void)
 	 */
 	while (num_values < 1000)
 	{
-		val += pg_lrand48();
+		val += pg_prng_i32(&pr_state);
 		values[num_values++] = val;
 	}
 
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 233ddbf4c2..29d0f467cb 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -103,9 +103,9 @@ sub mkvcbuild
 	$solution = CreateSolution($vsVersion, $config);
 
 	our @pgportfiles = qw(
-	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c random.c
-	  srandom.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
+	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c
+	  getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
+	  snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
 	  dirent.c dlopen.c getopt.c getopt_long.c link.c
 	  pread.c preadv.c pwrite.c pwritev.c pg_bitutils.c
 	  pg_strong_random.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c
@@ -131,9 +131,9 @@ sub mkvcbuild
 	  config_info.c controldata_utils.c d2s.c encnames.c exec.c
 	  f2s.c file_perm.c file_utils.c hashfn.c hex.c ip.c jsonapi.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
-	  saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c username.c
-	  wait_error.c wchar.c);
+	  pg_get_line.c pg_lzcompress.c pg_prng.c pgfnames.c psprintf.c relpath.c
+	  rmtree.c saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c
+	  username.c wait_error.c wchar.c);
 
 	if ($solution->{options}->{openssl})
 	{
#20Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Fabien COELHO (#19)
1 attachment(s)
Re: rand48 replacement

Here is a v4, which:

- moves the stuff to common and fully removes random/srandom (Tom)
- includes a range generation function based on the bitmask method (Dean)
but iterates with splitmix so that the state always advances once (Me)

And a v5 where an unused test file does also compile if we insist.

--
Fabien.

Attachments:

prng-5.patchtext/x-diff; name=prng-5.patchDownload
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 2c2f149fb0..146b524076 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -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]);
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index fafbab6b02..3009861e45 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -5152,7 +5152,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]);
 		}
diff --git a/contrib/tsm_system_rows/tsm_system_rows.c b/contrib/tsm_system_rows/tsm_system_rows.c
index 4996612902..1a46d4b143 100644
--- a/contrib/tsm_system_rows/tsm_system_rows.c
+++ b/contrib/tsm_system_rows/tsm_system_rows.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/contrib/tsm_system_time/tsm_system_time.c b/contrib/tsm_system_time/tsm_system_time.c
index 788d8f9a68..36acc6c106 100644
--- a/contrib/tsm_system_time/tsm_system_time.c
+++ b/contrib/tsm_system_time/tsm_system_time.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 0c9591415e..1ddbeed2dc 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1308,7 +1308,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 					 * Found a suitable tuple, so save it, replacing one old
 					 * tuple at random
 					 */
-					int			k = (int) (targrows * sampler_random_fract(rstate.randstate));
+					int			k = (int) (targrows * sampler_random_fract(&rstate.randstate));
 
 					Assert(k >= 0 && k < targrows);
 					heap_freetuple(rows[k]);
diff --git a/src/backend/optimizer/geqo/geqo_random.c b/src/backend/optimizer/geqo/geqo_random.c
index f21bc047e6..8b42a9ffba 100644
--- a/src/backend/optimizer/geqo/geqo_random.c
+++ b/src/backend/optimizer/geqo/geqo_random.c
@@ -21,14 +21,7 @@ geqo_set_seed(PlannerInfo *root, double seed)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	/*
-	 * XXX. This seeding algorithm could certainly be improved - but it is not
-	 * critical to do so.
-	 */
-	memset(private->random_state, 0, sizeof(private->random_state));
-	memcpy(private->random_state,
-		   &seed,
-		   Min(sizeof(private->random_state), sizeof(seed)));
+	pg_prng_fseed(&private->random_state, seed);
 }
 
 double
@@ -36,5 +29,5 @@ geqo_rand(PlannerInfo *root)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	return pg_erand48(private->random_state);
+	return pg_prng_f64(&private->random_state);
 }
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 098bbb372b..9fc0b108dd 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -21,6 +21,7 @@
 
 #include "catalog/pg_type.h"
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/shortest_dec.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
@@ -65,7 +66,7 @@ float8		degree_c_one = 1.0;
 
 /* State for drandom() and setseed() */
 static bool drandom_seed_set = false;
-static unsigned short drandom_seed[3] = {0, 0, 0};
+static pg_prng_state drandom_seed = PG_PRNG_DEFAULT_STATE;
 
 /* Local function prototypes */
 static double sind_q1(double x);
@@ -2762,22 +2763,20 @@ drandom(PG_FUNCTION_ARGS)
 		 * Should that fail for some reason, we fall back on a lower-quality
 		 * seed based on current time and PID.
 		 */
-		if (!pg_strong_random(drandom_seed, sizeof(drandom_seed)))
+		if (!pg_strong_random(&drandom_seed, sizeof(drandom_seed)))
 		{
 			TimestampTz now = GetCurrentTimestamp();
 			uint64		iseed;
 
 			/* Mix the PID with the most predictable bits of the timestamp */
 			iseed = (uint64) now ^ ((uint64) MyProcPid << 32);
-			drandom_seed[0] = (unsigned short) iseed;
-			drandom_seed[1] = (unsigned short) (iseed >> 16);
-			drandom_seed[2] = (unsigned short) (iseed >> 32);
+			pg_prng_seed(&drandom_seed, iseed);
 		}
 		drandom_seed_set = true;
 	}
 
-	/* pg_erand48 produces desired result range [0.0 - 1.0) */
-	result = pg_erand48(drandom_seed);
+	/* produces desired result range [0.0 - 1.0) */
+	result = pg_prng_f64(&drandom_seed);
 
 	PG_RETURN_FLOAT8(result);
 }
@@ -2790,7 +2789,6 @@ Datum
 setseed(PG_FUNCTION_ARGS)
 {
 	float8		seed = PG_GETARG_FLOAT8(0);
-	uint64		iseed;
 
 	if (seed < -1 || seed > 1 || isnan(seed))
 		ereport(ERROR,
@@ -2798,11 +2796,7 @@ setseed(PG_FUNCTION_ARGS)
 				 errmsg("setseed parameter %g is out of allowed range [-1,1]",
 						seed)));
 
-	/* Use sign bit + 47 fractional bits to fill drandom_seed[] */
-	iseed = (int64) (seed * (float8) UINT64CONST(0x7FFFFFFFFFFF));
-	drandom_seed[0] = (unsigned short) iseed;
-	drandom_seed[1] = (unsigned short) (iseed >> 16);
-	drandom_seed[2] = (unsigned short) (iseed >> 32);
+	pg_prng_fseed(&drandom_seed, seed);
 	drandom_seed_set = true;
 
 	PG_RETURN_VOID();
diff --git a/src/backend/utils/misc/sampling.c b/src/backend/utils/misc/sampling.c
index 0c327e823f..7b59128878 100644
--- a/src/backend/utils/misc/sampling.c
+++ b/src/backend/utils/misc/sampling.c
@@ -49,7 +49,7 @@ BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize,
 	bs->t = 0;					/* blocks scanned so far */
 	bs->m = 0;					/* blocks selected so far */
 
-	sampler_random_init_state(randseed, bs->randstate);
+	sampler_random_init_state(randseed, &bs->randstate);
 
 	return Min(bs->n, bs->N);
 }
@@ -98,7 +98,7 @@ BlockSampler_Next(BlockSampler bs)
 	 * less than k, which means that we cannot fail to select enough blocks.
 	 *----------
 	 */
-	V = sampler_random_fract(bs->randstate);
+	V = sampler_random_fract(&bs->randstate);
 	p = 1.0 - (double) k / (double) K;
 	while (V < p)
 	{
@@ -136,10 +136,10 @@ reservoir_init_selection_state(ReservoirState rs, int n)
 	 * Reservoir sampling is not used anywhere where it would need to return
 	 * repeatable results so we can initialize it randomly.
 	 */
-	sampler_random_init_state(random(), rs->randstate);
+	sampler_random_init_state(random(), &rs->randstate);
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	rs->W = exp(-log(sampler_random_fract(rs->randstate)) / n);
+	rs->W = exp(-log(sampler_random_fract(&rs->randstate)) / n);
 }
 
 double
@@ -154,7 +154,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 		double		V,
 					quot;
 
-		V = sampler_random_fract(rs->randstate);	/* Generate V */
+		V = sampler_random_fract(&rs->randstate);	/* Generate V */
 		S = 0;
 		t += 1;
 		/* Note: "num" in Vitter's code is always equal to t - n */
@@ -186,7 +186,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 						tmp;
 
 			/* Generate U and X */
-			U = sampler_random_fract(rs->randstate);
+			U = sampler_random_fract(&rs->randstate);
 			X = t * (W - 1.0);
 			S = floor(X);		/* S is tentatively set to floor(X) */
 			/* Test if U <= h(S)/cg(X) in the manner of (6.3) */
@@ -215,7 +215,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 				y *= numer / denom;
 				denom -= 1;
 			}
-			W = exp(-log(sampler_random_fract(rs->randstate)) / n); /* Generate W in advance */
+			W = exp(-log(sampler_random_fract(&rs->randstate)) / n); /* Generate W in advance */
 			if (exp(log(y) / n) <= (t + X) / t)
 				break;
 		}
@@ -230,24 +230,22 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
  *----------
  */
 void
-sampler_random_init_state(long seed, SamplerRandomState randstate)
+sampler_random_init_state(long seed, pg_prng_state *randstate)
 {
-	randstate[0] = 0x330e;		/* same as pg_erand48, but could be anything */
-	randstate[1] = (unsigned short) seed;
-	randstate[2] = (unsigned short) (seed >> 16);
+	pg_prng_seed(randstate, (uint64_t) seed);
 }
 
 /* Select a random value R uniformly distributed in (0 - 1) */
 double
-sampler_random_fract(SamplerRandomState randstate)
+sampler_random_fract(pg_prng_state *randstate)
 {
 	double		res;
 
-	/* pg_erand48 returns a value in [0.0 - 1.0), so we must reject 0 */
+	/* pg_prng_f64 returned value in [0.0 - 1.0), so we must reject 0.0 */
 	do
 	{
-		res = pg_erand48(randstate);
-	} while (res == 0.0);
+		res = pg_prng_f64(randstate);
+	} while (unlikely(res == 0.0));
 	return res;
 }
 
@@ -266,22 +264,28 @@ double
 anl_random_fract(void)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(random(), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* and compute a random fraction */
-	return sampler_random_fract(oldrs.randstate);
+	return sampler_random_fract(&oldrs.randstate);
 }
 
 double
 anl_init_selection_state(int n)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(random(), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	return exp(-log(sampler_random_fract(oldrs.randstate)) / n);
+	return exp(-log(sampler_random_fract(&oldrs.randstate)) / n);
 }
 
 double
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 4aeccd93af..6d749bbbb6 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -58,6 +58,7 @@
 #endif
 
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/logging.h"
 #include "common/string.h"
 #include "common/username.h"
@@ -343,16 +344,8 @@ typedef struct StatsData
 	SimpleStats lag;
 } StatsData;
 
-/*
- * Struct to keep random state.
- */
-typedef struct RandomState
-{
-	unsigned short xseed[3];
-} RandomState;
-
 /* 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 */
 static THREAD_BARRIER_T barrier;
@@ -454,7 +447,7 @@ typedef struct
 	 * Separate randomness for each client. This is used for random functions
 	 * 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			command;		/* command number in script */
@@ -491,9 +484,9 @@ typedef struct
 	 * random state to make all of them independent of each other and
 	 * therefore deterministic at the thread level.
 	 */
-	RandomState ts_choose_rs;	/* random state for selecting a script */
-	RandomState ts_throttle_rs; /* random state for transaction throttling */
-	RandomState ts_sample_rs;	/* random state for log sampling */
+	pg_prng_state	ts_choose_rs;	/* random state for selecting a script */
+	pg_prng_state	ts_throttle_rs; /* random state for transaction throttling */
+	pg_prng_state	ts_sample_rs;	/* random state for log sampling */
 
 	int64		throttle_trigger;	/* previous/next throttling (us) */
 	FILE	   *logfile;		/* where to log, or NULL */
@@ -891,42 +884,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.
  */
 static void
-initRandomState(RandomState *random_state)
+initRandomState(pg_prng_state *state)
 {
-	random_state->xseed[0] = (unsigned short)
-		(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);
+	pg_prng_seed(state, pg_prng_u64(&base_random_sequence));
 }
 
+
 /*
- * Random number generator: uniform distribution from min to max inclusive.
+ * PRNG: uniform distribution from min to max inclusive.
  *
  * Although the limits are expressed as int64, you can't generate the full
  * int64 range in one call, because the difference of the limits mustn't
- * overflow int64.  In practice it's unwise to ask for more than an int32
- * range, because of the limited precision of pg_erand48().
+ * overflow int64.
  */
 static int64
-getrand(RandomState *random_state, int64 min, int64 max)
+getrand(pg_prng_state *state, int64 min, int64 max)
 {
-	/*
-	 * 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));
+	return min + (int64) pg_prng_u64_range(state, max - min + 1);
 }
 
 /*
@@ -935,8 +914,7 @@ getrand(RandomState *random_state, int64 min, int64 max)
  * value is exp(-parameter).
  */
 static int64
-getExponentialRand(RandomState *random_state, int64 min, int64 max,
-				   double parameter)
+getExponentialRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		cut,
 				uniform,
@@ -946,7 +924,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 	Assert(parameter > 0.0);
 	cut = exp(-parameter);
 	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	uniform = 1.0 - pg_prng_f64(state);
 
 	/*
 	 * inner expression in (cut, 1] (if parameter > 0), rand in [0, 1)
@@ -959,8 +937,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 
 /* random number generator: gaussian distribution from min to max inclusive */
 static int64
-getGaussianRand(RandomState *random_state, int64 min, int64 max,
-				double parameter)
+getGaussianRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		stdev;
 	double		rand;
@@ -983,13 +960,13 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
 	do
 	{
 		/*
-		 * pg_erand48 generates [0,1), but for the basic version of the
+		 * pg_prng_f64 generates [0,1), but for the basic version of the
 		 * Box-Muller transform the two uniformly distributed random numbers
 		 * are expected in (0, 1] (see
 		 * https://en.wikipedia.org/wiki/Box-Muller_transform)
 		 */
-		double		rand1 = 1.0 - pg_erand48(random_state->xseed);
-		double		rand2 = 1.0 - pg_erand48(random_state->xseed);
+		double		rand1 = 1.0 - pg_prng_f64(state);
+		double		rand2 = 1.0 - pg_prng_f64(state);
 
 		/* Box-Muller basic form transform */
 		double		var_sqrt = sqrt(-2.0 * log(rand1));
@@ -1019,7 +996,7 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
  * not be one.
  */
 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
@@ -1027,8 +1004,8 @@ getPoissonRand(RandomState *random_state, double center)
 	 */
 	double		uniform;
 
-	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	/* pseudo-random value in [0, 1), uniform in (0, 1] */
+	uniform = 1.0 - pg_prng_f64(state);
 
 	return (int64) (-log(uniform) * center + 0.5);
 }
@@ -1041,7 +1018,7 @@ getPoissonRand(RandomState *random_state, double center)
  * This works for s > 1.0, but may perform badly for s very close to 1.0.
  */
 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		x,
@@ -1056,8 +1033,8 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 	while (true)
 	{
 		/* random variates */
-		u = pg_erand48(random_state->xseed);
-		v = pg_erand48(random_state->xseed);
+		u = pg_prng_f64(state);
+		v = pg_prng_f64(state);
 
 		x = floor(pow(u, -1.0 / (s - 1.0)));
 
@@ -1071,14 +1048,14 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 
 /* random number generator: zipfian distribution from min to max inclusive */
 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;
 
 	/* abort if parameter is invalid */
 	Assert(MIN_ZIPFIAN_PARAM <= s && s <= MAX_ZIPFIAN_PARAM);
 
-	return min - 1 + computeIterativeZipfian(random_state, n, s);
+	return min - 1 + computeIterativeZipfian(state, n, s);
 }
 
 /*
@@ -1135,7 +1112,7 @@ getHashMurmur2(int64 val, uint64 seed)
  * For small sizes, this generates each of the (size!) possible permutations
  * of integers in the range [0, size) with roughly equal probability.  Once
  * the size is larger than 20, the number of possible permutations exceeds the
- * number of distinct states of the internal pseudorandom number generators,
+ * number of distinct states of the internal pseudorandom number generator,
  * and so not all possible permutations can be generated, but the permutations
  * chosen should continue to give the appearance of being random.
  *
@@ -1145,25 +1122,19 @@ getHashMurmur2(int64 val, uint64 seed)
 static int64
 permute(const int64 val, const int64 isize, const int64 seed)
 {
-	RandomState random_state1;
-	RandomState random_state2;
-	uint64		size;
-	uint64		v;
-	int			masklen;
-	uint64		mask;
-	int			i;
+	/* using a high-end PRNG is probably overkill */
+	pg_prng_state	state;
+	uint64			size;
+	uint64			v;
+	int				masklen;
+	uint64			mask;
+	int				i;
 
 	if (isize < 2)
 		return 0;				/* nothing to permute */
 
-	/* Initialize a pair of random states using the seed */
-	random_state1.xseed[0] = seed & 0xFFFF;
-	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;
+	/* Initialize prng state using the seed */
+	pg_prng_seed(&state, (uint64) seed);
 
 	/* Computations are performed on unsigned values */
 	size = (uint64) isize;
@@ -1209,8 +1180,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 					t;
 
 		/* Random multiply (by an odd number), XOR and rotate of lower half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		if (v <= mask)
 		{
 			v = ((v * m) ^ r) & mask;
@@ -1218,8 +1189,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random multiply (by an odd number), XOR and rotate of upper half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		t = size - 1 - v;
 		if (t <= mask)
 		{
@@ -1229,7 +1200,7 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random offset */
-		r = (uint64) getrand(&random_state2, 0, size - 1);
+		r = pg_prng_u64(&state) % (size - 1);
 		v = (v + r) % size;
 	}
 
@@ -3790,7 +3761,7 @@ doLog(TState *thread, CState *st,
 	 * to the random sample.
 	 */
 	if (sample_rate != 0.0 &&
-		pg_erand48(thread->ts_sample_rs.xseed) > sample_rate)
+		pg_prng_f64(&thread->ts_sample_rs) > sample_rate)
 		return;
 
 	/* should we aggregate the results or not? */
@@ -5714,12 +5685,11 @@ set_random_seed(const char *seed)
 
 	if (seed != NULL)
 		pg_log_info("setting random seed to %llu", (unsigned long long) iseed);
+
 	random_seed = iseed;
 
 	/* Fill base_random_sequence with low-order bits of seed */
-	base_random_sequence.xseed[0] = iseed & 0xFFFF;
-	base_random_sequence.xseed[1] = (iseed >> 16) & 0xFFFF;
-	base_random_sequence.xseed[2] = (iseed >> 32) & 0xFFFF;
+	pg_prng_seed(&base_random_sequence, (uint64_t) iseed);
 
 	return true;
 }
@@ -6405,9 +6375,7 @@ main(int argc, char **argv)
 	/* set default seed for hash functions */
 	if (lookupVariable(&state[0], "default_seed") == NULL)
 	{
-		uint64		seed =
-		((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) |
-		(((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) << 32);
+		uint64		seed = pg_prng_u64(&base_random_sequence);
 
 		for (i = 0; i < nclients; i++)
 			if (!putVariableInt(&state[i], "startup", "default_seed", (int64) seed))
diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl
index 3aa9d5d753..a5a9bcbd56 100644
--- a/src/bin/pgbench/t/001_pgbench_with_server.pl
+++ b/src/bin/pgbench/t/001_pgbench_with_server.pl
@@ -427,9 +427,9 @@ pgbench(
 
 		# After explicit seeding, the four random checks (1-3,20) are
 		# deterministic
-		qr{command=1.: int 13\b},      # uniform random
-		qr{command=2.: int 116\b},     # exponential random
-		qr{command=3.: int 1498\b},    # gaussian random
+		qr{command=1.: int 18\b},      # uniform random
+		qr{command=2.: int 100\b},     # exponential random
+		qr{command=3.: int 1526\b},    # gaussian random
 		qr{command=4.: int 4\b},
 		qr{command=5.: int 5\b},
 		qr{command=6.: int 6\b},
@@ -496,6 +496,7 @@ pgbench(
 		qr{command=109.: boolean true\b},
 		qr{command=110.: boolean true\b},
 		qr{command=111.: boolean true\b},
+		qr{command=113.: boolean true\b},
 	],
 	'pgbench expressions',
 	{
@@ -639,8 +640,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))
 -- actual values
 \set t debug(permute(:v, 1) = 0)
-\set t debug(permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1 and \
-             permute(0, 2, 5435) = 1 and permute(1, 2, 5435) = 0)
+\set t debug(permute(0, 2, 5431) = 1 and permute(1, 2, 5431) = 0 and \
+             permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1)
+-- 63 bits tests for checking permute portability across architectures
+\set size debug(:max - 10)
+\set t debug(permute(:size-1, :size, 5432) = 4352241160009873020 and \
+             permute(:size-2, :size, 5432) = 4990004119691638710 and \
+             permute(:size-3, :size, 5432) = 5868029867256127549 and \
+             permute(:size-4, :size, 5432) = 1238830324886341378 and \
+             permute(:size-5, :size, 5432) = 7415914367102906897 and \
+             permute(:size-6, :size, 5432) = 3214501037984818995 and \
+             permute(:size-7, :size, 5432) =  600660351261124695)
 }
 	});
 
diff --git a/src/common/Makefile b/src/common/Makefile
index 38a8599337..9aee26f694 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -67,6 +67,7 @@ OBJS_COMMON = \
 	md5_common.o \
 	pg_get_line.o \
 	pg_lzcompress.o \
+	pg_prng.o \
 	pgfnames.o \
 	psprintf.o \
 	relpath.o \
diff --git a/src/common/pg_prng.c b/src/common/pg_prng.c
new file mode 100644
index 0000000000..fac7a04a49
--- /dev/null
+++ b/src/common/pg_prng.c
@@ -0,0 +1,222 @@
+/*
+ * Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * Previous version was the 1988 (?) rand48 standard with 48 bits state LCG,
+ * designed for running on 32 bits or even 16 bits architectures, and to produce
+ * 32 bit ints and floats (i.e. *not* doubles).
+ *
+ * The implied 16-bits unpacking/packing may have a detrimental performance impact
+ * on modern 64 bits architectures.
+ *
+ * We (probably) want:
+ * - one reasonable default PRNG for all pg internal uses.
+ * - NOT to invent a new design!
+ * - something fast, close to rand48 (which basically does 2 arithmetic ops)
+ *   no need for something cryptographic though, which would imply slow
+ * - to produce 64 bits integers & doubles with a 52 bits mantissa
+ *   so state size > 64 bits.
+ * - a small state though, because we might generate quite a few of them
+ *   for different purposes so state size <= 256 or even <= 128 bits
+ * - the state to be aligned to whatever, so state size = 128 bits
+ * - 64 bits operations for efficiency on modern architectures,
+ *   but NOT 128 bits operations.
+ * - not to depend on special hardware for speed (eg MMX/SSE/AES).
+ * - not something with obvious known and relevant defects.
+ * - not something with "rights" attached.
+ *
+ * These constraints reduce a lot the available options from
+ * https://en.wikipedia.org/wiki/List_of_random_number_generators
+ *
+ * LCG
+ * - POSIX rand48: m = 2^48, a = 25214903917, c = 11, extract 47...16
+ * - MMIX by Donald Knuth: m = 2^64, a = 6364136223846793005, c = 1442695040888963407
+ * - very bad on low bits, which MUST NOT BE USED
+ *   a 128 bits would do, but no clear standards, and it would imply 128-bit ops,
+ *   see PCG below.
+ *
+ * PCG Family (https://www.pcg-random.org/)
+ * - this is basically an LCG with an improved outpout function
+ *   so as to avoid typical LCG issues and pass stats tests.
+ * - recommands state size to be twice the output size,
+ *   which seems a reasonable requirement.
+ * - this implies 128 bit ops for our target parameters,
+ *   otherwise it is a fairly good candidate for simplicity,
+ *   efficiency and quality.
+ *
+ * Xor-Shift-Rotate PRNGs:
+ * - a generic annoying feature of this generator class is that there is one
+ *   special all-zero state which must not be used, inducing a constraint on
+ *   initialization, and when the generator get close to it (i.e. many zeros
+ *   in the state) it takes some iterations to recover, with small lumps
+ *   of close/related/bad numbers generated in the process.
+ * Xoroshiro128+
+ * - fails statistical linearity tests in the low bits.
+ * Xoroshiro128**
+ *   https://prng.di.unimi.it/xoroshiro128starstar.c
+ * - mild and irrelevant issues: close to zero state, stats issues after some transforms
+ * Xoshiro256**
+ * - it looks good, but some issues (no big deal for pg usage IMO)
+ *   https://www.pcg-random.org/posts/a-quick-look-at-xoshiro256.html
+ *   mild problems getting out of closes-to-zero state
+ *
+ * MT19937-64
+ * + default in many places, standard
+ * - huge 2.5 KiB state (but TinyMT, 127 bits state space)
+ * - slow (but SFMT, twice as fast)
+ * - vulnerabilities on tinyMT?
+ *
+ * WELL
+ * - large state, 32-bit oriented
+ *
+ * ACORN Family (https://en.wikipedia.org/wiki/ACORN_(PRNG))
+ * - Fortran / double oriented, no C implementation found?!
+ * - medium large state "In practice we recommend using k > 10, M = 2^60 (for general application)
+ *   or M = 2^120 (for demanding applications requiring high-quality pseudo-random numbers
+ *   that will consistently pass all the tests in standard test packages such as TestU01)
+ *   and choose any odd value less than M for the seed."
+ *   k = 11 => 88 bytes/704-bits state, too large:-(
+ *
+ * Splitmix?
+ * Others?
+ *
+ * The conclusion is that we use xor-shift-rotate generator below.
+ */
+
+#include "c.h"
+#include "common/pg_prng.h"
+#include "port/pg_bitutils.h"
+#include <math.h>
+
+#define FIRST_BIT_MASK UINT64CONST(0x8000000000000000)
+#define RIGHT_HALF_MASK UINT64CONST(0x00000000FFFFFFFF)
+#define DMANTISSA_MASK UINT64CONST(0x000FFFFFFFFFFFFF)
+
+/* 64-bits rotate left */
+static inline uint64_t rotl(const uint64_t x, const int bits)
+{
+	return (x << bits) | (x >> (64 - bits));
+}
+
+/* smallest mask holding a value */
+static inline uint64_t mask_u64(uint64_t u)
+{
+	return (((uint64_t) 1) << (pg_leftmost_one_pos64(u) + 1)) - 1;
+}
+
+/* another 64-bits generator is used for state initialization or iterations */
+static uint64_t splitmix64(uint64_t * state)
+{
+	/* state update */
+	uint64_t	val = (*state += UINT64CONST(0x9E3779B97f4A7C15));
+
+	/* value extraction */
+	val = (val ^ (val >> 30)) * UINT64CONST(0xBF58476D1CE4E5B9);
+	val = (val ^ (val >> 27)) * UINT64CONST(0x94D049BB133111EB);
+
+	return val ^ (val >> 31);
+}
+
+/* seed prng state from a 64 bits integer, ensuring non zero */
+void pg_prng_seed(pg_prng_state *state, uint64_t seed)
+{
+	state->s0 = splitmix64(&seed);
+	state->s1 = splitmix64(&seed);
+}
+
+/* seed with 53 bits (mantissa & sign) from a float */
+void pg_prng_fseed(pg_prng_state *state, double fseed)
+{
+	uint64 seed = (int64) (((double) DMANTISSA_MASK) * fseed);
+	pg_prng_seed(state, seed);
+}
+
+/* strong random seeding */
+void pg_prng_strong_seed(pg_prng_state *state)
+{
+	pg_strong_random((void *) state, sizeof(pg_prng_state));
+
+	/* avoid zero with Donald Knuth's LCG parameters */
+	if (unlikely(state->s0 == 0 && state->s1 == 0))
+	{
+		/* should it warn that something is amiss if we get there? */
+		pg_prng_state def = PG_PRNG_DEFAULT_STATE;
+		*state = def;
+	}
+}
+
+/* generator & state update */
+static uint64_t xoroshiro128ss(pg_prng_state *state)
+{
+	const uint64_t	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;
+}
+
+/* u64 generator */
+uint64_t pg_prng_u64(pg_prng_state *state)
+{
+	return xoroshiro128ss(state);
+}
+
+/* select in a range with bitmask rejection */
+uint64_t pg_prng_u64_range(pg_prng_state *state, uint64_t range)
+{
+	/* we always iterate over the state once, for determinism */
+	uint64_t next = xoroshiro128ss(state);
+	uint64_t val = next;
+
+	if (range >= 2)
+	{
+		uint64_t mask = mask_u64(range-1);
+
+		/* iterate over splitmix till val in [0, range) */
+		while ((val = val & mask) >= range)
+			val = splitmix64(&next);
+	}
+	else
+		val = 0;
+
+	return val;
+}
+
+/* i64 generator */
+int64_t pg_prng_i64(pg_prng_state *state)
+{
+	return (int64_t) xoroshiro128ss(state);
+}
+
+/* u32 generator */
+uint32_t pg_prng_u32(pg_prng_state *state)
+{
+	const uint64_t v = xoroshiro128ss(state);
+	return (uint32_t) (((v >> 32) ^ v) & RIGHT_HALF_MASK);
+}
+
+/* i32 generator */
+int32_t pg_prng_i32(pg_prng_state *state)
+{
+	const uint64_t v = xoroshiro128ss(state);
+	return (int32_t) (((v >> 32) ^ v) & RIGHT_HALF_MASK);
+}
+
+/* double generator */
+double pg_prng_f64(pg_prng_state *state)
+{
+	uint64_t v = xoroshiro128ss(state);
+	return ldexp((double) (v & DMANTISSA_MASK), -52);
+}
+
+/* bool generator */
+bool pg_prng_bool(pg_prng_state *state)
+{
+	uint64_t v = xoroshiro128ss(state);
+	return (v & FIRST_BIT_MASK) == FIRST_BIT_MASK;
+}
diff --git a/src/include/common/pg_prng.h b/src/include/common/pg_prng.h
new file mode 100644
index 0000000000..6d582cc3dc
--- /dev/null
+++ b/src/include/common/pg_prng.h
@@ -0,0 +1,34 @@
+/*-------------------------------------------------------------------------
+ *
+ * PRNG: internal Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * src/include/common/pg_prng.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_PRNG_H
+#define PG_PRNG_H
+
+/* 128 bits state */
+typedef struct pg_prng_state {
+	uint64_t	s0, s1;
+} pg_prng_state;
+
+/* use Donald Knuth's LCG constants for default state */
+#define PG_PRNG_DEFAULT_STATE \
+	{ UINT64CONST(0x5851F42D4C957F2D), UINT64CONST(0x14057B7EF767814F) }
+
+extern void pg_prng_seed(pg_prng_state *state, uint64_t seed);
+extern void pg_prng_fseed(pg_prng_state *state, double fseed);
+extern void pg_prng_strong_seed(pg_prng_state *state);
+extern uint64_t pg_prng_u64(pg_prng_state *state);
+extern uint64_t pg_prng_u64_range(pg_prng_state *state, uint64_t range);
+extern int64_t pg_prng_i64(pg_prng_state *state);
+extern uint32_t pg_prng_u32(pg_prng_state *state);
+extern int32_t pg_prng_i32(pg_prng_state *state);
+extern double pg_prng_f64(pg_prng_state *state);
+extern bool pg_prng_bool(pg_prng_state *state);
+
+#endif							/* PG_PRNG_H */
diff --git a/src/include/optimizer/geqo.h b/src/include/optimizer/geqo.h
index 24dcdfb6cc..b28e4f084a 100644
--- a/src/include/optimizer/geqo.h
+++ b/src/include/optimizer/geqo.h
@@ -22,6 +22,7 @@
 #ifndef GEQO_H
 #define GEQO_H
 
+#include "common/pg_prng.h"
 #include "nodes/pathnodes.h"
 #include "optimizer/geqo_gene.h"
 
@@ -72,8 +73,8 @@ extern double Geqo_seed;		/* 0 .. 1 */
  */
 typedef struct
 {
-	List	   *initial_rels;	/* the base relations we are joining */
-	unsigned short random_state[3]; /* state for pg_erand48() */
+	List		   *initial_rels;	/* the base relations we are joining */
+	pg_prng_state	random_state;   /* PRNG state */
 } GeqoPrivateData;
 
 
diff --git a/src/include/port.h b/src/include/port.h
index 82f63de325..eb18665196 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -356,11 +356,6 @@ extern int	gettimeofday(struct timeval *tp, struct timezone *tzp);
 #define pgoff_t off_t
 #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
 extern int	fls(int mask);
 #endif
diff --git a/src/include/utils/sampling.h b/src/include/utils/sampling.h
index a58d14281b..c0b1c6a645 100644
--- a/src/include/utils/sampling.h
+++ b/src/include/utils/sampling.h
@@ -13,15 +13,14 @@
 #ifndef SAMPLING_H
 #define SAMPLING_H
 
+#include "common/pg_prng.h"
 #include "storage/block.h"		/* for typedef BlockNumber */
 
 
 /* Random generator for sampling code */
-typedef unsigned short SamplerRandomState[3];
-
 extern void sampler_random_init_state(long seed,
-									  SamplerRandomState randstate);
-extern double sampler_random_fract(SamplerRandomState randstate);
+									  pg_prng_state *randstate);
+extern double sampler_random_fract(pg_prng_state *randstate);
 
 /* Block sampling methods */
 
@@ -32,7 +31,7 @@ typedef struct
 	int			n;				/* desired sample size */
 	BlockNumber t;				/* current block number */
 	int			m;				/* blocks selected so far */
-	SamplerRandomState randstate;	/* random generator state */
+	pg_prng_state randstate;	/* random generator state */
 } BlockSamplerData;
 
 typedef BlockSamplerData *BlockSampler;
@@ -46,8 +45,9 @@ extern BlockNumber BlockSampler_Next(BlockSampler bs);
 
 typedef struct
 {
-	double		W;
-	SamplerRandomState randstate;	/* random generator state */
+	double			W;
+	pg_prng_state	randstate;	/* random generator state */
+	bool			randstate_initialized;
 } ReservoirStateData;
 
 typedef ReservoirStateData *ReservoirState;
diff --git a/src/port/Makefile b/src/port/Makefile
index 52dbf5783f..b3754d8940 100644
--- a/src/port/Makefile
+++ b/src/port/Makefile
@@ -42,7 +42,6 @@ OBJS = \
 	$(PG_CRC32C_OBJS) \
 	bsearch_arg.o \
 	chklocale.o \
-	erand48.o \
 	inet_net_ntop.o \
 	noblock.o \
 	path.o \
diff --git a/src/port/erand48.c b/src/port/erand48.c
deleted file mode 100644
index a82ea94220..0000000000
--- a/src/port/erand48.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/port/random.c b/src/port/random.c
deleted file mode 100644
index 2dd59a0829..0000000000
--- a/src/port/random.c
+++ /dev/null
@@ -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();
-}
diff --git a/src/port/srandom.c b/src/port/srandom.c
deleted file mode 100644
index cf1007b2ef..0000000000
--- a/src/port/srandom.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/test/modules/test_integerset/test_integerset.c b/src/test/modules/test_integerset/test_integerset.c
index 21c6f49b37..8935a81284 100644
--- a/src/test/modules/test_integerset/test_integerset.c
+++ b/src/test/modules/test_integerset/test_integerset.c
@@ -12,6 +12,7 @@
  */
 #include "postgres.h"
 
+#include "common/pg_prng.h"
 #include "fmgr.h"
 #include "lib/integerset.h"
 #include "miscadmin.h"
@@ -99,12 +100,16 @@ static void check_with_filler(IntegerSet *intset, uint64 x, uint64 value, uint64
 static void test_single_value_and_filler(uint64 value, uint64 filler_min, uint64 filler_max);
 static void test_huge_distances(void);
 
+static pg_prng_state	pr_state;
+
 /*
  * SQL-callable entry point to perform all tests.
  */
 Datum
 test_integerset(PG_FUNCTION_ARGS)
 {
+	pg_prng_strong_seed(&pr_state);
+
 	/* Tests for various corner cases */
 	test_empty();
 	test_huge_distances();
@@ -248,7 +253,7 @@ test_pattern(const test_spec *spec)
 		 * only a small part of the integer space is used.  We would very
 		 * rarely hit values that are actually in the set.
 		 */
-		x = (pg_lrand48() << 31) | pg_lrand48();
+		x = pg_prng_u64(&pr_state);
 		x = x % (last_int + 1000);
 
 		/* Do we expect this value to be present in the set? */
@@ -571,7 +576,7 @@ test_huge_distances(void)
 	 */
 	while (num_values < 1000)
 	{
-		val += pg_lrand48();
+		val += pg_prng_i32(&pr_state);
 		values[num_values++] = val;
 	}
 
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 233ddbf4c2..29d0f467cb 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -103,9 +103,9 @@ sub mkvcbuild
 	$solution = CreateSolution($vsVersion, $config);
 
 	our @pgportfiles = qw(
-	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c random.c
-	  srandom.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
+	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c
+	  getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
+	  snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
 	  dirent.c dlopen.c getopt.c getopt_long.c link.c
 	  pread.c preadv.c pwrite.c pwritev.c pg_bitutils.c
 	  pg_strong_random.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c
@@ -131,9 +131,9 @@ sub mkvcbuild
 	  config_info.c controldata_utils.c d2s.c encnames.c exec.c
 	  f2s.c file_perm.c file_utils.c hashfn.c hex.c ip.c jsonapi.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
-	  saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c username.c
-	  wait_error.c wchar.c);
+	  pg_get_line.c pg_lzcompress.c pg_prng.c pgfnames.c psprintf.c relpath.c
+	  rmtree.c saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c
+	  username.c wait_error.c wchar.c);
 
 	if ($solution->{options}->{openssl})
 	{
#21Yura Sokolov
y.sokolov@postgrespro.ru
In reply to: Fabien COELHO (#20)
Re: rand48 replacement

Fabien COELHO wrote 2021-07-03 11:45:

And a v5 where an unused test file does also compile if we insist.

About patch:
1. PostgreSQL source uses `uint64` and `uint32`, but not
`uint64_t`/`uint32_t`
2. I don't see why pg_prng_state could not be `typedef uint64
pg_prng_state[2];`
3. Then SamplerRandomState and pgbench RandomState could stay.
Patch will be a lot shorter.
I don't like mix of semantic refactoring and syntactic refactoring in
the
same patch.
While I could agree with replacing `SamplerRandomState =>
pg_prng_state`, I'd
rather see it in separate commit.
And that separate commit could contain transition:
`typedef uint64 pg_prng_state[2];` => `typedef struct { uint64 s0, s1
} pg_prng_state;`
4. There is no need in ReservoirStateData->randstate_initialized. There
could
be macros/function:
`bool pg_prng_initiated(state) { return (state[0]|state[1]) != 0; }`
5. Is there need for 128bit prng at all? At least 2*64bit.
There are 2*32bit xoroshiro64
https://prng.di.unimi.it/xoroshiro64starstar.c
And there is 4*32bit xoshiro128:
https://prng.di.unimi.it/xoshiro128plusplus.c
32bit operations are faster on 32bit platforms.
But 32bit platforms are quite rare in production this days.
Therefore I don't have strong opinion on this.

regards,
Sokolov Yura.

#22Tom Lane
tgl@sss.pgh.pa.us
In reply to: Yura Sokolov (#21)
Re: rand48 replacement

Yura Sokolov <y.sokolov@postgrespro.ru> writes:

2. I don't see why pg_prng_state could not be `typedef uint64
pg_prng_state[2];`

Please no. That sort of typedef behaves so weirdly that it's
a foot-gun.

regards, tom lane

#23Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Yura Sokolov (#21)
Re: rand48 replacement

Hello Yura,

1. PostgreSQL source uses `uint64` and `uint32`, but not
`uint64_t`/`uint32_t`
2. I don't see why pg_prng_state could not be `typedef uint64
pg_prng_state[2];`

It could, but I do not see that as desirable. From an API design point of
view we want something clean and abstract, and for me a struct looks
better for that. It would be a struct with an array insided, I think that
the code is more readable by avoiding constant index accesses (s[0] vs
s0), we do not need actual indexes.

3. Then SamplerRandomState and pgbench RandomState could stay.
Patch will be a lot shorter.

You mean "typedef pg_prng_state SamplerRandomState"? One point of the
patch is to have "one" standard PRNG commonly used where one is needed, so
I'd say we want the name to be used, hence the substitutions.

Also, I have a thing against objects being named "Random" which are not
random, which is highly misleading. A PRNG is purely deterministic.
Removing misleading names is also a benefit.

So If people want to keep the old name it can be done. But I see these
name changes as desirable.

I don't like mix of semantic refactoring and syntactic refactoring in
the same patch. While I could agree with replacing `SamplerRandomState
=> pg_prng_state`, I'd rather see it in separate commit. And that
separate commit could contain transition: `typedef uint64
pg_prng_state[2];` => `typedef struct { uint64 s0, s1 } pg_prng_state;`

I would tend to agree on principle, but separating in two phases here
looks pointless: why implementing a cleaner rand48 interface, which would
then NOT be the rand48 standard, just to upgrade it to something else in
the next commit? And the other path is as painfull and pointless.

So I think that the new feature better comes with its associated
refactoring which is an integral part of it.

4. There is no need in ReservoirStateData->randstate_initialized. There could
be macros/function:
`bool pg_prng_initiated(state) { return (state[0]|state[1]) != 0; }`

It would work for this peculiar implementation but not necessary for
others that we may want to substitute later, as it would mean either
breaking the interface or adding a boolean in the structure if there is no
special unintialized state that can be detected, which would impact memory
usage and alignment.

So I think it is better to keep it that way, usually the user knows
whether its structure has been initialized, and the special case for
reservoir where the user does not seem to know about that can handle its
own boolean without impacting the common API or the data structure.

5. Is there need for 128 bit prng at all?

This is a 64 bits PRNG with a 128 bit state. We are generating 64 bits
values, so we want a 64 bit PRNG. A prng state must be larger than its
generated value, so we need sizeof(state) > 64 bits, hence at least 128
bits if we add 128 bits memory alignement.

And there is 4*32bit xoshiro128:
https://prng.di.unimi.it/xoshiro128plusplus.c
32bit operations are faster on 32bit platforms.
But 32bit platforms are quite rare in production this days.
Therefore I don't have strong opinion on this.

I think that 99.9% hardware running postgres is 64 bits, so 64 bits is the
right choice.

--
Fabien.

#24Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Yura Sokolov (#21)
1 attachment(s)
Re: rand48 replacement

1. PostgreSQL source uses `uint64` and `uint32`, but not
`uint64_t`/`uint32_t`

Indeed you are right. Attached v6 does that as well.

--
Fabien.

Attachments:

prng-6.patchtext/x-diff; name=prng-6.patchDownload
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 2c2f149fb0..146b524076 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -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]);
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index fafbab6b02..3009861e45 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -5152,7 +5152,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]);
 		}
diff --git a/contrib/tsm_system_rows/tsm_system_rows.c b/contrib/tsm_system_rows/tsm_system_rows.c
index 4996612902..1a46d4b143 100644
--- a/contrib/tsm_system_rows/tsm_system_rows.c
+++ b/contrib/tsm_system_rows/tsm_system_rows.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/contrib/tsm_system_time/tsm_system_time.c b/contrib/tsm_system_time/tsm_system_time.c
index 788d8f9a68..36acc6c106 100644
--- a/contrib/tsm_system_time/tsm_system_time.c
+++ b/contrib/tsm_system_time/tsm_system_time.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 0c9591415e..1ddbeed2dc 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1308,7 +1308,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 					 * Found a suitable tuple, so save it, replacing one old
 					 * tuple at random
 					 */
-					int			k = (int) (targrows * sampler_random_fract(rstate.randstate));
+					int			k = (int) (targrows * sampler_random_fract(&rstate.randstate));
 
 					Assert(k >= 0 && k < targrows);
 					heap_freetuple(rows[k]);
diff --git a/src/backend/optimizer/geqo/geqo_random.c b/src/backend/optimizer/geqo/geqo_random.c
index f21bc047e6..8b42a9ffba 100644
--- a/src/backend/optimizer/geqo/geqo_random.c
+++ b/src/backend/optimizer/geqo/geqo_random.c
@@ -21,14 +21,7 @@ geqo_set_seed(PlannerInfo *root, double seed)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	/*
-	 * XXX. This seeding algorithm could certainly be improved - but it is not
-	 * critical to do so.
-	 */
-	memset(private->random_state, 0, sizeof(private->random_state));
-	memcpy(private->random_state,
-		   &seed,
-		   Min(sizeof(private->random_state), sizeof(seed)));
+	pg_prng_fseed(&private->random_state, seed);
 }
 
 double
@@ -36,5 +29,5 @@ geqo_rand(PlannerInfo *root)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	return pg_erand48(private->random_state);
+	return pg_prng_f64(&private->random_state);
 }
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 098bbb372b..9fc0b108dd 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -21,6 +21,7 @@
 
 #include "catalog/pg_type.h"
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/shortest_dec.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
@@ -65,7 +66,7 @@ float8		degree_c_one = 1.0;
 
 /* State for drandom() and setseed() */
 static bool drandom_seed_set = false;
-static unsigned short drandom_seed[3] = {0, 0, 0};
+static pg_prng_state drandom_seed = PG_PRNG_DEFAULT_STATE;
 
 /* Local function prototypes */
 static double sind_q1(double x);
@@ -2762,22 +2763,20 @@ drandom(PG_FUNCTION_ARGS)
 		 * Should that fail for some reason, we fall back on a lower-quality
 		 * seed based on current time and PID.
 		 */
-		if (!pg_strong_random(drandom_seed, sizeof(drandom_seed)))
+		if (!pg_strong_random(&drandom_seed, sizeof(drandom_seed)))
 		{
 			TimestampTz now = GetCurrentTimestamp();
 			uint64		iseed;
 
 			/* Mix the PID with the most predictable bits of the timestamp */
 			iseed = (uint64) now ^ ((uint64) MyProcPid << 32);
-			drandom_seed[0] = (unsigned short) iseed;
-			drandom_seed[1] = (unsigned short) (iseed >> 16);
-			drandom_seed[2] = (unsigned short) (iseed >> 32);
+			pg_prng_seed(&drandom_seed, iseed);
 		}
 		drandom_seed_set = true;
 	}
 
-	/* pg_erand48 produces desired result range [0.0 - 1.0) */
-	result = pg_erand48(drandom_seed);
+	/* produces desired result range [0.0 - 1.0) */
+	result = pg_prng_f64(&drandom_seed);
 
 	PG_RETURN_FLOAT8(result);
 }
@@ -2790,7 +2789,6 @@ Datum
 setseed(PG_FUNCTION_ARGS)
 {
 	float8		seed = PG_GETARG_FLOAT8(0);
-	uint64		iseed;
 
 	if (seed < -1 || seed > 1 || isnan(seed))
 		ereport(ERROR,
@@ -2798,11 +2796,7 @@ setseed(PG_FUNCTION_ARGS)
 				 errmsg("setseed parameter %g is out of allowed range [-1,1]",
 						seed)));
 
-	/* Use sign bit + 47 fractional bits to fill drandom_seed[] */
-	iseed = (int64) (seed * (float8) UINT64CONST(0x7FFFFFFFFFFF));
-	drandom_seed[0] = (unsigned short) iseed;
-	drandom_seed[1] = (unsigned short) (iseed >> 16);
-	drandom_seed[2] = (unsigned short) (iseed >> 32);
+	pg_prng_fseed(&drandom_seed, seed);
 	drandom_seed_set = true;
 
 	PG_RETURN_VOID();
diff --git a/src/backend/utils/misc/sampling.c b/src/backend/utils/misc/sampling.c
index 0c327e823f..7b59128878 100644
--- a/src/backend/utils/misc/sampling.c
+++ b/src/backend/utils/misc/sampling.c
@@ -49,7 +49,7 @@ BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize,
 	bs->t = 0;					/* blocks scanned so far */
 	bs->m = 0;					/* blocks selected so far */
 
-	sampler_random_init_state(randseed, bs->randstate);
+	sampler_random_init_state(randseed, &bs->randstate);
 
 	return Min(bs->n, bs->N);
 }
@@ -98,7 +98,7 @@ BlockSampler_Next(BlockSampler bs)
 	 * less than k, which means that we cannot fail to select enough blocks.
 	 *----------
 	 */
-	V = sampler_random_fract(bs->randstate);
+	V = sampler_random_fract(&bs->randstate);
 	p = 1.0 - (double) k / (double) K;
 	while (V < p)
 	{
@@ -136,10 +136,10 @@ reservoir_init_selection_state(ReservoirState rs, int n)
 	 * Reservoir sampling is not used anywhere where it would need to return
 	 * repeatable results so we can initialize it randomly.
 	 */
-	sampler_random_init_state(random(), rs->randstate);
+	sampler_random_init_state(random(), &rs->randstate);
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	rs->W = exp(-log(sampler_random_fract(rs->randstate)) / n);
+	rs->W = exp(-log(sampler_random_fract(&rs->randstate)) / n);
 }
 
 double
@@ -154,7 +154,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 		double		V,
 					quot;
 
-		V = sampler_random_fract(rs->randstate);	/* Generate V */
+		V = sampler_random_fract(&rs->randstate);	/* Generate V */
 		S = 0;
 		t += 1;
 		/* Note: "num" in Vitter's code is always equal to t - n */
@@ -186,7 +186,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 						tmp;
 
 			/* Generate U and X */
-			U = sampler_random_fract(rs->randstate);
+			U = sampler_random_fract(&rs->randstate);
 			X = t * (W - 1.0);
 			S = floor(X);		/* S is tentatively set to floor(X) */
 			/* Test if U <= h(S)/cg(X) in the manner of (6.3) */
@@ -215,7 +215,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 				y *= numer / denom;
 				denom -= 1;
 			}
-			W = exp(-log(sampler_random_fract(rs->randstate)) / n); /* Generate W in advance */
+			W = exp(-log(sampler_random_fract(&rs->randstate)) / n); /* Generate W in advance */
 			if (exp(log(y) / n) <= (t + X) / t)
 				break;
 		}
@@ -230,24 +230,22 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
  *----------
  */
 void
-sampler_random_init_state(long seed, SamplerRandomState randstate)
+sampler_random_init_state(long seed, pg_prng_state *randstate)
 {
-	randstate[0] = 0x330e;		/* same as pg_erand48, but could be anything */
-	randstate[1] = (unsigned short) seed;
-	randstate[2] = (unsigned short) (seed >> 16);
+	pg_prng_seed(randstate, (uint64_t) seed);
 }
 
 /* Select a random value R uniformly distributed in (0 - 1) */
 double
-sampler_random_fract(SamplerRandomState randstate)
+sampler_random_fract(pg_prng_state *randstate)
 {
 	double		res;
 
-	/* pg_erand48 returns a value in [0.0 - 1.0), so we must reject 0 */
+	/* pg_prng_f64 returned value in [0.0 - 1.0), so we must reject 0.0 */
 	do
 	{
-		res = pg_erand48(randstate);
-	} while (res == 0.0);
+		res = pg_prng_f64(randstate);
+	} while (unlikely(res == 0.0));
 	return res;
 }
 
@@ -266,22 +264,28 @@ double
 anl_random_fract(void)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(random(), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* and compute a random fraction */
-	return sampler_random_fract(oldrs.randstate);
+	return sampler_random_fract(&oldrs.randstate);
 }
 
 double
 anl_init_selection_state(int n)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(random(), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	return exp(-log(sampler_random_fract(oldrs.randstate)) / n);
+	return exp(-log(sampler_random_fract(&oldrs.randstate)) / n);
 }
 
 double
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 4aeccd93af..6d749bbbb6 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -58,6 +58,7 @@
 #endif
 
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/logging.h"
 #include "common/string.h"
 #include "common/username.h"
@@ -343,16 +344,8 @@ typedef struct StatsData
 	SimpleStats lag;
 } StatsData;
 
-/*
- * Struct to keep random state.
- */
-typedef struct RandomState
-{
-	unsigned short xseed[3];
-} RandomState;
-
 /* 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 */
 static THREAD_BARRIER_T barrier;
@@ -454,7 +447,7 @@ typedef struct
 	 * Separate randomness for each client. This is used for random functions
 	 * 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			command;		/* command number in script */
@@ -491,9 +484,9 @@ typedef struct
 	 * random state to make all of them independent of each other and
 	 * therefore deterministic at the thread level.
 	 */
-	RandomState ts_choose_rs;	/* random state for selecting a script */
-	RandomState ts_throttle_rs; /* random state for transaction throttling */
-	RandomState ts_sample_rs;	/* random state for log sampling */
+	pg_prng_state	ts_choose_rs;	/* random state for selecting a script */
+	pg_prng_state	ts_throttle_rs; /* random state for transaction throttling */
+	pg_prng_state	ts_sample_rs;	/* random state for log sampling */
 
 	int64		throttle_trigger;	/* previous/next throttling (us) */
 	FILE	   *logfile;		/* where to log, or NULL */
@@ -891,42 +884,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.
  */
 static void
-initRandomState(RandomState *random_state)
+initRandomState(pg_prng_state *state)
 {
-	random_state->xseed[0] = (unsigned short)
-		(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);
+	pg_prng_seed(state, pg_prng_u64(&base_random_sequence));
 }
 
+
 /*
- * Random number generator: uniform distribution from min to max inclusive.
+ * PRNG: uniform distribution from min to max inclusive.
  *
  * Although the limits are expressed as int64, you can't generate the full
  * int64 range in one call, because the difference of the limits mustn't
- * overflow int64.  In practice it's unwise to ask for more than an int32
- * range, because of the limited precision of pg_erand48().
+ * overflow int64.
  */
 static int64
-getrand(RandomState *random_state, int64 min, int64 max)
+getrand(pg_prng_state *state, int64 min, int64 max)
 {
-	/*
-	 * 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));
+	return min + (int64) pg_prng_u64_range(state, max - min + 1);
 }
 
 /*
@@ -935,8 +914,7 @@ getrand(RandomState *random_state, int64 min, int64 max)
  * value is exp(-parameter).
  */
 static int64
-getExponentialRand(RandomState *random_state, int64 min, int64 max,
-				   double parameter)
+getExponentialRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		cut,
 				uniform,
@@ -946,7 +924,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 	Assert(parameter > 0.0);
 	cut = exp(-parameter);
 	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	uniform = 1.0 - pg_prng_f64(state);
 
 	/*
 	 * inner expression in (cut, 1] (if parameter > 0), rand in [0, 1)
@@ -959,8 +937,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 
 /* random number generator: gaussian distribution from min to max inclusive */
 static int64
-getGaussianRand(RandomState *random_state, int64 min, int64 max,
-				double parameter)
+getGaussianRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		stdev;
 	double		rand;
@@ -983,13 +960,13 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
 	do
 	{
 		/*
-		 * pg_erand48 generates [0,1), but for the basic version of the
+		 * pg_prng_f64 generates [0,1), but for the basic version of the
 		 * Box-Muller transform the two uniformly distributed random numbers
 		 * are expected in (0, 1] (see
 		 * https://en.wikipedia.org/wiki/Box-Muller_transform)
 		 */
-		double		rand1 = 1.0 - pg_erand48(random_state->xseed);
-		double		rand2 = 1.0 - pg_erand48(random_state->xseed);
+		double		rand1 = 1.0 - pg_prng_f64(state);
+		double		rand2 = 1.0 - pg_prng_f64(state);
 
 		/* Box-Muller basic form transform */
 		double		var_sqrt = sqrt(-2.0 * log(rand1));
@@ -1019,7 +996,7 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
  * not be one.
  */
 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
@@ -1027,8 +1004,8 @@ getPoissonRand(RandomState *random_state, double center)
 	 */
 	double		uniform;
 
-	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	/* pseudo-random value in [0, 1), uniform in (0, 1] */
+	uniform = 1.0 - pg_prng_f64(state);
 
 	return (int64) (-log(uniform) * center + 0.5);
 }
@@ -1041,7 +1018,7 @@ getPoissonRand(RandomState *random_state, double center)
  * This works for s > 1.0, but may perform badly for s very close to 1.0.
  */
 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		x,
@@ -1056,8 +1033,8 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 	while (true)
 	{
 		/* random variates */
-		u = pg_erand48(random_state->xseed);
-		v = pg_erand48(random_state->xseed);
+		u = pg_prng_f64(state);
+		v = pg_prng_f64(state);
 
 		x = floor(pow(u, -1.0 / (s - 1.0)));
 
@@ -1071,14 +1048,14 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 
 /* random number generator: zipfian distribution from min to max inclusive */
 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;
 
 	/* abort if parameter is invalid */
 	Assert(MIN_ZIPFIAN_PARAM <= s && s <= MAX_ZIPFIAN_PARAM);
 
-	return min - 1 + computeIterativeZipfian(random_state, n, s);
+	return min - 1 + computeIterativeZipfian(state, n, s);
 }
 
 /*
@@ -1135,7 +1112,7 @@ getHashMurmur2(int64 val, uint64 seed)
  * For small sizes, this generates each of the (size!) possible permutations
  * of integers in the range [0, size) with roughly equal probability.  Once
  * the size is larger than 20, the number of possible permutations exceeds the
- * number of distinct states of the internal pseudorandom number generators,
+ * number of distinct states of the internal pseudorandom number generator,
  * and so not all possible permutations can be generated, but the permutations
  * chosen should continue to give the appearance of being random.
  *
@@ -1145,25 +1122,19 @@ getHashMurmur2(int64 val, uint64 seed)
 static int64
 permute(const int64 val, const int64 isize, const int64 seed)
 {
-	RandomState random_state1;
-	RandomState random_state2;
-	uint64		size;
-	uint64		v;
-	int			masklen;
-	uint64		mask;
-	int			i;
+	/* using a high-end PRNG is probably overkill */
+	pg_prng_state	state;
+	uint64			size;
+	uint64			v;
+	int				masklen;
+	uint64			mask;
+	int				i;
 
 	if (isize < 2)
 		return 0;				/* nothing to permute */
 
-	/* Initialize a pair of random states using the seed */
-	random_state1.xseed[0] = seed & 0xFFFF;
-	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;
+	/* Initialize prng state using the seed */
+	pg_prng_seed(&state, (uint64) seed);
 
 	/* Computations are performed on unsigned values */
 	size = (uint64) isize;
@@ -1209,8 +1180,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 					t;
 
 		/* Random multiply (by an odd number), XOR and rotate of lower half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		if (v <= mask)
 		{
 			v = ((v * m) ^ r) & mask;
@@ -1218,8 +1189,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random multiply (by an odd number), XOR and rotate of upper half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		t = size - 1 - v;
 		if (t <= mask)
 		{
@@ -1229,7 +1200,7 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random offset */
-		r = (uint64) getrand(&random_state2, 0, size - 1);
+		r = pg_prng_u64(&state) % (size - 1);
 		v = (v + r) % size;
 	}
 
@@ -3790,7 +3761,7 @@ doLog(TState *thread, CState *st,
 	 * to the random sample.
 	 */
 	if (sample_rate != 0.0 &&
-		pg_erand48(thread->ts_sample_rs.xseed) > sample_rate)
+		pg_prng_f64(&thread->ts_sample_rs) > sample_rate)
 		return;
 
 	/* should we aggregate the results or not? */
@@ -5714,12 +5685,11 @@ set_random_seed(const char *seed)
 
 	if (seed != NULL)
 		pg_log_info("setting random seed to %llu", (unsigned long long) iseed);
+
 	random_seed = iseed;
 
 	/* Fill base_random_sequence with low-order bits of seed */
-	base_random_sequence.xseed[0] = iseed & 0xFFFF;
-	base_random_sequence.xseed[1] = (iseed >> 16) & 0xFFFF;
-	base_random_sequence.xseed[2] = (iseed >> 32) & 0xFFFF;
+	pg_prng_seed(&base_random_sequence, (uint64_t) iseed);
 
 	return true;
 }
@@ -6405,9 +6375,7 @@ main(int argc, char **argv)
 	/* set default seed for hash functions */
 	if (lookupVariable(&state[0], "default_seed") == NULL)
 	{
-		uint64		seed =
-		((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) |
-		(((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) << 32);
+		uint64		seed = pg_prng_u64(&base_random_sequence);
 
 		for (i = 0; i < nclients; i++)
 			if (!putVariableInt(&state[i], "startup", "default_seed", (int64) seed))
diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl
index 3aa9d5d753..a5a9bcbd56 100644
--- a/src/bin/pgbench/t/001_pgbench_with_server.pl
+++ b/src/bin/pgbench/t/001_pgbench_with_server.pl
@@ -427,9 +427,9 @@ pgbench(
 
 		# After explicit seeding, the four random checks (1-3,20) are
 		# deterministic
-		qr{command=1.: int 13\b},      # uniform random
-		qr{command=2.: int 116\b},     # exponential random
-		qr{command=3.: int 1498\b},    # gaussian random
+		qr{command=1.: int 18\b},      # uniform random
+		qr{command=2.: int 100\b},     # exponential random
+		qr{command=3.: int 1526\b},    # gaussian random
 		qr{command=4.: int 4\b},
 		qr{command=5.: int 5\b},
 		qr{command=6.: int 6\b},
@@ -496,6 +496,7 @@ pgbench(
 		qr{command=109.: boolean true\b},
 		qr{command=110.: boolean true\b},
 		qr{command=111.: boolean true\b},
+		qr{command=113.: boolean true\b},
 	],
 	'pgbench expressions',
 	{
@@ -639,8 +640,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))
 -- actual values
 \set t debug(permute(:v, 1) = 0)
-\set t debug(permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1 and \
-             permute(0, 2, 5435) = 1 and permute(1, 2, 5435) = 0)
+\set t debug(permute(0, 2, 5431) = 1 and permute(1, 2, 5431) = 0 and \
+             permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1)
+-- 63 bits tests for checking permute portability across architectures
+\set size debug(:max - 10)
+\set t debug(permute(:size-1, :size, 5432) = 4352241160009873020 and \
+             permute(:size-2, :size, 5432) = 4990004119691638710 and \
+             permute(:size-3, :size, 5432) = 5868029867256127549 and \
+             permute(:size-4, :size, 5432) = 1238830324886341378 and \
+             permute(:size-5, :size, 5432) = 7415914367102906897 and \
+             permute(:size-6, :size, 5432) = 3214501037984818995 and \
+             permute(:size-7, :size, 5432) =  600660351261124695)
 }
 	});
 
diff --git a/src/common/Makefile b/src/common/Makefile
index 38a8599337..9aee26f694 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -67,6 +67,7 @@ OBJS_COMMON = \
 	md5_common.o \
 	pg_get_line.o \
 	pg_lzcompress.o \
+	pg_prng.o \
 	pgfnames.o \
 	psprintf.o \
 	relpath.o \
diff --git a/src/common/pg_prng.c b/src/common/pg_prng.c
new file mode 100644
index 0000000000..38fd785ac0
--- /dev/null
+++ b/src/common/pg_prng.c
@@ -0,0 +1,222 @@
+/*
+ * Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * Previous version was the 1988 (?) rand48 standard with 48 bits state LCG,
+ * designed for running on 32 bits or even 16 bits architectures, and to produce
+ * 32 bit ints and floats (i.e. *not* doubles).
+ *
+ * The implied 16-bits unpacking/packing may have a detrimental performance impact
+ * on modern 64 bits architectures.
+ *
+ * We (probably) want:
+ * - one reasonable default PRNG for all pg internal uses.
+ * - NOT to invent a new design!
+ * - something fast, close to rand48 (which basically does 2 arithmetic ops)
+ *   no need for something cryptographic though, which would imply slow
+ * - to produce 64 bits integers & doubles with a 52 bits mantissa
+ *   so state size > 64 bits.
+ * - a small state though, because we might generate quite a few of them
+ *   for different purposes so state size <= 256 or even <= 128 bits
+ * - the state to be aligned to whatever, so state size = 128 bits
+ * - 64 bits operations for efficiency on modern architectures,
+ *   but NOT 128 bits operations.
+ * - not to depend on special hardware for speed (eg MMX/SSE/AES).
+ * - not something with obvious known and relevant defects.
+ * - not something with "rights" attached.
+ *
+ * These constraints reduce a lot the available options from
+ * https://en.wikipedia.org/wiki/List_of_random_number_generators
+ *
+ * LCG
+ * - POSIX rand48: m = 2^48, a = 25214903917, c = 11, extract 47...16
+ * - MMIX by Donald Knuth: m = 2^64, a = 6364136223846793005, c = 1442695040888963407
+ * - very bad on low bits, which MUST NOT BE USED
+ *   a 128 bits would do, but no clear standards, and it would imply 128-bit ops,
+ *   see PCG below.
+ *
+ * PCG Family (https://www.pcg-random.org/)
+ * - this is basically an LCG with an improved outpout function
+ *   so as to avoid typical LCG issues and pass stats tests.
+ * - recommands state size to be twice the output size,
+ *   which seems a reasonable requirement.
+ * - this implies 128 bit ops for our target parameters,
+ *   otherwise it is a fairly good candidate for simplicity,
+ *   efficiency and quality.
+ *
+ * Xor-Shift-Rotate PRNGs:
+ * - a generic annoying feature of this generator class is that there is one
+ *   special all-zero state which must not be used, inducing a constraint on
+ *   initialization, and when the generator get close to it (i.e. many zeros
+ *   in the state) it takes some iterations to recover, with small lumps
+ *   of close/related/bad numbers generated in the process.
+ * Xoroshiro128+
+ * - fails statistical linearity tests in the low bits.
+ * Xoroshiro128**
+ *   https://prng.di.unimi.it/xoroshiro128starstar.c
+ * - mild and irrelevant issues: close to zero state, stats issues after some transforms
+ * Xoshiro256**
+ * - it looks good, but some issues (no big deal for pg usage IMO)
+ *   https://www.pcg-random.org/posts/a-quick-look-at-xoshiro256.html
+ *   mild problems getting out of closes-to-zero state
+ *
+ * MT19937-64
+ * + default in many places, standard
+ * - huge 2.5 KiB state (but TinyMT, 127 bits state space)
+ * - slow (but SFMT, twice as fast)
+ * - vulnerabilities on tinyMT?
+ *
+ * WELL
+ * - large state, 32-bit oriented
+ *
+ * ACORN Family (https://en.wikipedia.org/wiki/ACORN_(PRNG))
+ * - Fortran / double oriented, no C implementation found?!
+ * - medium large state "In practice we recommend using k > 10, M = 2^60 (for general application)
+ *   or M = 2^120 (for demanding applications requiring high-quality pseudo-random numbers
+ *   that will consistently pass all the tests in standard test packages such as TestU01)
+ *   and choose any odd value less than M for the seed."
+ *   k = 11 => 88 bytes/704-bits state, too large:-(
+ *
+ * Splitmix?
+ * Others?
+ *
+ * The conclusion is that we use xor-shift-rotate generator below.
+ */
+
+#include "c.h"
+#include "common/pg_prng.h"
+#include "port/pg_bitutils.h"
+#include <math.h>
+
+#define FIRST_BIT_MASK UINT64CONST(0x8000000000000000)
+#define RIGHT_HALF_MASK UINT64CONST(0x00000000FFFFFFFF)
+#define DMANTISSA_MASK UINT64CONST(0x000FFFFFFFFFFFFF)
+
+/* 64-bits rotate left */
+static inline uint64 rotl(const uint64 x, const int bits)
+{
+	return (x << bits) | (x >> (64 - bits));
+}
+
+/* smallest mask holding a value */
+static inline uint64 mask_u64(uint64 u)
+{
+	return (((uint64) 1) << (pg_leftmost_one_pos64(u) + 1)) - 1;
+}
+
+/* another 64-bits generator is used for state initialization or iterations */
+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);
+}
+
+/* seed prng state from a 64 bits integer, ensuring non zero */
+void pg_prng_seed(pg_prng_state *state, uint64 seed)
+{
+	state->s0 = splitmix64(&seed);
+	state->s1 = splitmix64(&seed);
+}
+
+/* seed with 53 bits (mantissa & sign) from a float */
+void pg_prng_fseed(pg_prng_state *state, double fseed)
+{
+	uint64 seed = (int64) (((double) DMANTISSA_MASK) * fseed);
+	pg_prng_seed(state, seed);
+}
+
+/* strong random seeding */
+void pg_prng_strong_seed(pg_prng_state *state)
+{
+	pg_strong_random((void *) state, sizeof(pg_prng_state));
+
+	/* avoid zero with Donald Knuth's LCG parameters */
+	if (unlikely(state->s0 == 0 && state->s1 == 0))
+	{
+		/* should it warn that something is amiss if we get there? */
+		pg_prng_state def = PG_PRNG_DEFAULT_STATE;
+		*state = def;
+	}
+}
+
+/* generator & state update */
+static uint64 xoroshiro128ss(pg_prng_state *state)
+{
+	const 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;
+}
+
+/* u64 generator */
+uint64 pg_prng_u64(pg_prng_state *state)
+{
+	return xoroshiro128ss(state);
+}
+
+/* select in a range with bitmask rejection */
+uint64 pg_prng_u64_range(pg_prng_state *state, uint64 range)
+{
+	/* we always iterate over the state once, for determinism */
+	uint64 next = xoroshiro128ss(state);
+	uint64 val = next;
+
+	if (range >= 2)
+	{
+		uint64 mask = mask_u64(range-1);
+
+		/* iterate over splitmix till val in [0, range) */
+		while ((val = val & mask) >= range)
+			val = splitmix64(&next);
+	}
+	else
+		val = 0;
+
+	return val;
+}
+
+/* i64 generator */
+int64 pg_prng_i64(pg_prng_state *state)
+{
+	return (int64) xoroshiro128ss(state);
+}
+
+/* u32 generator */
+uint32 pg_prng_u32(pg_prng_state *state)
+{
+	const uint64 v = xoroshiro128ss(state);
+	return (uint32) (((v >> 32) ^ v) & RIGHT_HALF_MASK);
+}
+
+/* i32 generator */
+int32 pg_prng_i32(pg_prng_state *state)
+{
+	const uint64 v = xoroshiro128ss(state);
+	return (int32) (((v >> 32) ^ v) & RIGHT_HALF_MASK);
+}
+
+/* double generator */
+double pg_prng_f64(pg_prng_state *state)
+{
+	uint64 v = xoroshiro128ss(state);
+	return ldexp((double) (v & DMANTISSA_MASK), -52);
+}
+
+/* bool generator */
+bool pg_prng_bool(pg_prng_state *state)
+{
+	uint64 v = xoroshiro128ss(state);
+	return (v & FIRST_BIT_MASK) == FIRST_BIT_MASK;
+}
diff --git a/src/include/common/pg_prng.h b/src/include/common/pg_prng.h
new file mode 100644
index 0000000000..1a3acf930f
--- /dev/null
+++ b/src/include/common/pg_prng.h
@@ -0,0 +1,34 @@
+/*-------------------------------------------------------------------------
+ *
+ * PRNG: internal Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * src/include/common/pg_prng.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_PRNG_H
+#define PG_PRNG_H
+
+/* 128 bits state */
+typedef struct pg_prng_state {
+	uint64	s0, s1;
+} pg_prng_state;
+
+/* use Donald Knuth's LCG constants for default state */
+#define PG_PRNG_DEFAULT_STATE \
+	{ UINT64CONST(0x5851F42D4C957F2D), UINT64CONST(0x14057B7EF767814F) }
+
+extern void pg_prng_seed(pg_prng_state *state, uint64 seed);
+extern void pg_prng_fseed(pg_prng_state *state, double fseed);
+extern void pg_prng_strong_seed(pg_prng_state *state);
+extern uint64 pg_prng_u64(pg_prng_state *state);
+extern uint64 pg_prng_u64_range(pg_prng_state *state, uint64 range);
+extern int64 pg_prng_i64(pg_prng_state *state);
+extern uint32 pg_prng_u32(pg_prng_state *state);
+extern int32 pg_prng_i32(pg_prng_state *state);
+extern double pg_prng_f64(pg_prng_state *state);
+extern bool pg_prng_bool(pg_prng_state *state);
+
+#endif							/* PG_PRNG_H */
diff --git a/src/include/optimizer/geqo.h b/src/include/optimizer/geqo.h
index 24dcdfb6cc..b28e4f084a 100644
--- a/src/include/optimizer/geqo.h
+++ b/src/include/optimizer/geqo.h
@@ -22,6 +22,7 @@
 #ifndef GEQO_H
 #define GEQO_H
 
+#include "common/pg_prng.h"
 #include "nodes/pathnodes.h"
 #include "optimizer/geqo_gene.h"
 
@@ -72,8 +73,8 @@ extern double Geqo_seed;		/* 0 .. 1 */
  */
 typedef struct
 {
-	List	   *initial_rels;	/* the base relations we are joining */
-	unsigned short random_state[3]; /* state for pg_erand48() */
+	List		   *initial_rels;	/* the base relations we are joining */
+	pg_prng_state	random_state;   /* PRNG state */
 } GeqoPrivateData;
 
 
diff --git a/src/include/port.h b/src/include/port.h
index 82f63de325..eb18665196 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -356,11 +356,6 @@ extern int	gettimeofday(struct timeval *tp, struct timezone *tzp);
 #define pgoff_t off_t
 #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
 extern int	fls(int mask);
 #endif
diff --git a/src/include/utils/sampling.h b/src/include/utils/sampling.h
index a58d14281b..c0b1c6a645 100644
--- a/src/include/utils/sampling.h
+++ b/src/include/utils/sampling.h
@@ -13,15 +13,14 @@
 #ifndef SAMPLING_H
 #define SAMPLING_H
 
+#include "common/pg_prng.h"
 #include "storage/block.h"		/* for typedef BlockNumber */
 
 
 /* Random generator for sampling code */
-typedef unsigned short SamplerRandomState[3];
-
 extern void sampler_random_init_state(long seed,
-									  SamplerRandomState randstate);
-extern double sampler_random_fract(SamplerRandomState randstate);
+									  pg_prng_state *randstate);
+extern double sampler_random_fract(pg_prng_state *randstate);
 
 /* Block sampling methods */
 
@@ -32,7 +31,7 @@ typedef struct
 	int			n;				/* desired sample size */
 	BlockNumber t;				/* current block number */
 	int			m;				/* blocks selected so far */
-	SamplerRandomState randstate;	/* random generator state */
+	pg_prng_state randstate;	/* random generator state */
 } BlockSamplerData;
 
 typedef BlockSamplerData *BlockSampler;
@@ -46,8 +45,9 @@ extern BlockNumber BlockSampler_Next(BlockSampler bs);
 
 typedef struct
 {
-	double		W;
-	SamplerRandomState randstate;	/* random generator state */
+	double			W;
+	pg_prng_state	randstate;	/* random generator state */
+	bool			randstate_initialized;
 } ReservoirStateData;
 
 typedef ReservoirStateData *ReservoirState;
diff --git a/src/port/Makefile b/src/port/Makefile
index 52dbf5783f..b3754d8940 100644
--- a/src/port/Makefile
+++ b/src/port/Makefile
@@ -42,7 +42,6 @@ OBJS = \
 	$(PG_CRC32C_OBJS) \
 	bsearch_arg.o \
 	chklocale.o \
-	erand48.o \
 	inet_net_ntop.o \
 	noblock.o \
 	path.o \
diff --git a/src/port/erand48.c b/src/port/erand48.c
deleted file mode 100644
index a82ea94220..0000000000
--- a/src/port/erand48.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/port/random.c b/src/port/random.c
deleted file mode 100644
index 2dd59a0829..0000000000
--- a/src/port/random.c
+++ /dev/null
@@ -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();
-}
diff --git a/src/port/srandom.c b/src/port/srandom.c
deleted file mode 100644
index cf1007b2ef..0000000000
--- a/src/port/srandom.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/test/modules/test_integerset/test_integerset.c b/src/test/modules/test_integerset/test_integerset.c
index 21c6f49b37..8935a81284 100644
--- a/src/test/modules/test_integerset/test_integerset.c
+++ b/src/test/modules/test_integerset/test_integerset.c
@@ -12,6 +12,7 @@
  */
 #include "postgres.h"
 
+#include "common/pg_prng.h"
 #include "fmgr.h"
 #include "lib/integerset.h"
 #include "miscadmin.h"
@@ -99,12 +100,16 @@ static void check_with_filler(IntegerSet *intset, uint64 x, uint64 value, uint64
 static void test_single_value_and_filler(uint64 value, uint64 filler_min, uint64 filler_max);
 static void test_huge_distances(void);
 
+static pg_prng_state	pr_state;
+
 /*
  * SQL-callable entry point to perform all tests.
  */
 Datum
 test_integerset(PG_FUNCTION_ARGS)
 {
+	pg_prng_strong_seed(&pr_state);
+
 	/* Tests for various corner cases */
 	test_empty();
 	test_huge_distances();
@@ -248,7 +253,7 @@ test_pattern(const test_spec *spec)
 		 * only a small part of the integer space is used.  We would very
 		 * rarely hit values that are actually in the set.
 		 */
-		x = (pg_lrand48() << 31) | pg_lrand48();
+		x = pg_prng_u64(&pr_state);
 		x = x % (last_int + 1000);
 
 		/* Do we expect this value to be present in the set? */
@@ -571,7 +576,7 @@ test_huge_distances(void)
 	 */
 	while (num_values < 1000)
 	{
-		val += pg_lrand48();
+		val += pg_prng_i32(&pr_state);
 		values[num_values++] = val;
 	}
 
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 233ddbf4c2..29d0f467cb 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -103,9 +103,9 @@ sub mkvcbuild
 	$solution = CreateSolution($vsVersion, $config);
 
 	our @pgportfiles = qw(
-	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c random.c
-	  srandom.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
+	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c
+	  getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
+	  snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
 	  dirent.c dlopen.c getopt.c getopt_long.c link.c
 	  pread.c preadv.c pwrite.c pwritev.c pg_bitutils.c
 	  pg_strong_random.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c
@@ -131,9 +131,9 @@ sub mkvcbuild
 	  config_info.c controldata_utils.c d2s.c encnames.c exec.c
 	  f2s.c file_perm.c file_utils.c hashfn.c hex.c ip.c jsonapi.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
-	  saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c username.c
-	  wait_error.c wchar.c);
+	  pg_get_line.c pg_lzcompress.c pg_prng.c pgfnames.c psprintf.c relpath.c
+	  rmtree.c saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c
+	  username.c wait_error.c wchar.c);
 
 	if ($solution->{options}->{openssl})
 	{
#25Dean Rasheed
dean.a.rasheed@gmail.com
In reply to: Fabien COELHO (#19)
Re: rand48 replacement

On Sat, 3 Jul 2021 at 08:06, Fabien COELHO <coelho@cri.ensmp.fr> wrote:

Here is a v4, which:

- moves the stuff to common and fully removes random/srandom (Tom)
- includes a range generation function based on the bitmask method (Dean)
but iterates with splitmix so that the state always advances once (Me)

At the risk of repeating myself: do *not* invent your own scheme.

The problem with iterating using splitmix is that splitmix is a simple
shuffling function that takes a single input and returns a mutated
output depending only on that input. So let's say for simplicity that
you're generating numbers in the range [0,N) with N=2^64-n and n<2^63.
Each of the n values in [N,2^64) that lie outside the range wanted are
just mapped in a deterministic way back onto (at most) n values in the
range [0,N), making those n values twice as likely to be chosen as the
other N-n values. So what you've just invented is an algorithm with
the complexity of the unbiased bitmask method, but basically the same
bias as the biased integer multiply method.

I don't understand why you object to advancing the state more than
once. Doing so doesn't make the resulting sequence of numbers any less
deterministic.

In fact, I'm pretty sure you *have to* advance the state more than
once in *any* unbiased scheme. That's a common characteristic of all
the unbiased methods I've seen, and intuitively, I think it has to be
so.

Otherwise, I'm happy with the use of the bitmask method, as long as
it's implemented correctly.

Regards,
Dean

#26Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Dean Rasheed (#25)
Re: rand48 replacement

Hello Dean,

- moves the stuff to common and fully removes random/srandom (Tom)
- includes a range generation function based on the bitmask method (Dean)
but iterates with splitmix so that the state always advances once (Me)

At the risk of repeating myself: do *not* invent your own scheme.

The problem with iterating using splitmix is that splitmix is a simple
shuffling function that takes a single input and returns a mutated
output depending only on that input.

It also iterates over its 64 bits state in a round robin fashion so that
the cycle size is 2^64 (it is a simple add).

So let's say for simplicity that you're generating numbers in the range
[0,N) with N=2^64-n and n<2^63. Each of the n values in [N,2^64) that
lie outside the range wanted are just mapped in a deterministic way back
onto (at most) n values in the range [0,N), making those n values twice
as likely to be chosen as the other N-n values.

I do understand your point. If the value is outside the range, splitmix
iterates over its seed and the extraction functions produces a new number
which is tested again. I do not understand the "mapped back onto" part,
the out of range value is just discarded and the loops starts over with a
new derivation, and why it would imply that some values are more likely to
come out.

So what you've just invented is an algorithm with the complexity of the
unbiased bitmask method,

That is what I am trying to implement.

but basically the same bias as the biased integer multiply method.

I did not understand.

I don't understand why you object to advancing the state more than
once. Doing so doesn't make the resulting sequence of numbers any less
deterministic.

It does, somehow, hence my struggle to try to avoid it.

call seed(0xdeadbeef);
x1 = somepseudorand();
x2 = somepseudorand();
x3 = somepsuedorand();

I think we should want x3 to be the same result whatever the previous
calls to the API.

In fact, I'm pretty sure you *have to* advance the state more than
once in *any* unbiased scheme. That's a common characteristic of all
the unbiased methods I've seen, and intuitively, I think it has to be
so.

Yes and no. We can advance another state seeded by the root prng.

Otherwise, I'm happy with the use of the bitmask method, as long as
it's implemented correctly.

I did not understand why it is not correct.

--
Fabien.

#27Dean Rasheed
dean.a.rasheed@gmail.com
In reply to: Fabien COELHO (#26)
Re: rand48 replacement

On Sun, 4 Jul 2021 at 10:35, Fabien COELHO <coelho@cri.ensmp.fr> wrote:

I did not understand why it is not correct.

Well, to make it easier to visualise, let's imagine our word size is
just 3 bits instead of 64 bits, and that the basic prng() function
generates numbers in the range [0,8). Similarly, imagine a splitmix3()
that operates on 3-bit values. So it might do something like this
(state offset and return values made up):

splitmix3(state):
state=0 -> 5, return 2
state=1 -> 6, return 5
state=2 -> 7, return 0
state=3 -> 0, return 3
state=4 -> 1, return 6
state=5 -> 2, return 1
state=6 -> 3, return 7
state=7 -> 4, return 4

Now suppose we want a random number in the range [0,6). This is what
happens with your algorithm for each of the possible prng() return
values:

prng() returns 0 -- OK
prng() returns 1 -- OK
prng() returns 2 -- OK
prng() returns 3 -- OK
prng() returns 4 -- OK
prng() returns 5 -- OK
prng() returns 6 -- out of range so use splitmix3() with initial state=6:
state=6 -> 3, return 7 -- still out of range, so repeat
state=3 -> 0, return 3 -- now OK
prng() returns 7 -- out of range so use splitmix3() with initial state=7:
state=7 -> 4, return 4 -- now OK

So, assuming that prng() chooses each of the 8 possible values with
equal probability (1/8), the overall result is that the values 0,1,2
and 5 are returned with a probability of 1/8, whereas 3 and 4 are
returned with a probability of 2/8.

Using the correct implementation of the bitmask algorithm, each
iteration calls prng() again, so in the end no particular return value
is ever more likely than any other (hence it's unbiased).

As for determinism, the end result is still fully deterministic. For
example, lets say that prng() returns the following sequence, for some
initial state:

1,0,3,0,3,7,4,7,6,6,5,3,7,7,7,0,3,6,5,2,3,3,4,0,0,2,7,4,...

then the bitmask method just returns that sequence with all the 6's
and 7's removed:

1,0,3,0,3,4,5,3,0,3,5,2,3,3,4,0,0,2,4,...

and that same sequence will always be returned, when starting from
that initial seed.

Regards,
Dean

#28Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Dean Rasheed (#27)
1 attachment(s)
Re: rand48 replacement

Now suppose we want a random number in the range [0,6). This is what
happens with your algorithm for each of the possible prng() return
values:

prng() returns 0 -- OK
prng() returns 1 -- OK
prng() returns 2 -- OK
prng() returns 3 -- OK
prng() returns 4 -- OK
prng() returns 5 -- OK
prng() returns 6 -- out of range so use splitmix3() with initial state=6:
state=6 -> 3, return 7 -- still out of range, so repeat
state=3 -> 0, return 3 -- now OK
prng() returns 7 -- out of range so use splitmix3() with initial state=7:
state=7 -> 4, return 4 -- now OK

So, assuming that prng() chooses each of the 8 possible values with
equal probability (1/8), the overall result is that the values 0,1,2
and 5 are returned with a probability of 1/8, whereas 3 and 4 are
returned with a probability of 2/8.

Ok, I got that explanation.

Using the correct implementation of the bitmask algorithm, each
iteration calls prng() again, so in the end no particular return value
is ever more likely than any other (hence it's unbiased).

Ok, you're taking into account the number of states of the PRNG, so this
finite number implies some bias on some values if you actually enumerate
all possible cases, as you do above.

I was reasoning "as if" the splitmix PRNG was an actual random function,
which is obviously false, but is also somehow a usual (false) assumption
with PRNGs, and with this false assumption my implementation is perfect:-)

The defect of the modulo method for range extraction is that even with an
actual (real) random generator the results would be biased. The bias is in
the method itself. Now you are arguing for a bias linked to the internals
of the PRNG. They are not the same in nature, even if the effect is the
same.

Also the bias is significant for close to the limit ranges, which is not
the kind of use case I have in mind when looking at pgbench.

If you consider the PRNG internals, then splitmix extraction function
could also be taken into account. If it is not invertible (I'm unsure),
then assuming it is some kind of hash function, about 1/e of output values
would not reachable, which is yet another bias that you could argue
against.

Using the initial PRNG works better only because the underlying 128 bits
state is much larger than the output value. Which is the point for having
a larger state in the first place, anyway.

As for determinism, the end result is still fully deterministic. For
example, lets say that prng() returns the following sequence, for some
initial state:

1,0,3,0,3,7,4,7,6,6,5,3,7,7,7,0,3,6,5,2,3,3,4,0,0,2,7,4,...

then the bitmask method just returns that sequence with all the 6's
and 7's removed:

1,0,3,0,3,4,5,3,0,3,5,2,3,3,4,0,0,2,4,...

and that same sequence will always be returned, when starting from
that initial seed.

Yes and no.

The result is indeed deterministic of you call the function with the same
range. However, if you change the range value in one place then sometimes
the state can advance differently, so the determinism is lost, meaning
that it depends on actual range values.

Attached a v7 which does as you wish, but also looses the deterministic
non-value dependent property I was seeking. I would work around that by
deriving another 128 bit generator instead of splitmix 64 bit, but that is
overkill.

--
Fabien.

Attachments:

prng-7.patchtext/x-diff; NAME=prng-7.patchDownload
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 2c2f149fb0..146b524076 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -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]);
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index fafbab6b02..3009861e45 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -5152,7 +5152,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]);
 		}
diff --git a/contrib/tsm_system_rows/tsm_system_rows.c b/contrib/tsm_system_rows/tsm_system_rows.c
index 4996612902..1a46d4b143 100644
--- a/contrib/tsm_system_rows/tsm_system_rows.c
+++ b/contrib/tsm_system_rows/tsm_system_rows.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/contrib/tsm_system_time/tsm_system_time.c b/contrib/tsm_system_time/tsm_system_time.c
index 788d8f9a68..36acc6c106 100644
--- a/contrib/tsm_system_time/tsm_system_time.c
+++ b/contrib/tsm_system_time/tsm_system_time.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 0c9591415e..1ddbeed2dc 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1308,7 +1308,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 					 * Found a suitable tuple, so save it, replacing one old
 					 * tuple at random
 					 */
-					int			k = (int) (targrows * sampler_random_fract(rstate.randstate));
+					int			k = (int) (targrows * sampler_random_fract(&rstate.randstate));
 
 					Assert(k >= 0 && k < targrows);
 					heap_freetuple(rows[k]);
diff --git a/src/backend/optimizer/geqo/geqo_random.c b/src/backend/optimizer/geqo/geqo_random.c
index f21bc047e6..8b42a9ffba 100644
--- a/src/backend/optimizer/geqo/geqo_random.c
+++ b/src/backend/optimizer/geqo/geqo_random.c
@@ -21,14 +21,7 @@ geqo_set_seed(PlannerInfo *root, double seed)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	/*
-	 * XXX. This seeding algorithm could certainly be improved - but it is not
-	 * critical to do so.
-	 */
-	memset(private->random_state, 0, sizeof(private->random_state));
-	memcpy(private->random_state,
-		   &seed,
-		   Min(sizeof(private->random_state), sizeof(seed)));
+	pg_prng_fseed(&private->random_state, seed);
 }
 
 double
@@ -36,5 +29,5 @@ geqo_rand(PlannerInfo *root)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	return pg_erand48(private->random_state);
+	return pg_prng_f64(&private->random_state);
 }
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 098bbb372b..9fc0b108dd 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -21,6 +21,7 @@
 
 #include "catalog/pg_type.h"
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/shortest_dec.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
@@ -65,7 +66,7 @@ float8		degree_c_one = 1.0;
 
 /* State for drandom() and setseed() */
 static bool drandom_seed_set = false;
-static unsigned short drandom_seed[3] = {0, 0, 0};
+static pg_prng_state drandom_seed = PG_PRNG_DEFAULT_STATE;
 
 /* Local function prototypes */
 static double sind_q1(double x);
@@ -2762,22 +2763,20 @@ drandom(PG_FUNCTION_ARGS)
 		 * Should that fail for some reason, we fall back on a lower-quality
 		 * seed based on current time and PID.
 		 */
-		if (!pg_strong_random(drandom_seed, sizeof(drandom_seed)))
+		if (!pg_strong_random(&drandom_seed, sizeof(drandom_seed)))
 		{
 			TimestampTz now = GetCurrentTimestamp();
 			uint64		iseed;
 
 			/* Mix the PID with the most predictable bits of the timestamp */
 			iseed = (uint64) now ^ ((uint64) MyProcPid << 32);
-			drandom_seed[0] = (unsigned short) iseed;
-			drandom_seed[1] = (unsigned short) (iseed >> 16);
-			drandom_seed[2] = (unsigned short) (iseed >> 32);
+			pg_prng_seed(&drandom_seed, iseed);
 		}
 		drandom_seed_set = true;
 	}
 
-	/* pg_erand48 produces desired result range [0.0 - 1.0) */
-	result = pg_erand48(drandom_seed);
+	/* produces desired result range [0.0 - 1.0) */
+	result = pg_prng_f64(&drandom_seed);
 
 	PG_RETURN_FLOAT8(result);
 }
@@ -2790,7 +2789,6 @@ Datum
 setseed(PG_FUNCTION_ARGS)
 {
 	float8		seed = PG_GETARG_FLOAT8(0);
-	uint64		iseed;
 
 	if (seed < -1 || seed > 1 || isnan(seed))
 		ereport(ERROR,
@@ -2798,11 +2796,7 @@ setseed(PG_FUNCTION_ARGS)
 				 errmsg("setseed parameter %g is out of allowed range [-1,1]",
 						seed)));
 
-	/* Use sign bit + 47 fractional bits to fill drandom_seed[] */
-	iseed = (int64) (seed * (float8) UINT64CONST(0x7FFFFFFFFFFF));
-	drandom_seed[0] = (unsigned short) iseed;
-	drandom_seed[1] = (unsigned short) (iseed >> 16);
-	drandom_seed[2] = (unsigned short) (iseed >> 32);
+	pg_prng_fseed(&drandom_seed, seed);
 	drandom_seed_set = true;
 
 	PG_RETURN_VOID();
diff --git a/src/backend/utils/misc/sampling.c b/src/backend/utils/misc/sampling.c
index 0c327e823f..7b59128878 100644
--- a/src/backend/utils/misc/sampling.c
+++ b/src/backend/utils/misc/sampling.c
@@ -49,7 +49,7 @@ BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize,
 	bs->t = 0;					/* blocks scanned so far */
 	bs->m = 0;					/* blocks selected so far */
 
-	sampler_random_init_state(randseed, bs->randstate);
+	sampler_random_init_state(randseed, &bs->randstate);
 
 	return Min(bs->n, bs->N);
 }
@@ -98,7 +98,7 @@ BlockSampler_Next(BlockSampler bs)
 	 * less than k, which means that we cannot fail to select enough blocks.
 	 *----------
 	 */
-	V = sampler_random_fract(bs->randstate);
+	V = sampler_random_fract(&bs->randstate);
 	p = 1.0 - (double) k / (double) K;
 	while (V < p)
 	{
@@ -136,10 +136,10 @@ reservoir_init_selection_state(ReservoirState rs, int n)
 	 * Reservoir sampling is not used anywhere where it would need to return
 	 * repeatable results so we can initialize it randomly.
 	 */
-	sampler_random_init_state(random(), rs->randstate);
+	sampler_random_init_state(random(), &rs->randstate);
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	rs->W = exp(-log(sampler_random_fract(rs->randstate)) / n);
+	rs->W = exp(-log(sampler_random_fract(&rs->randstate)) / n);
 }
 
 double
@@ -154,7 +154,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 		double		V,
 					quot;
 
-		V = sampler_random_fract(rs->randstate);	/* Generate V */
+		V = sampler_random_fract(&rs->randstate);	/* Generate V */
 		S = 0;
 		t += 1;
 		/* Note: "num" in Vitter's code is always equal to t - n */
@@ -186,7 +186,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 						tmp;
 
 			/* Generate U and X */
-			U = sampler_random_fract(rs->randstate);
+			U = sampler_random_fract(&rs->randstate);
 			X = t * (W - 1.0);
 			S = floor(X);		/* S is tentatively set to floor(X) */
 			/* Test if U <= h(S)/cg(X) in the manner of (6.3) */
@@ -215,7 +215,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 				y *= numer / denom;
 				denom -= 1;
 			}
-			W = exp(-log(sampler_random_fract(rs->randstate)) / n); /* Generate W in advance */
+			W = exp(-log(sampler_random_fract(&rs->randstate)) / n); /* Generate W in advance */
 			if (exp(log(y) / n) <= (t + X) / t)
 				break;
 		}
@@ -230,24 +230,22 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
  *----------
  */
 void
-sampler_random_init_state(long seed, SamplerRandomState randstate)
+sampler_random_init_state(long seed, pg_prng_state *randstate)
 {
-	randstate[0] = 0x330e;		/* same as pg_erand48, but could be anything */
-	randstate[1] = (unsigned short) seed;
-	randstate[2] = (unsigned short) (seed >> 16);
+	pg_prng_seed(randstate, (uint64_t) seed);
 }
 
 /* Select a random value R uniformly distributed in (0 - 1) */
 double
-sampler_random_fract(SamplerRandomState randstate)
+sampler_random_fract(pg_prng_state *randstate)
 {
 	double		res;
 
-	/* pg_erand48 returns a value in [0.0 - 1.0), so we must reject 0 */
+	/* pg_prng_f64 returned value in [0.0 - 1.0), so we must reject 0.0 */
 	do
 	{
-		res = pg_erand48(randstate);
-	} while (res == 0.0);
+		res = pg_prng_f64(randstate);
+	} while (unlikely(res == 0.0));
 	return res;
 }
 
@@ -266,22 +264,28 @@ double
 anl_random_fract(void)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(random(), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* and compute a random fraction */
-	return sampler_random_fract(oldrs.randstate);
+	return sampler_random_fract(&oldrs.randstate);
 }
 
 double
 anl_init_selection_state(int n)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(random(), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	return exp(-log(sampler_random_fract(oldrs.randstate)) / n);
+	return exp(-log(sampler_random_fract(&oldrs.randstate)) / n);
 }
 
 double
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 4aeccd93af..6d749bbbb6 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -58,6 +58,7 @@
 #endif
 
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/logging.h"
 #include "common/string.h"
 #include "common/username.h"
@@ -343,16 +344,8 @@ typedef struct StatsData
 	SimpleStats lag;
 } StatsData;
 
-/*
- * Struct to keep random state.
- */
-typedef struct RandomState
-{
-	unsigned short xseed[3];
-} RandomState;
-
 /* 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 */
 static THREAD_BARRIER_T barrier;
@@ -454,7 +447,7 @@ typedef struct
 	 * Separate randomness for each client. This is used for random functions
 	 * 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			command;		/* command number in script */
@@ -491,9 +484,9 @@ typedef struct
 	 * random state to make all of them independent of each other and
 	 * therefore deterministic at the thread level.
 	 */
-	RandomState ts_choose_rs;	/* random state for selecting a script */
-	RandomState ts_throttle_rs; /* random state for transaction throttling */
-	RandomState ts_sample_rs;	/* random state for log sampling */
+	pg_prng_state	ts_choose_rs;	/* random state for selecting a script */
+	pg_prng_state	ts_throttle_rs; /* random state for transaction throttling */
+	pg_prng_state	ts_sample_rs;	/* random state for log sampling */
 
 	int64		throttle_trigger;	/* previous/next throttling (us) */
 	FILE	   *logfile;		/* where to log, or NULL */
@@ -891,42 +884,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.
  */
 static void
-initRandomState(RandomState *random_state)
+initRandomState(pg_prng_state *state)
 {
-	random_state->xseed[0] = (unsigned short)
-		(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);
+	pg_prng_seed(state, pg_prng_u64(&base_random_sequence));
 }
 
+
 /*
- * Random number generator: uniform distribution from min to max inclusive.
+ * PRNG: uniform distribution from min to max inclusive.
  *
  * Although the limits are expressed as int64, you can't generate the full
  * int64 range in one call, because the difference of the limits mustn't
- * overflow int64.  In practice it's unwise to ask for more than an int32
- * range, because of the limited precision of pg_erand48().
+ * overflow int64.
  */
 static int64
-getrand(RandomState *random_state, int64 min, int64 max)
+getrand(pg_prng_state *state, int64 min, int64 max)
 {
-	/*
-	 * 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));
+	return min + (int64) pg_prng_u64_range(state, max - min + 1);
 }
 
 /*
@@ -935,8 +914,7 @@ getrand(RandomState *random_state, int64 min, int64 max)
  * value is exp(-parameter).
  */
 static int64
-getExponentialRand(RandomState *random_state, int64 min, int64 max,
-				   double parameter)
+getExponentialRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		cut,
 				uniform,
@@ -946,7 +924,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 	Assert(parameter > 0.0);
 	cut = exp(-parameter);
 	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	uniform = 1.0 - pg_prng_f64(state);
 
 	/*
 	 * inner expression in (cut, 1] (if parameter > 0), rand in [0, 1)
@@ -959,8 +937,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 
 /* random number generator: gaussian distribution from min to max inclusive */
 static int64
-getGaussianRand(RandomState *random_state, int64 min, int64 max,
-				double parameter)
+getGaussianRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		stdev;
 	double		rand;
@@ -983,13 +960,13 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
 	do
 	{
 		/*
-		 * pg_erand48 generates [0,1), but for the basic version of the
+		 * pg_prng_f64 generates [0,1), but for the basic version of the
 		 * Box-Muller transform the two uniformly distributed random numbers
 		 * are expected in (0, 1] (see
 		 * https://en.wikipedia.org/wiki/Box-Muller_transform)
 		 */
-		double		rand1 = 1.0 - pg_erand48(random_state->xseed);
-		double		rand2 = 1.0 - pg_erand48(random_state->xseed);
+		double		rand1 = 1.0 - pg_prng_f64(state);
+		double		rand2 = 1.0 - pg_prng_f64(state);
 
 		/* Box-Muller basic form transform */
 		double		var_sqrt = sqrt(-2.0 * log(rand1));
@@ -1019,7 +996,7 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
  * not be one.
  */
 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
@@ -1027,8 +1004,8 @@ getPoissonRand(RandomState *random_state, double center)
 	 */
 	double		uniform;
 
-	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	/* pseudo-random value in [0, 1), uniform in (0, 1] */
+	uniform = 1.0 - pg_prng_f64(state);
 
 	return (int64) (-log(uniform) * center + 0.5);
 }
@@ -1041,7 +1018,7 @@ getPoissonRand(RandomState *random_state, double center)
  * This works for s > 1.0, but may perform badly for s very close to 1.0.
  */
 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		x,
@@ -1056,8 +1033,8 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 	while (true)
 	{
 		/* random variates */
-		u = pg_erand48(random_state->xseed);
-		v = pg_erand48(random_state->xseed);
+		u = pg_prng_f64(state);
+		v = pg_prng_f64(state);
 
 		x = floor(pow(u, -1.0 / (s - 1.0)));
 
@@ -1071,14 +1048,14 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 
 /* random number generator: zipfian distribution from min to max inclusive */
 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;
 
 	/* abort if parameter is invalid */
 	Assert(MIN_ZIPFIAN_PARAM <= s && s <= MAX_ZIPFIAN_PARAM);
 
-	return min - 1 + computeIterativeZipfian(random_state, n, s);
+	return min - 1 + computeIterativeZipfian(state, n, s);
 }
 
 /*
@@ -1135,7 +1112,7 @@ getHashMurmur2(int64 val, uint64 seed)
  * For small sizes, this generates each of the (size!) possible permutations
  * of integers in the range [0, size) with roughly equal probability.  Once
  * the size is larger than 20, the number of possible permutations exceeds the
- * number of distinct states of the internal pseudorandom number generators,
+ * number of distinct states of the internal pseudorandom number generator,
  * and so not all possible permutations can be generated, but the permutations
  * chosen should continue to give the appearance of being random.
  *
@@ -1145,25 +1122,19 @@ getHashMurmur2(int64 val, uint64 seed)
 static int64
 permute(const int64 val, const int64 isize, const int64 seed)
 {
-	RandomState random_state1;
-	RandomState random_state2;
-	uint64		size;
-	uint64		v;
-	int			masklen;
-	uint64		mask;
-	int			i;
+	/* using a high-end PRNG is probably overkill */
+	pg_prng_state	state;
+	uint64			size;
+	uint64			v;
+	int				masklen;
+	uint64			mask;
+	int				i;
 
 	if (isize < 2)
 		return 0;				/* nothing to permute */
 
-	/* Initialize a pair of random states using the seed */
-	random_state1.xseed[0] = seed & 0xFFFF;
-	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;
+	/* Initialize prng state using the seed */
+	pg_prng_seed(&state, (uint64) seed);
 
 	/* Computations are performed on unsigned values */
 	size = (uint64) isize;
@@ -1209,8 +1180,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 					t;
 
 		/* Random multiply (by an odd number), XOR and rotate of lower half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		if (v <= mask)
 		{
 			v = ((v * m) ^ r) & mask;
@@ -1218,8 +1189,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random multiply (by an odd number), XOR and rotate of upper half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		t = size - 1 - v;
 		if (t <= mask)
 		{
@@ -1229,7 +1200,7 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random offset */
-		r = (uint64) getrand(&random_state2, 0, size - 1);
+		r = pg_prng_u64(&state) % (size - 1);
 		v = (v + r) % size;
 	}
 
@@ -3790,7 +3761,7 @@ doLog(TState *thread, CState *st,
 	 * to the random sample.
 	 */
 	if (sample_rate != 0.0 &&
-		pg_erand48(thread->ts_sample_rs.xseed) > sample_rate)
+		pg_prng_f64(&thread->ts_sample_rs) > sample_rate)
 		return;
 
 	/* should we aggregate the results or not? */
@@ -5714,12 +5685,11 @@ set_random_seed(const char *seed)
 
 	if (seed != NULL)
 		pg_log_info("setting random seed to %llu", (unsigned long long) iseed);
+
 	random_seed = iseed;
 
 	/* Fill base_random_sequence with low-order bits of seed */
-	base_random_sequence.xseed[0] = iseed & 0xFFFF;
-	base_random_sequence.xseed[1] = (iseed >> 16) & 0xFFFF;
-	base_random_sequence.xseed[2] = (iseed >> 32) & 0xFFFF;
+	pg_prng_seed(&base_random_sequence, (uint64_t) iseed);
 
 	return true;
 }
@@ -6405,9 +6375,7 @@ main(int argc, char **argv)
 	/* set default seed for hash functions */
 	if (lookupVariable(&state[0], "default_seed") == NULL)
 	{
-		uint64		seed =
-		((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) |
-		(((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) << 32);
+		uint64		seed = pg_prng_u64(&base_random_sequence);
 
 		for (i = 0; i < nclients; i++)
 			if (!putVariableInt(&state[i], "startup", "default_seed", (int64) seed))
diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl
index 3aa9d5d753..a5a9bcbd56 100644
--- a/src/bin/pgbench/t/001_pgbench_with_server.pl
+++ b/src/bin/pgbench/t/001_pgbench_with_server.pl
@@ -427,9 +427,9 @@ pgbench(
 
 		# After explicit seeding, the four random checks (1-3,20) are
 		# deterministic
-		qr{command=1.: int 13\b},      # uniform random
-		qr{command=2.: int 116\b},     # exponential random
-		qr{command=3.: int 1498\b},    # gaussian random
+		qr{command=1.: int 18\b},      # uniform random
+		qr{command=2.: int 100\b},     # exponential random
+		qr{command=3.: int 1526\b},    # gaussian random
 		qr{command=4.: int 4\b},
 		qr{command=5.: int 5\b},
 		qr{command=6.: int 6\b},
@@ -496,6 +496,7 @@ pgbench(
 		qr{command=109.: boolean true\b},
 		qr{command=110.: boolean true\b},
 		qr{command=111.: boolean true\b},
+		qr{command=113.: boolean true\b},
 	],
 	'pgbench expressions',
 	{
@@ -639,8 +640,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))
 -- actual values
 \set t debug(permute(:v, 1) = 0)
-\set t debug(permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1 and \
-             permute(0, 2, 5435) = 1 and permute(1, 2, 5435) = 0)
+\set t debug(permute(0, 2, 5431) = 1 and permute(1, 2, 5431) = 0 and \
+             permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1)
+-- 63 bits tests for checking permute portability across architectures
+\set size debug(:max - 10)
+\set t debug(permute(:size-1, :size, 5432) = 4352241160009873020 and \
+             permute(:size-2, :size, 5432) = 4990004119691638710 and \
+             permute(:size-3, :size, 5432) = 5868029867256127549 and \
+             permute(:size-4, :size, 5432) = 1238830324886341378 and \
+             permute(:size-5, :size, 5432) = 7415914367102906897 and \
+             permute(:size-6, :size, 5432) = 3214501037984818995 and \
+             permute(:size-7, :size, 5432) =  600660351261124695)
 }
 	});
 
diff --git a/src/common/Makefile b/src/common/Makefile
index 38a8599337..9aee26f694 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -67,6 +67,7 @@ OBJS_COMMON = \
 	md5_common.o \
 	pg_get_line.o \
 	pg_lzcompress.o \
+	pg_prng.o \
 	pgfnames.o \
 	psprintf.o \
 	relpath.o \
diff --git a/src/common/pg_prng.c b/src/common/pg_prng.c
new file mode 100644
index 0000000000..78ab62509b
--- /dev/null
+++ b/src/common/pg_prng.c
@@ -0,0 +1,227 @@
+/*
+ * Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * Previous version was the 1988 (?) rand48 standard with 48 bits state LCG,
+ * designed for running on 32 bits or even 16 bits architectures, and to produce
+ * 32 bit ints and floats (i.e. *not* doubles).
+ *
+ * The implied 16-bits unpacking/packing may have a detrimental performance impact
+ * on modern 64 bits architectures.
+ *
+ * We (probably) want:
+ * - one reasonable default PRNG for all pg internal uses.
+ * - NOT to invent a new design!
+ * - something fast, close to rand48 (which basically does 2 arithmetic ops)
+ *   no need for something cryptographic though, which would imply slow
+ * - to produce 64 bits integers & doubles with a 52 bits mantissa
+ *   so state size > 64 bits.
+ * - a small state though, because we might generate quite a few of them
+ *   for different purposes so state size <= 256 or even <= 128 bits
+ * - the state to be aligned to whatever, so state size = 128 bits
+ * - 64 bits operations for efficiency on modern architectures,
+ *   but NOT 128 bits operations.
+ * - not to depend on special hardware for speed (eg MMX/SSE/AES).
+ * - not something with obvious known and relevant defects.
+ * - not something with "rights" attached.
+ *
+ * These constraints reduce a lot the available options from
+ * https://en.wikipedia.org/wiki/List_of_random_number_generators
+ *
+ * LCG
+ * - POSIX rand48: m = 2^48, a = 25214903917, c = 11, extract 47...16
+ * - MMIX by Donald Knuth: m = 2^64, a = 6364136223846793005, c = 1442695040888963407
+ * - very bad on low bits, which MUST NOT BE USED
+ *   a 128 bits would do, but no clear standards, and it would imply 128-bit ops,
+ *   see PCG below.
+ *
+ * PCG Family (https://www.pcg-random.org/)
+ * - this is basically an LCG with an improved outpout function
+ *   so as to avoid typical LCG issues and pass stats tests.
+ * - recommands state size to be twice the output size,
+ *   which seems a reasonable requirement.
+ * - this implies 128 bit ops for our target parameters,
+ *   otherwise it is a fairly good candidate for simplicity,
+ *   efficiency and quality.
+ *
+ * Xor-Shift-Rotate PRNGs:
+ * - a generic annoying feature of this generator class is that there is one
+ *   special all-zero state which must not be used, inducing a constraint on
+ *   initialization, and when the generator get close to it (i.e. many zeros
+ *   in the state) it takes some iterations to recover, with small lumps
+ *   of close/related/bad numbers generated in the process.
+ * Xoroshiro128+
+ * - fails statistical linearity tests in the low bits.
+ * Xoroshiro128**
+ *   https://prng.di.unimi.it/xoroshiro128starstar.c
+ * - mild and irrelevant issues: close to zero state, stats issues after some transforms
+ * Xoshiro256**
+ * - it looks good, but some issues (no big deal for pg usage IMO)
+ *   https://www.pcg-random.org/posts/a-quick-look-at-xoshiro256.html
+ *   mild problems getting out of closes-to-zero state
+ *
+ * MT19937-64
+ * + default in many places, standard
+ * - huge 2.5 KiB state (but TinyMT, 127 bits state space)
+ * - slow (but SFMT, twice as fast)
+ * - vulnerabilities on tinyMT?
+ *
+ * WELL
+ * - large state, 32-bit oriented
+ *
+ * ACORN Family (https://en.wikipedia.org/wiki/ACORN_(PRNG))
+ * - Fortran / double oriented, no C implementation found?!
+ * - medium large state "In practice we recommend using k > 10, M = 2^60 (for general application)
+ *   or M = 2^120 (for demanding applications requiring high-quality pseudo-random numbers
+ *   that will consistently pass all the tests in standard test packages such as TestU01)
+ *   and choose any odd value less than M for the seed."
+ *   k = 11 => 88 bytes/704-bits state, too large:-(
+ *
+ * Splitmix?
+ * Others?
+ *
+ * The conclusion is that we use xor-shift-rotate generator below.
+ */
+
+#include "c.h"
+#include "common/pg_prng.h"
+#include "port/pg_bitutils.h"
+#include <math.h>
+
+#define FIRST_BIT_MASK UINT64CONST(0x8000000000000000)
+#define RIGHT_HALF_MASK UINT64CONST(0x00000000FFFFFFFF)
+#define DMANTISSA_MASK UINT64CONST(0x000FFFFFFFFFFFFF)
+
+/* 64-bits rotate left */
+static inline uint64 rotl(const uint64 x, const int bits)
+{
+	return (x << bits) | (x >> (64 - bits));
+}
+
+/* smallest mask holding a value */
+static inline uint64 mask_u64(uint64 u)
+{
+	return (((uint64) 1) << (pg_leftmost_one_pos64(u) + 1)) - 1;
+}
+
+/* another 64-bits generator is used for state initialization or iterations */
+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);
+}
+
+/* seed prng state from a 64 bits integer, ensuring non zero */
+void pg_prng_seed(pg_prng_state *state, uint64 seed)
+{
+	state->s0 = splitmix64(&seed);
+	state->s1 = splitmix64(&seed);
+}
+
+/* seed with 53 bits (mantissa & sign) from a float */
+void pg_prng_fseed(pg_prng_state *state, double fseed)
+{
+	uint64 seed = (int64) (((double) DMANTISSA_MASK) * fseed);
+	pg_prng_seed(state, seed);
+}
+
+/* strong random seeding */
+void pg_prng_strong_seed(pg_prng_state *state)
+{
+	pg_strong_random((void *) state, sizeof(pg_prng_state));
+
+	/* avoid zero with Donald Knuth's LCG parameters */
+	if (unlikely(state->s0 == 0 && state->s1 == 0))
+	{
+		/* should it warn that something is amiss if we get there? */
+		pg_prng_state def = PG_PRNG_DEFAULT_STATE;
+		*state = def;
+	}
+}
+
+/* generator & state update */
+static uint64 xoroshiro128ss(pg_prng_state *state)
+{
+	const 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;
+}
+
+/* u64 generator */
+uint64 pg_prng_u64(pg_prng_state *state)
+{
+	return xoroshiro128ss(state);
+}
+
+/*
+ * select in a range with bitmask rejection.
+ *
+ * the prng may advance by several states or none,
+ * depending on the range value.
+ */
+uint64 pg_prng_u64_range(pg_prng_state *state, uint64 range)
+{
+	uint64 val;
+
+	if (range >= 2)
+	{
+		uint64 mask = mask_u64(range-1);
+
+		/* iterate till val in [0, range) */
+		do {
+			val = xoroshiro128ss(state) & mask;
+		}
+		while (val >= range);
+	}
+	else
+		val = 0;
+
+	return val;
+}
+
+/* i64 generator */
+int64 pg_prng_i64(pg_prng_state *state)
+{
+	return (int64) xoroshiro128ss(state);
+}
+
+/* u32 generator */
+uint32 pg_prng_u32(pg_prng_state *state)
+{
+	const uint64 v = xoroshiro128ss(state);
+	return (uint32) (((v >> 32) ^ v) & RIGHT_HALF_MASK);
+}
+
+/* i32 generator */
+int32 pg_prng_i32(pg_prng_state *state)
+{
+	const uint64 v = xoroshiro128ss(state);
+	return (int32) (((v >> 32) ^ v) & RIGHT_HALF_MASK);
+}
+
+/* double generator */
+double pg_prng_f64(pg_prng_state *state)
+{
+	uint64 v = xoroshiro128ss(state);
+	return ldexp((double) (v & DMANTISSA_MASK), -52);
+}
+
+/* bool generator */
+bool pg_prng_bool(pg_prng_state *state)
+{
+	uint64 v = xoroshiro128ss(state);
+	return (v & FIRST_BIT_MASK) == FIRST_BIT_MASK;
+}
diff --git a/src/include/common/pg_prng.h b/src/include/common/pg_prng.h
new file mode 100644
index 0000000000..1a3acf930f
--- /dev/null
+++ b/src/include/common/pg_prng.h
@@ -0,0 +1,34 @@
+/*-------------------------------------------------------------------------
+ *
+ * PRNG: internal Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * src/include/common/pg_prng.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_PRNG_H
+#define PG_PRNG_H
+
+/* 128 bits state */
+typedef struct pg_prng_state {
+	uint64	s0, s1;
+} pg_prng_state;
+
+/* use Donald Knuth's LCG constants for default state */
+#define PG_PRNG_DEFAULT_STATE \
+	{ UINT64CONST(0x5851F42D4C957F2D), UINT64CONST(0x14057B7EF767814F) }
+
+extern void pg_prng_seed(pg_prng_state *state, uint64 seed);
+extern void pg_prng_fseed(pg_prng_state *state, double fseed);
+extern void pg_prng_strong_seed(pg_prng_state *state);
+extern uint64 pg_prng_u64(pg_prng_state *state);
+extern uint64 pg_prng_u64_range(pg_prng_state *state, uint64 range);
+extern int64 pg_prng_i64(pg_prng_state *state);
+extern uint32 pg_prng_u32(pg_prng_state *state);
+extern int32 pg_prng_i32(pg_prng_state *state);
+extern double pg_prng_f64(pg_prng_state *state);
+extern bool pg_prng_bool(pg_prng_state *state);
+
+#endif							/* PG_PRNG_H */
diff --git a/src/include/optimizer/geqo.h b/src/include/optimizer/geqo.h
index 24dcdfb6cc..b28e4f084a 100644
--- a/src/include/optimizer/geqo.h
+++ b/src/include/optimizer/geqo.h
@@ -22,6 +22,7 @@
 #ifndef GEQO_H
 #define GEQO_H
 
+#include "common/pg_prng.h"
 #include "nodes/pathnodes.h"
 #include "optimizer/geqo_gene.h"
 
@@ -72,8 +73,8 @@ extern double Geqo_seed;		/* 0 .. 1 */
  */
 typedef struct
 {
-	List	   *initial_rels;	/* the base relations we are joining */
-	unsigned short random_state[3]; /* state for pg_erand48() */
+	List		   *initial_rels;	/* the base relations we are joining */
+	pg_prng_state	random_state;   /* PRNG state */
 } GeqoPrivateData;
 
 
diff --git a/src/include/port.h b/src/include/port.h
index 82f63de325..eb18665196 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -356,11 +356,6 @@ extern int	gettimeofday(struct timeval *tp, struct timezone *tzp);
 #define pgoff_t off_t
 #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
 extern int	fls(int mask);
 #endif
diff --git a/src/include/utils/sampling.h b/src/include/utils/sampling.h
index a58d14281b..c0b1c6a645 100644
--- a/src/include/utils/sampling.h
+++ b/src/include/utils/sampling.h
@@ -13,15 +13,14 @@
 #ifndef SAMPLING_H
 #define SAMPLING_H
 
+#include "common/pg_prng.h"
 #include "storage/block.h"		/* for typedef BlockNumber */
 
 
 /* Random generator for sampling code */
-typedef unsigned short SamplerRandomState[3];
-
 extern void sampler_random_init_state(long seed,
-									  SamplerRandomState randstate);
-extern double sampler_random_fract(SamplerRandomState randstate);
+									  pg_prng_state *randstate);
+extern double sampler_random_fract(pg_prng_state *randstate);
 
 /* Block sampling methods */
 
@@ -32,7 +31,7 @@ typedef struct
 	int			n;				/* desired sample size */
 	BlockNumber t;				/* current block number */
 	int			m;				/* blocks selected so far */
-	SamplerRandomState randstate;	/* random generator state */
+	pg_prng_state randstate;	/* random generator state */
 } BlockSamplerData;
 
 typedef BlockSamplerData *BlockSampler;
@@ -46,8 +45,9 @@ extern BlockNumber BlockSampler_Next(BlockSampler bs);
 
 typedef struct
 {
-	double		W;
-	SamplerRandomState randstate;	/* random generator state */
+	double			W;
+	pg_prng_state	randstate;	/* random generator state */
+	bool			randstate_initialized;
 } ReservoirStateData;
 
 typedef ReservoirStateData *ReservoirState;
diff --git a/src/port/Makefile b/src/port/Makefile
index 52dbf5783f..b3754d8940 100644
--- a/src/port/Makefile
+++ b/src/port/Makefile
@@ -42,7 +42,6 @@ OBJS = \
 	$(PG_CRC32C_OBJS) \
 	bsearch_arg.o \
 	chklocale.o \
-	erand48.o \
 	inet_net_ntop.o \
 	noblock.o \
 	path.o \
diff --git a/src/port/erand48.c b/src/port/erand48.c
deleted file mode 100644
index a82ea94220..0000000000
--- a/src/port/erand48.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/port/random.c b/src/port/random.c
deleted file mode 100644
index 2dd59a0829..0000000000
--- a/src/port/random.c
+++ /dev/null
@@ -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();
-}
diff --git a/src/port/srandom.c b/src/port/srandom.c
deleted file mode 100644
index cf1007b2ef..0000000000
--- a/src/port/srandom.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/test/modules/test_integerset/test_integerset.c b/src/test/modules/test_integerset/test_integerset.c
index 21c6f49b37..8935a81284 100644
--- a/src/test/modules/test_integerset/test_integerset.c
+++ b/src/test/modules/test_integerset/test_integerset.c
@@ -12,6 +12,7 @@
  */
 #include "postgres.h"
 
+#include "common/pg_prng.h"
 #include "fmgr.h"
 #include "lib/integerset.h"
 #include "miscadmin.h"
@@ -99,12 +100,16 @@ static void check_with_filler(IntegerSet *intset, uint64 x, uint64 value, uint64
 static void test_single_value_and_filler(uint64 value, uint64 filler_min, uint64 filler_max);
 static void test_huge_distances(void);
 
+static pg_prng_state	pr_state;
+
 /*
  * SQL-callable entry point to perform all tests.
  */
 Datum
 test_integerset(PG_FUNCTION_ARGS)
 {
+	pg_prng_strong_seed(&pr_state);
+
 	/* Tests for various corner cases */
 	test_empty();
 	test_huge_distances();
@@ -248,7 +253,7 @@ test_pattern(const test_spec *spec)
 		 * only a small part of the integer space is used.  We would very
 		 * rarely hit values that are actually in the set.
 		 */
-		x = (pg_lrand48() << 31) | pg_lrand48();
+		x = pg_prng_u64(&pr_state);
 		x = x % (last_int + 1000);
 
 		/* Do we expect this value to be present in the set? */
@@ -571,7 +576,7 @@ test_huge_distances(void)
 	 */
 	while (num_values < 1000)
 	{
-		val += pg_lrand48();
+		val += pg_prng_i32(&pr_state);
 		values[num_values++] = val;
 	}
 
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 233ddbf4c2..29d0f467cb 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -103,9 +103,9 @@ sub mkvcbuild
 	$solution = CreateSolution($vsVersion, $config);
 
 	our @pgportfiles = qw(
-	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c random.c
-	  srandom.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
+	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c
+	  getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
+	  snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
 	  dirent.c dlopen.c getopt.c getopt_long.c link.c
 	  pread.c preadv.c pwrite.c pwritev.c pg_bitutils.c
 	  pg_strong_random.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c
@@ -131,9 +131,9 @@ sub mkvcbuild
 	  config_info.c controldata_utils.c d2s.c encnames.c exec.c
 	  f2s.c file_perm.c file_utils.c hashfn.c hex.c ip.c jsonapi.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
-	  saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c username.c
-	  wait_error.c wchar.c);
+	  pg_get_line.c pg_lzcompress.c pg_prng.c pgfnames.c psprintf.c relpath.c
+	  rmtree.c saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c
+	  username.c wait_error.c wchar.c);
 
 	if ($solution->{options}->{openssl})
 	{
#29Dean Rasheed
dean.a.rasheed@gmail.com
In reply to: Fabien COELHO (#28)
Re: rand48 replacement

On Sun, 4 Jul 2021 at 17:03, Fabien COELHO <coelho@cri.ensmp.fr> wrote:

As for determinism, the end result is still fully deterministic.

The result is indeed deterministic of you call the function with the same
range. However, if you change the range value in one place then sometimes
the state can advance differently, so the determinism is lost, meaning
that it depends on actual range values.

Ah yes, that's true. I can trivially reproduce that in other languages
too. For example, in python, if I call random.seed(0) and then
random.randrange() with the inputs 10,10,10 then the results are
6,6,0. But if the randrange() inputs are 10,1000,10 then the results
are 6,776,6. So the result from the 3rd call changes as a result of
changing the 2nd input. That's not entirely surprising. The important
property of determinism is that if I set a seed, and then make an
identical set of calls to the random API, the results will be
identical every time, so that it's possible to write tests with
predictable/repeatable results.

I would work around that by
deriving another 128 bit generator instead of splitmix 64 bit, but that is
overkill.

Not really relevant now, but I'm pretty sure that's impossible to do.
You might try it as an interesting academic exercise, but I believe
it's a logical impossibility.

Regards,
Dean

#30Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Dean Rasheed (#29)
Re: rand48 replacement

The important property of determinism is that if I set a seed, and then
make an identical set of calls to the random API, the results will be
identical every time, so that it's possible to write tests with
predictable/repeatable results.

Hmmm… I like my stronger determinism definition more than this one:-)

I would work around that by deriving another 128 bit generator instead
of splitmix 64 bit, but that is overkill.

Not really relevant now, but I'm pretty sure that's impossible to do.
You might try it as an interesting academic exercise, but I believe
it's a logical impossibility.

Hmmm… I was simply thinking of seeding a new pg_prng_state from the main
pg_prng_state with some transformation, and then iterate over the second
PRNG, pretty much like I did with splitmix, but with 128 bits so that your
#states argument does not apply, i.e. something like:

/* select in a range with bitmask rejection */
uint64 pg_prng_u64_range(pg_prng_state *state, uint64 range)
{
/* always advance state once */
uint64 next = xoroshiro128ss(state);
uint64 val;

if (range >= 2)
{
uint64 mask = mask_u64(range-1);

val = next & mask;

if (val >= range)
{
/* copy and update current prng state */
pg_prng_state iterator = *state;

iterator.s0 ^= next;
iterator.s1 += UINT64CONST(0x9E3779B97f4A7C15);

/* iterate till val in [0, range) */
while ((val = xoroshiro128ss(&iterator) & mask) >= range)
;
}
}
else
val = 0;

return val;
}

The initial pseudo-random sequence is left to proceed, and a new PRNG is
basically forked for iterating on the mask, if needed.

--
Fabien.

#31Yura Sokolov
y.sokolov@postgrespro.ru
In reply to: Fabien COELHO (#30)
Re: rand48 replacement

Fabien COELHO писал 2021-07-04 23:29:

The important property of determinism is that if I set a seed, and
then make an identical set of calls to the random API, the results
will be identical every time, so that it's possible to write tests
with predictable/repeatable results.

Hmmm… I like my stronger determinism definition more than this one:-)

I would work around that by deriving another 128 bit generator
instead of splitmix 64 bit, but that is overkill.

Not really relevant now, but I'm pretty sure that's impossible to do.
You might try it as an interesting academic exercise, but I believe
it's a logical impossibility.

Hmmm… I was simply thinking of seeding a new pg_prng_state from the
main pg_prng_state with some transformation, and then iterate over the
second PRNG, pretty much like I did with splitmix, but with 128 bits
so that your #states argument does not apply, i.e. something like:

/* select in a range with bitmask rejection */
uint64 pg_prng_u64_range(pg_prng_state *state, uint64 range)
{
/* always advance state once */
uint64 next = xoroshiro128ss(state);
uint64 val;

if (range >= 2)
{
uint64 mask = mask_u64(range-1);

val = next & mask;

if (val >= range)
{
/* copy and update current prng state */
pg_prng_state iterator = *state;

iterator.s0 ^= next;
iterator.s1 += UINT64CONST(0x9E3779B97f4A7C15);

/* iterate till val in [0, range) */
while ((val = xoroshiro128ss(&iterator) & mask) >= range)
;
}
}
else
val = 0;

return val;
}

The initial pseudo-random sequence is left to proceed, and a new PRNG
is basically forked for iterating on the mask, if needed.

I believe most "range" values are small, much smaller than UINT32_MAX.
In this case, according to [1]https://www.pcg-random.org/posts/bounded-rands.html fastest method is Lemire's one (I'd take
original version from [2]https://lemire.me/blog/2016/06/30/fast-random-shuffling/)

Therefore combined method pg_prng_u64_range could branch on range value

uint64 pg_prng_u64_range(pg_prng_state *state, uint64 range)
{
uint64 val = xoroshiro128ss(state);
uint64 m;
if ((range & (range-1) == 0) /* handle all power 2 cases */
return range != 0 ? val & (range-1) : 0;
if (likely(range < PG_UINT32_MAX/32)
{
/*
* Daniel Lamire's unbiased range random algorithm based on
rejection sampling
* https://lemire.me/blog/2016/06/30/fast-random-shuffling/
*/
m = (uint32)val * range;
if ((uint32)m < range)
{
uint32 t = -range % range;
while ((uint32)m < t)
m = (uint32)xoroshiro128ss(state) * range;
}
return m >> 32;
}
/* Apple's mask method */
m = mask_u64(range-1);
val &= m;
while (val >= range)
val = xoroshiro128ss(state) & m;
return val;
}

Mask method could also be faster when range is close to mask.
For example, fast check for "range is within 1/1024 to mask" is
range < (range/512 + (range&(range*2)))

And then method choose could like:
if (likely(range < UINT32_MAX/32 && range > (range/512 +
(range&(range*2)))))

But I don't know does additional condition worth difference or not.

[1]: https://www.pcg-random.org/posts/bounded-rands.html
[2]: https://lemire.me/blog/2016/06/30/fast-random-shuffling/

regards,
Sokolov Yura

#32Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Yura Sokolov (#31)
Re: rand48 replacement

Hello Yura,

I believe most "range" values are small, much smaller than UINT32_MAX.
In this case, according to [1] fastest method is Lemire's one (I'd take
original version from [2]) [...]

Yep.

I share your point that the range is more often 32 bits.

However, I'm not enthousiastic at combining two methods depending on the
range, the function looks complex enough without that, so I would suggest
not to take this option. Also, the decision process adds to the average
cost, which is undesirable. I would certainly select the unbias multiply
method if we want a u32 range variant.

--
Fabien.

#33Yura Sokolov
y.sokolov@postgrespro.ru
In reply to: Fabien COELHO (#32)
Re: rand48 replacement

Fabien COELHO писал 2021-07-06 09:13:

Hello Yura,

I believe most "range" values are small, much smaller than UINT32_MAX.
In this case, according to [1] fastest method is Lemire's one (I'd
take
original version from [2]) [...]

Yep.

I share your point that the range is more often 32 bits.

However, I'm not enthousiastic at combining two methods depending on
the range, the function looks complex enough without that, so I would
suggest not to take this option. Also, the decision process adds to
the average cost, which is undesirable.

Given 99.99% cases will be in the likely case, branch predictor should
eliminate decision cost.

And as Dean Rasheed correctly mentioned, mask method will
have really bad pattern for branch predictor if range is not just below
or equal to power of two.
For example, rand_range(10000) will have 60% probability to pass through
`while (val > range)` and 40% probability to go to next loop iteration.
rand_range(100000) will have 76%/24% probabilities. Branch predictor
doesn't like it. Even rand_range(1000000), which is quite close to 2^20,
will have 95%/5%, and still not enough to please BP.

But with unbias multiply method it will be 0.0002%/99.9998% for 10000,
0,002%/99.998% for 100000 and 0.02%/99.98% for 1000000 - much-much
better.
Branch predictor will make it almost free (i hope).

And __builtin_clzl is not free lunch either, it has latency 3-4 cycles
on modern processor. Well, probably it could run in parallel with some
part of xoroshiro, but it depends on how compiler will optimize this
function.

I would certainly select the unbias multiply method if we want a u32
range variant.

There could be two functions.

regards,
Sokolov Yura.

#34Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Yura Sokolov (#33)
Re: rand48 replacement

Hello Yura,

However, I'm not enthousiastic at combining two methods depending on
the range, the function looks complex enough without that, so I would
suggest not to take this option. Also, the decision process adds to
the average cost, which is undesirable.

Given 99.99% cases will be in the likely case, branch predictor should
eliminate decision cost.

Hmmm. ISTM that a branch predictor should predict that unknown < small
should probably be false, so a hint should be given that it is really
true.

And as Dean Rasheed correctly mentioned, mask method will have really
bad pattern for branch predictor if range is not just below or equal to
power of two.

On average the bitmask is the better unbiased method, if the online
figures are to be trusted. Also, as already said, I do not really want to
add code complexity, especially to get lower average performance, and
especially with code like "threshold = - range % range", where both
variables are unsigned, I have a headache just looking at it:-)

And __builtin_clzl is not free lunch either, it has latency 3-4 cycles
on modern processor.

Well, % is not cheap either.

Well, probably it could run in parallel with some part of xoroshiro, but
it depends on how compiler will optimize this function.

I would certainly select the unbias multiply method if we want a u32
range variant.

There could be two functions.

Yep, but do we need them? Who is likely to want 32 bits pseudo random
ints in a range? pgbench needs 64 bits.

So I'm still inclined to just keep the faster-on-average bitmask method,
despite that it may be slower for some ranges. The average cost for the
worst case in PRNG calls is, if I'm not mistaken:

1 * 0.5 + 2 * 0.25 + 3 * 0.125 + ... ~ 2

which does not look too bad.

--
Fabien.

#35Yura Sokolov
y.sokolov@postgrespro.ru
In reply to: Fabien COELHO (#34)
Re: rand48 replacement

Fabien COELHO писал 2021-07-06 23:49:

Hello Yura,

However, I'm not enthousiastic at combining two methods depending on
the range, the function looks complex enough without that, so I would
suggest not to take this option. Also, the decision process adds to
the average cost, which is undesirable.

Given 99.99% cases will be in the likely case, branch predictor should
eliminate decision cost.

Hmmm. ISTM that a branch predictor should predict that unknown < small
should probably be false, so a hint should be given that it is really
true.

Why? Branch predictor is history based: if it were usually true here
then it will be true this time either.
unknown < small is usually true therefore branch predictor will assume
it is true.

I put `likely` for compiler: compiler then puts `likely` path closer.

And as Dean Rasheed correctly mentioned, mask method will have really
bad pattern for branch predictor if range is not just below or equal
to power of two.

On average the bitmask is the better unbiased method, if the online
figures are to be trusted. Also, as already said, I do not really want
to add code complexity, especially to get lower average performance,
and especially with code like "threshold = - range % range", where
both variables are unsigned, I have a headache just looking at it:-)

If you mention https://www.pcg-random.org/posts/bounded-rands.html then
1. first graphs are made with not exact Lemire's code.
Last code from
https://lemire.me/blog/2016/06/30/fast-random-shuffling/
(which I derived from) performs modulo operation only if `(leftover <
range)`.
Even with `rand_range(1000000)` it is just once in four thousands
runs.
2. You can see "Small-Constant Benchmark" at that page, Debiased Int is
1.5 times faster. And even in "Small-Shuffle" benchmark their
unoptimized
version is on-par with mask method.
3. If you go to "Making Improvements/Faster Threshold-Based Discarding"
section, then you'll see code my version is matched with. It is twice
faster than masked method in Small-Shuffle benchmark, and just a bit
slower in Large-Shuffle.

And __builtin_clzl is not free lunch either, it has latency 3-4 cycles
on modern processor.

Well, % is not cheap either.

Well, probably it could run in parallel with some part of xoroshiro,
but it depends on how compiler will optimize this function.

I would certainly select the unbias multiply method if we want a u32
range variant.

There could be two functions.

Yep, but do we need them? Who is likely to want 32 bits pseudo random
ints in a range? pgbench needs 64 bits.

So I'm still inclined to just keep the faster-on-average bitmask
method, despite that it may be slower for some ranges. The average
cost for the worst case in PRNG calls is, if I'm not mistaken:

1 * 0.5 + 2 * 0.25 + 3 * 0.125 + ... ~ 2

which does not look too bad.

You doesn't count cost of branch-misprediction.
https://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-processing-an-unsorted-array
https://lemire.me/blog/2019/10/15/mispredicted-branches-can-multiply-your-running-times/
Therefore calculation should be at least:

1 * 0.5 + 0.5 * (3 + 0.5 * (3 + ...)) = 6

By the way, we have 64bit random. If we use 44bit from it for range <=
(1<<20), then
bias will be less than 1/(2**24). Could we just ignore it (given it is
not crypto
strong random)?

uint64 pg_prng_u64_range(pg_prng_state *state, uint64 range)
{
uint64 val = xoroshiro128ss(state);
uint64 m;
if ((range & (range-1) == 0) /* handle all power 2 cases */
return range != 0 ? val & (range-1) : 0;
if (likely(range < (1<<20)))
/*
* While multiply method is biased, bias will be smaller than
1/(1<<24) for
* such small ranges. Lets ignore it.
*/
return ((val >> 20) * range) >> 44;
/* Apple's mask method */
m = mask_u64(range-1);
val &= m;
while (val >= range)
val = xoroshiro128ss(state) & m;
return val;
}

Anyway, excuse me for heating this discussion cause of such
non-essential issue.
I'll try to control myself and don't proceed it further.

regards,
Sokolov Yura.

#36Dean Rasheed
dean.a.rasheed@gmail.com
In reply to: Yura Sokolov (#35)
Re: rand48 replacement

On Wed, 7 Jul 2021 at 04:00, Yura Sokolov <y.sokolov@postgrespro.ru> wrote:

Anyway, excuse me for heating this discussion cause of such
non-essential issue.
I'll try to control myself and don't proceed it further.

Whilst it has been interesting learning and discussing all these
different techniques, I think it's probably best to stick with the
bitmask method, rather than making the code too complex and difficult
to follow. The bitmask method has the advantage of being very simple,
easy to understand and fast (fastest in many of the benchmarks, and
close enough in others to make me think that the difference won't
matter for our purposes).

To test the current patch, I hacked up a simple SQL-callable server
function: random(bigint, bigint) returns bigint, similar to the one in
pgbench. After doing so, I couldn't help thinking that it would be
useful to have such a function in core, so maybe that could be a
follow-on patch. Anyway, that led to the following observations:

Firstly, there's a bug in the existing mask_u64() code -- if
pg_leftmost_one_pos64(u) returns 63, you end up with a mask equal to
0, and it breaks down.

Secondly, I think it would be simpler to implement this as a bitshift,
rather than a bitmask, using the high bits from the random number.
That might not make much difference for xoroshiro**, but in general,
PRNGs tend to be weaker in the lower bits, so it seems preferable on
that basis. But also, it makes the code simpler and less error-prone.

Finally, I think it would be better to treat the upper bound of the
range as inclusive. Doing so makes the function able to cover all
possible 64-bit ranges. It would then be easy (perhaps in another
follow-on patch) to make the pgbench random() function work for all
64-bit bounds (as long as max >= min), without the weird overflow
checking it currently has.

Putting those 3 things together, the code (minus comments) becomes:

if (range > 0)
{
int rshift = 63 - pg_leftmost_one_pos64(range);

do
{
val = xoroshiro128ss(state) >> rshift;
}
while (val > range);
}
else
val = 0;

which reduces the complexity a bit.

Regards,
Dean

#37Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Dean Rasheed (#36)
1 attachment(s)
Re: rand48 replacement

Hello Dean,

Whilst it has been interesting learning and discussing all these
different techniques, I think it's probably best to stick with the
bitmask method, rather than making the code too complex and difficult
to follow.

Yes.

The bitmask method has the advantage of being very simple, easy to
understand and fast (fastest in many of the benchmarks, and close enough
in others to make me think that the difference won't matter for our
purposes).

To test the current patch, I hacked up a simple SQL-callable server
function: random(bigint, bigint) returns bigint, similar to the one in
pgbench. After doing so, I couldn't help thinking that it would be
useful to have such a function in core, so maybe that could be a
follow-on patch.

Yep.

Anyway, that led to the following observations:

Firstly, there's a bug in the existing mask_u64() code -- if
pg_leftmost_one_pos64(u) returns 63, you end up with a mask equal to
0, and it breaks down.

Oops:-(

Secondly, I think it would be simpler to implement this as a bitshift,
rather than a bitmask, using the high bits from the random number. That
might not make much difference for xoroshiro**, but in general, PRNGs
tend to be weaker in the lower bits, so it seems preferable on that
basis. But also, it makes the code simpler and less error-prone.

Indeed, that looks like a good option.

Finally, I think it would be better to treat the upper bound of the
range as inclusive.

This bothered me as well, but the usual approach seems to use range as the
number of values, so I was hesitant to depart from that. I'm still
hesitant to go that way.

Doing so makes the function able to cover all
possible 64-bit ranges. It would then be easy (perhaps in another
follow-on patch) to make the pgbench random() function work for all
64-bit bounds (as long as max >= min), without the weird overflow
checking it currently has.

Putting those 3 things together, the code (minus comments) becomes:

if (range > 0)
{
int rshift = 63 - pg_leftmost_one_pos64(range);

do
{
val = xoroshiro128ss(state) >> rshift;
}
while (val > range);
}
else
val = 0;

which reduces the complexity a bit.

Indeed.

Attached v9 follows this approach but for the range being inclusive, as
most sources I found understand the range as the number of values.

--
Fabien.

Attachments:

prng-9.patchtext/x-diff; name=prng-9.patchDownload
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 2c2f149fb0..146b524076 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -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]);
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index f15c97ad7a..3f9e954bd2 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -5146,7 +5146,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]);
 		}
diff --git a/contrib/tsm_system_rows/tsm_system_rows.c b/contrib/tsm_system_rows/tsm_system_rows.c
index 4996612902..1a46d4b143 100644
--- a/contrib/tsm_system_rows/tsm_system_rows.c
+++ b/contrib/tsm_system_rows/tsm_system_rows.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/contrib/tsm_system_time/tsm_system_time.c b/contrib/tsm_system_time/tsm_system_time.c
index 788d8f9a68..36acc6c106 100644
--- a/contrib/tsm_system_time/tsm_system_time.c
+++ b/contrib/tsm_system_time/tsm_system_time.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 0c9591415e..1ddbeed2dc 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1308,7 +1308,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 					 * Found a suitable tuple, so save it, replacing one old
 					 * tuple at random
 					 */
-					int			k = (int) (targrows * sampler_random_fract(rstate.randstate));
+					int			k = (int) (targrows * sampler_random_fract(&rstate.randstate));
 
 					Assert(k >= 0 && k < targrows);
 					heap_freetuple(rows[k]);
diff --git a/src/backend/optimizer/geqo/geqo_random.c b/src/backend/optimizer/geqo/geqo_random.c
index f21bc047e6..8b42a9ffba 100644
--- a/src/backend/optimizer/geqo/geqo_random.c
+++ b/src/backend/optimizer/geqo/geqo_random.c
@@ -21,14 +21,7 @@ geqo_set_seed(PlannerInfo *root, double seed)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	/*
-	 * XXX. This seeding algorithm could certainly be improved - but it is not
-	 * critical to do so.
-	 */
-	memset(private->random_state, 0, sizeof(private->random_state));
-	memcpy(private->random_state,
-		   &seed,
-		   Min(sizeof(private->random_state), sizeof(seed)));
+	pg_prng_fseed(&private->random_state, seed);
 }
 
 double
@@ -36,5 +29,5 @@ geqo_rand(PlannerInfo *root)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	return pg_erand48(private->random_state);
+	return pg_prng_f64(&private->random_state);
 }
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 098bbb372b..9fc0b108dd 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -21,6 +21,7 @@
 
 #include "catalog/pg_type.h"
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/shortest_dec.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
@@ -65,7 +66,7 @@ float8		degree_c_one = 1.0;
 
 /* State for drandom() and setseed() */
 static bool drandom_seed_set = false;
-static unsigned short drandom_seed[3] = {0, 0, 0};
+static pg_prng_state drandom_seed = PG_PRNG_DEFAULT_STATE;
 
 /* Local function prototypes */
 static double sind_q1(double x);
@@ -2762,22 +2763,20 @@ drandom(PG_FUNCTION_ARGS)
 		 * Should that fail for some reason, we fall back on a lower-quality
 		 * seed based on current time and PID.
 		 */
-		if (!pg_strong_random(drandom_seed, sizeof(drandom_seed)))
+		if (!pg_strong_random(&drandom_seed, sizeof(drandom_seed)))
 		{
 			TimestampTz now = GetCurrentTimestamp();
 			uint64		iseed;
 
 			/* Mix the PID with the most predictable bits of the timestamp */
 			iseed = (uint64) now ^ ((uint64) MyProcPid << 32);
-			drandom_seed[0] = (unsigned short) iseed;
-			drandom_seed[1] = (unsigned short) (iseed >> 16);
-			drandom_seed[2] = (unsigned short) (iseed >> 32);
+			pg_prng_seed(&drandom_seed, iseed);
 		}
 		drandom_seed_set = true;
 	}
 
-	/* pg_erand48 produces desired result range [0.0 - 1.0) */
-	result = pg_erand48(drandom_seed);
+	/* produces desired result range [0.0 - 1.0) */
+	result = pg_prng_f64(&drandom_seed);
 
 	PG_RETURN_FLOAT8(result);
 }
@@ -2790,7 +2789,6 @@ Datum
 setseed(PG_FUNCTION_ARGS)
 {
 	float8		seed = PG_GETARG_FLOAT8(0);
-	uint64		iseed;
 
 	if (seed < -1 || seed > 1 || isnan(seed))
 		ereport(ERROR,
@@ -2798,11 +2796,7 @@ setseed(PG_FUNCTION_ARGS)
 				 errmsg("setseed parameter %g is out of allowed range [-1,1]",
 						seed)));
 
-	/* Use sign bit + 47 fractional bits to fill drandom_seed[] */
-	iseed = (int64) (seed * (float8) UINT64CONST(0x7FFFFFFFFFFF));
-	drandom_seed[0] = (unsigned short) iseed;
-	drandom_seed[1] = (unsigned short) (iseed >> 16);
-	drandom_seed[2] = (unsigned short) (iseed >> 32);
+	pg_prng_fseed(&drandom_seed, seed);
 	drandom_seed_set = true;
 
 	PG_RETURN_VOID();
diff --git a/src/backend/utils/misc/sampling.c b/src/backend/utils/misc/sampling.c
index 0c327e823f..7b59128878 100644
--- a/src/backend/utils/misc/sampling.c
+++ b/src/backend/utils/misc/sampling.c
@@ -49,7 +49,7 @@ BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize,
 	bs->t = 0;					/* blocks scanned so far */
 	bs->m = 0;					/* blocks selected so far */
 
-	sampler_random_init_state(randseed, bs->randstate);
+	sampler_random_init_state(randseed, &bs->randstate);
 
 	return Min(bs->n, bs->N);
 }
@@ -98,7 +98,7 @@ BlockSampler_Next(BlockSampler bs)
 	 * less than k, which means that we cannot fail to select enough blocks.
 	 *----------
 	 */
-	V = sampler_random_fract(bs->randstate);
+	V = sampler_random_fract(&bs->randstate);
 	p = 1.0 - (double) k / (double) K;
 	while (V < p)
 	{
@@ -136,10 +136,10 @@ reservoir_init_selection_state(ReservoirState rs, int n)
 	 * Reservoir sampling is not used anywhere where it would need to return
 	 * repeatable results so we can initialize it randomly.
 	 */
-	sampler_random_init_state(random(), rs->randstate);
+	sampler_random_init_state(random(), &rs->randstate);
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	rs->W = exp(-log(sampler_random_fract(rs->randstate)) / n);
+	rs->W = exp(-log(sampler_random_fract(&rs->randstate)) / n);
 }
 
 double
@@ -154,7 +154,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 		double		V,
 					quot;
 
-		V = sampler_random_fract(rs->randstate);	/* Generate V */
+		V = sampler_random_fract(&rs->randstate);	/* Generate V */
 		S = 0;
 		t += 1;
 		/* Note: "num" in Vitter's code is always equal to t - n */
@@ -186,7 +186,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 						tmp;
 
 			/* Generate U and X */
-			U = sampler_random_fract(rs->randstate);
+			U = sampler_random_fract(&rs->randstate);
 			X = t * (W - 1.0);
 			S = floor(X);		/* S is tentatively set to floor(X) */
 			/* Test if U <= h(S)/cg(X) in the manner of (6.3) */
@@ -215,7 +215,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 				y *= numer / denom;
 				denom -= 1;
 			}
-			W = exp(-log(sampler_random_fract(rs->randstate)) / n); /* Generate W in advance */
+			W = exp(-log(sampler_random_fract(&rs->randstate)) / n); /* Generate W in advance */
 			if (exp(log(y) / n) <= (t + X) / t)
 				break;
 		}
@@ -230,24 +230,22 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
  *----------
  */
 void
-sampler_random_init_state(long seed, SamplerRandomState randstate)
+sampler_random_init_state(long seed, pg_prng_state *randstate)
 {
-	randstate[0] = 0x330e;		/* same as pg_erand48, but could be anything */
-	randstate[1] = (unsigned short) seed;
-	randstate[2] = (unsigned short) (seed >> 16);
+	pg_prng_seed(randstate, (uint64_t) seed);
 }
 
 /* Select a random value R uniformly distributed in (0 - 1) */
 double
-sampler_random_fract(SamplerRandomState randstate)
+sampler_random_fract(pg_prng_state *randstate)
 {
 	double		res;
 
-	/* pg_erand48 returns a value in [0.0 - 1.0), so we must reject 0 */
+	/* pg_prng_f64 returned value in [0.0 - 1.0), so we must reject 0.0 */
 	do
 	{
-		res = pg_erand48(randstate);
-	} while (res == 0.0);
+		res = pg_prng_f64(randstate);
+	} while (unlikely(res == 0.0));
 	return res;
 }
 
@@ -266,22 +264,28 @@ double
 anl_random_fract(void)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(random(), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* and compute a random fraction */
-	return sampler_random_fract(oldrs.randstate);
+	return sampler_random_fract(&oldrs.randstate);
 }
 
 double
 anl_init_selection_state(int n)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(random(), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	return exp(-log(sampler_random_fract(oldrs.randstate)) / n);
+	return exp(-log(sampler_random_fract(&oldrs.randstate)) / n);
 }
 
 double
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 4aeccd93af..6d749bbbb6 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -58,6 +58,7 @@
 #endif
 
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/logging.h"
 #include "common/string.h"
 #include "common/username.h"
@@ -343,16 +344,8 @@ typedef struct StatsData
 	SimpleStats lag;
 } StatsData;
 
-/*
- * Struct to keep random state.
- */
-typedef struct RandomState
-{
-	unsigned short xseed[3];
-} RandomState;
-
 /* 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 */
 static THREAD_BARRIER_T barrier;
@@ -454,7 +447,7 @@ typedef struct
 	 * Separate randomness for each client. This is used for random functions
 	 * 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			command;		/* command number in script */
@@ -491,9 +484,9 @@ typedef struct
 	 * random state to make all of them independent of each other and
 	 * therefore deterministic at the thread level.
 	 */
-	RandomState ts_choose_rs;	/* random state for selecting a script */
-	RandomState ts_throttle_rs; /* random state for transaction throttling */
-	RandomState ts_sample_rs;	/* random state for log sampling */
+	pg_prng_state	ts_choose_rs;	/* random state for selecting a script */
+	pg_prng_state	ts_throttle_rs; /* random state for transaction throttling */
+	pg_prng_state	ts_sample_rs;	/* random state for log sampling */
 
 	int64		throttle_trigger;	/* previous/next throttling (us) */
 	FILE	   *logfile;		/* where to log, or NULL */
@@ -891,42 +884,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.
  */
 static void
-initRandomState(RandomState *random_state)
+initRandomState(pg_prng_state *state)
 {
-	random_state->xseed[0] = (unsigned short)
-		(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);
+	pg_prng_seed(state, pg_prng_u64(&base_random_sequence));
 }
 
+
 /*
- * Random number generator: uniform distribution from min to max inclusive.
+ * PRNG: uniform distribution from min to max inclusive.
  *
  * Although the limits are expressed as int64, you can't generate the full
  * int64 range in one call, because the difference of the limits mustn't
- * overflow int64.  In practice it's unwise to ask for more than an int32
- * range, because of the limited precision of pg_erand48().
+ * overflow int64.
  */
 static int64
-getrand(RandomState *random_state, int64 min, int64 max)
+getrand(pg_prng_state *state, int64 min, int64 max)
 {
-	/*
-	 * 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));
+	return min + (int64) pg_prng_u64_range(state, max - min + 1);
 }
 
 /*
@@ -935,8 +914,7 @@ getrand(RandomState *random_state, int64 min, int64 max)
  * value is exp(-parameter).
  */
 static int64
-getExponentialRand(RandomState *random_state, int64 min, int64 max,
-				   double parameter)
+getExponentialRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		cut,
 				uniform,
@@ -946,7 +924,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 	Assert(parameter > 0.0);
 	cut = exp(-parameter);
 	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	uniform = 1.0 - pg_prng_f64(state);
 
 	/*
 	 * inner expression in (cut, 1] (if parameter > 0), rand in [0, 1)
@@ -959,8 +937,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 
 /* random number generator: gaussian distribution from min to max inclusive */
 static int64
-getGaussianRand(RandomState *random_state, int64 min, int64 max,
-				double parameter)
+getGaussianRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		stdev;
 	double		rand;
@@ -983,13 +960,13 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
 	do
 	{
 		/*
-		 * pg_erand48 generates [0,1), but for the basic version of the
+		 * pg_prng_f64 generates [0,1), but for the basic version of the
 		 * Box-Muller transform the two uniformly distributed random numbers
 		 * are expected in (0, 1] (see
 		 * https://en.wikipedia.org/wiki/Box-Muller_transform)
 		 */
-		double		rand1 = 1.0 - pg_erand48(random_state->xseed);
-		double		rand2 = 1.0 - pg_erand48(random_state->xseed);
+		double		rand1 = 1.0 - pg_prng_f64(state);
+		double		rand2 = 1.0 - pg_prng_f64(state);
 
 		/* Box-Muller basic form transform */
 		double		var_sqrt = sqrt(-2.0 * log(rand1));
@@ -1019,7 +996,7 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
  * not be one.
  */
 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
@@ -1027,8 +1004,8 @@ getPoissonRand(RandomState *random_state, double center)
 	 */
 	double		uniform;
 
-	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	/* pseudo-random value in [0, 1), uniform in (0, 1] */
+	uniform = 1.0 - pg_prng_f64(state);
 
 	return (int64) (-log(uniform) * center + 0.5);
 }
@@ -1041,7 +1018,7 @@ getPoissonRand(RandomState *random_state, double center)
  * This works for s > 1.0, but may perform badly for s very close to 1.0.
  */
 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		x,
@@ -1056,8 +1033,8 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 	while (true)
 	{
 		/* random variates */
-		u = pg_erand48(random_state->xseed);
-		v = pg_erand48(random_state->xseed);
+		u = pg_prng_f64(state);
+		v = pg_prng_f64(state);
 
 		x = floor(pow(u, -1.0 / (s - 1.0)));
 
@@ -1071,14 +1048,14 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 
 /* random number generator: zipfian distribution from min to max inclusive */
 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;
 
 	/* abort if parameter is invalid */
 	Assert(MIN_ZIPFIAN_PARAM <= s && s <= MAX_ZIPFIAN_PARAM);
 
-	return min - 1 + computeIterativeZipfian(random_state, n, s);
+	return min - 1 + computeIterativeZipfian(state, n, s);
 }
 
 /*
@@ -1135,7 +1112,7 @@ getHashMurmur2(int64 val, uint64 seed)
  * For small sizes, this generates each of the (size!) possible permutations
  * of integers in the range [0, size) with roughly equal probability.  Once
  * the size is larger than 20, the number of possible permutations exceeds the
- * number of distinct states of the internal pseudorandom number generators,
+ * number of distinct states of the internal pseudorandom number generator,
  * and so not all possible permutations can be generated, but the permutations
  * chosen should continue to give the appearance of being random.
  *
@@ -1145,25 +1122,19 @@ getHashMurmur2(int64 val, uint64 seed)
 static int64
 permute(const int64 val, const int64 isize, const int64 seed)
 {
-	RandomState random_state1;
-	RandomState random_state2;
-	uint64		size;
-	uint64		v;
-	int			masklen;
-	uint64		mask;
-	int			i;
+	/* using a high-end PRNG is probably overkill */
+	pg_prng_state	state;
+	uint64			size;
+	uint64			v;
+	int				masklen;
+	uint64			mask;
+	int				i;
 
 	if (isize < 2)
 		return 0;				/* nothing to permute */
 
-	/* Initialize a pair of random states using the seed */
-	random_state1.xseed[0] = seed & 0xFFFF;
-	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;
+	/* Initialize prng state using the seed */
+	pg_prng_seed(&state, (uint64) seed);
 
 	/* Computations are performed on unsigned values */
 	size = (uint64) isize;
@@ -1209,8 +1180,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 					t;
 
 		/* Random multiply (by an odd number), XOR and rotate of lower half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		if (v <= mask)
 		{
 			v = ((v * m) ^ r) & mask;
@@ -1218,8 +1189,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random multiply (by an odd number), XOR and rotate of upper half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		t = size - 1 - v;
 		if (t <= mask)
 		{
@@ -1229,7 +1200,7 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random offset */
-		r = (uint64) getrand(&random_state2, 0, size - 1);
+		r = pg_prng_u64(&state) % (size - 1);
 		v = (v + r) % size;
 	}
 
@@ -3790,7 +3761,7 @@ doLog(TState *thread, CState *st,
 	 * to the random sample.
 	 */
 	if (sample_rate != 0.0 &&
-		pg_erand48(thread->ts_sample_rs.xseed) > sample_rate)
+		pg_prng_f64(&thread->ts_sample_rs) > sample_rate)
 		return;
 
 	/* should we aggregate the results or not? */
@@ -5714,12 +5685,11 @@ set_random_seed(const char *seed)
 
 	if (seed != NULL)
 		pg_log_info("setting random seed to %llu", (unsigned long long) iseed);
+
 	random_seed = iseed;
 
 	/* Fill base_random_sequence with low-order bits of seed */
-	base_random_sequence.xseed[0] = iseed & 0xFFFF;
-	base_random_sequence.xseed[1] = (iseed >> 16) & 0xFFFF;
-	base_random_sequence.xseed[2] = (iseed >> 32) & 0xFFFF;
+	pg_prng_seed(&base_random_sequence, (uint64_t) iseed);
 
 	return true;
 }
@@ -6405,9 +6375,7 @@ main(int argc, char **argv)
 	/* set default seed for hash functions */
 	if (lookupVariable(&state[0], "default_seed") == NULL)
 	{
-		uint64		seed =
-		((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) |
-		(((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) << 32);
+		uint64		seed = pg_prng_u64(&base_random_sequence);
 
 		for (i = 0; i < nclients; i++)
 			if (!putVariableInt(&state[i], "startup", "default_seed", (int64) seed))
diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl
index 3aa9d5d753..cd91c9ac3a 100644
--- a/src/bin/pgbench/t/001_pgbench_with_server.pl
+++ b/src/bin/pgbench/t/001_pgbench_with_server.pl
@@ -427,9 +427,9 @@ pgbench(
 
 		# After explicit seeding, the four random checks (1-3,20) are
 		# deterministic
-		qr{command=1.: int 13\b},      # uniform random
-		qr{command=2.: int 116\b},     # exponential random
-		qr{command=3.: int 1498\b},    # gaussian random
+		qr{command=1.: int 17\b},      # uniform random
+		qr{command=2.: int 108\b},     # exponential random
+		qr{command=3.: int 1452\b},    # gaussian random
 		qr{command=4.: int 4\b},
 		qr{command=5.: int 5\b},
 		qr{command=6.: int 6\b},
@@ -496,6 +496,7 @@ pgbench(
 		qr{command=109.: boolean true\b},
 		qr{command=110.: boolean true\b},
 		qr{command=111.: boolean true\b},
+		qr{command=113.: boolean true\b},
 	],
 	'pgbench expressions',
 	{
@@ -639,8 +640,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))
 -- actual values
 \set t debug(permute(:v, 1) = 0)
-\set t debug(permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1 and \
-             permute(0, 2, 5435) = 1 and permute(1, 2, 5435) = 0)
+\set t debug(permute(0, 2, 5431) = 1 and permute(1, 2, 5431) = 0 and \
+             permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1)
+-- 63 bits tests for checking permute portability across architectures
+\set size debug(:max - 10)
+\set t debug(permute(:size-1, :size, 5432) = 4352241160009873020 and \
+             permute(:size-2, :size, 5432) = 4990004119691638710 and \
+             permute(:size-3, :size, 5432) = 5868029867256127549 and \
+             permute(:size-4, :size, 5432) = 1238830324886341378 and \
+             permute(:size-5, :size, 5432) = 7415914367102906897 and \
+             permute(:size-6, :size, 5432) = 3214501037984818995 and \
+             permute(:size-7, :size, 5432) =  600660351261124695)
 }
 	});
 
diff --git a/src/common/Makefile b/src/common/Makefile
index 38a8599337..9aee26f694 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -67,6 +67,7 @@ OBJS_COMMON = \
 	md5_common.o \
 	pg_get_line.o \
 	pg_lzcompress.o \
+	pg_prng.o \
 	pgfnames.o \
 	psprintf.o \
 	relpath.o \
diff --git a/src/common/pg_prng.c b/src/common/pg_prng.c
new file mode 100644
index 0000000000..6f3b1df3b1
--- /dev/null
+++ b/src/common/pg_prng.c
@@ -0,0 +1,236 @@
+/*
+ * Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * Previous version was the 1988 (?) rand48 standard with 48 bits state LCG,
+ * designed for running on 32 bits or even 16 bits architectures, and to produce
+ * 32 bit ints and floats (i.e. *not* doubles).
+ *
+ * The implied 16-bits unpacking/packing may have a detrimental performance impact
+ * on modern 64 bits architectures.
+ *
+ * We (probably) want:
+ * - one reasonable default PRNG for all pg internal uses.
+ * - NOT to invent a new design!
+ * - something fast, close to rand48 (which basically does 2 arithmetic ops)
+ *   no need for something cryptographic though, which would imply slow
+ * - to produce 64 bits integers & doubles with a 52 bits mantissa
+ *   so state size > 64 bits.
+ * - a small state though, because we might generate quite a few of them
+ *   for different purposes so state size <= 256 or even <= 128 bits
+ * - the state to be aligned to whatever, so state size = 128 bits
+ * - 64 bits operations for efficiency on modern architectures,
+ *   but NOT 128 bits operations.
+ * - not to depend on special hardware for speed (eg MMX/SSE/AES).
+ * - not something with obvious known and relevant defects.
+ * - not something with "rights" attached.
+ *
+ * These constraints reduce a lot the available options from
+ * https://en.wikipedia.org/wiki/List_of_random_number_generators
+ *
+ * LCG
+ * - POSIX rand48: m = 2^48, a = 25214903917, c = 11, extract 47...16
+ * - MMIX by Donald Knuth: m = 2^64, a = 6364136223846793005, c = 1442695040888963407
+ * - very bad on low bits, which MUST NOT BE USED
+ *   a 128 bits would do, but no clear standards, and it would imply 128-bit ops,
+ *   see PCG below.
+ *
+ * PCG Family (https://www.pcg-random.org/)
+ * - this is basically an LCG with an improved outpout function
+ *   so as to avoid typical LCG issues and pass stats tests.
+ * - recommands state size to be twice the output size,
+ *   which seems a reasonable requirement.
+ * - this implies 128 bit ops for our target parameters,
+ *   otherwise it is a fairly good candidate for simplicity,
+ *   efficiency and quality.
+ *
+ * Xor-Shift-Rotate PRNGs:
+ * - a generic annoying feature of this generator class is that there is one
+ *   special all-zero state which must not be used, inducing a constraint on
+ *   initialization, and when the generator get close to it (i.e. many zeros
+ *   in the state) it takes some iterations to recover, with small lumps
+ *   of close/related/bad numbers generated in the process.
+ * Xoroshiro128+
+ * - fails statistical linearity tests in the low bits.
+ * Xoroshiro128**
+ *   https://prng.di.unimi.it/xoroshiro128starstar.c
+ * - mild and irrelevant issues: close to zero state, stats issues after some transforms
+ * Xoshiro256**
+ * - it looks good, but some issues (no big deal for pg usage IMO)
+ *   https://www.pcg-random.org/posts/a-quick-look-at-xoshiro256.html
+ *   mild problems getting out of closes-to-zero state
+ *
+ * MT19937-64
+ * + default in many places, standard
+ * - huge 2.5 KiB state (but TinyMT, 127 bits state space)
+ * - slow (but SFMT, twice as fast)
+ * - vulnerabilities on tinyMT?
+ *
+ * WELL
+ * - large state, 32-bit oriented
+ *
+ * ACORN Family (https://en.wikipedia.org/wiki/ACORN_(PRNG))
+ * - Fortran / double oriented, no C implementation found?!
+ * - medium large state "In practice we recommend using k > 10, M = 2^60 (for general application)
+ *   or M = 2^120 (for demanding applications requiring high-quality pseudo-random numbers
+ *   that will consistently pass all the tests in standard test packages such as TestU01)
+ *   and choose any odd value less than M for the seed."
+ *   k = 11 => 88 bytes/704-bits state, too large:-(
+ *
+ * Splitmix?
+ * Others?
+ *
+ * The conclusion is that we use xor-shift-rotate generator below.
+ */
+
+#include "c.h"
+#include "common/pg_prng.h"
+#include "port/pg_bitutils.h"
+#include <math.h>
+
+#define FIRST_BIT_MASK UINT64CONST(0x8000000000000000)
+#define RIGHT_HALF_MASK UINT64CONST(0x00000000FFFFFFFF)
+#define DMANTISSA_MASK UINT64CONST(0x000FFFFFFFFFFFFF)
+
+/* 64-bits rotate left */
+static inline uint64
+rotl(const uint64 x, const int bits)
+{
+	return (x << bits) | (x >> (64 - bits));
+}
+
+/* another 64-bits generator is used for state initialization or iterations */
+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);
+}
+
+/* seed prng state from a 64 bits integer, ensuring non zero */
+void
+pg_prng_seed(pg_prng_state *state, uint64 seed)
+{
+	state->s0 = splitmix64(&seed);
+	state->s1 = splitmix64(&seed);
+}
+
+/* seed with 53 bits (mantissa & sign) from a float */
+void
+pg_prng_fseed(pg_prng_state *state, double fseed)
+{
+	uint64 seed = (int64) (((double) DMANTISSA_MASK) * fseed);
+	pg_prng_seed(state, seed);
+}
+
+/* strong random seeding */
+void
+pg_prng_strong_seed(pg_prng_state *state)
+{
+	pg_strong_random((void *) state, sizeof(pg_prng_state));
+
+	/* avoid zero with Donald Knuth's LCG parameters */
+	if (unlikely(state->s0 == 0 && state->s1 == 0))
+	{
+		/* should it warn that something is amiss if we get there? */
+		pg_prng_state def = PG_PRNG_DEFAULT_STATE;
+		*state = def;
+	}
+}
+
+/* generator & state update */
+static uint64
+xoroshiro128ss(pg_prng_state *state)
+{
+	const 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;
+}
+
+/* u64 generator */
+uint64
+pg_prng_u64(pg_prng_state *state)
+{
+	return xoroshiro128ss(state);
+}
+
+/*
+ * select in a range with bitmask rejection.
+ *
+ * the prng may advance by several states or none,
+ * depending on the range value.
+ */
+uint64
+pg_prng_u64_range(pg_prng_state *state, uint64 range)
+{
+	uint64 val;
+
+	if (range > 1)
+	{
+		/* bit position is between 0 and 63, so rshift >= 0 */
+		uint32 rshift = 63 - pg_leftmost_one_pos64(range-1);
+
+		/* iterate till val in [0, range) */
+		do
+		{
+			val = xoroshiro128ss(state) >> rshift;
+		}
+		while (val >= range);
+	}
+	else
+		val = 0;
+
+	return val;
+}
+
+/* i64 generator */
+int64
+pg_prng_i64(pg_prng_state *state)
+{
+	return (int64) xoroshiro128ss(state);
+}
+
+/* u32 generator */
+uint32
+pg_prng_u32(pg_prng_state *state)
+{
+	const uint64 v = xoroshiro128ss(state);
+	return (uint32) (((v >> 32) ^ v) & RIGHT_HALF_MASK);
+}
+
+/* i32 generator */
+int32
+pg_prng_i32(pg_prng_state *state)
+{
+	const uint64 v = xoroshiro128ss(state);
+	return (int32) (((v >> 32) ^ v) & RIGHT_HALF_MASK);
+}
+
+/* double generator */
+double
+pg_prng_f64(pg_prng_state *state)
+{
+	uint64 v = xoroshiro128ss(state);
+	return ldexp((double) (v & DMANTISSA_MASK), -52);
+}
+
+/* bool generator */
+bool
+pg_prng_bool(pg_prng_state *state)
+{
+	uint64 v = xoroshiro128ss(state);
+	return (v & FIRST_BIT_MASK) == FIRST_BIT_MASK;
+}
diff --git a/src/include/common/pg_prng.h b/src/include/common/pg_prng.h
new file mode 100644
index 0000000000..1a3acf930f
--- /dev/null
+++ b/src/include/common/pg_prng.h
@@ -0,0 +1,34 @@
+/*-------------------------------------------------------------------------
+ *
+ * PRNG: internal Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * src/include/common/pg_prng.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_PRNG_H
+#define PG_PRNG_H
+
+/* 128 bits state */
+typedef struct pg_prng_state {
+	uint64	s0, s1;
+} pg_prng_state;
+
+/* use Donald Knuth's LCG constants for default state */
+#define PG_PRNG_DEFAULT_STATE \
+	{ UINT64CONST(0x5851F42D4C957F2D), UINT64CONST(0x14057B7EF767814F) }
+
+extern void pg_prng_seed(pg_prng_state *state, uint64 seed);
+extern void pg_prng_fseed(pg_prng_state *state, double fseed);
+extern void pg_prng_strong_seed(pg_prng_state *state);
+extern uint64 pg_prng_u64(pg_prng_state *state);
+extern uint64 pg_prng_u64_range(pg_prng_state *state, uint64 range);
+extern int64 pg_prng_i64(pg_prng_state *state);
+extern uint32 pg_prng_u32(pg_prng_state *state);
+extern int32 pg_prng_i32(pg_prng_state *state);
+extern double pg_prng_f64(pg_prng_state *state);
+extern bool pg_prng_bool(pg_prng_state *state);
+
+#endif							/* PG_PRNG_H */
diff --git a/src/include/optimizer/geqo.h b/src/include/optimizer/geqo.h
index 24dcdfb6cc..b28e4f084a 100644
--- a/src/include/optimizer/geqo.h
+++ b/src/include/optimizer/geqo.h
@@ -22,6 +22,7 @@
 #ifndef GEQO_H
 #define GEQO_H
 
+#include "common/pg_prng.h"
 #include "nodes/pathnodes.h"
 #include "optimizer/geqo_gene.h"
 
@@ -72,8 +73,8 @@ extern double Geqo_seed;		/* 0 .. 1 */
  */
 typedef struct
 {
-	List	   *initial_rels;	/* the base relations we are joining */
-	unsigned short random_state[3]; /* state for pg_erand48() */
+	List		   *initial_rels;	/* the base relations we are joining */
+	pg_prng_state	random_state;   /* PRNG state */
 } GeqoPrivateData;
 
 
diff --git a/src/include/port.h b/src/include/port.h
index 82f63de325..eb18665196 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -356,11 +356,6 @@ extern int	gettimeofday(struct timeval *tp, struct timezone *tzp);
 #define pgoff_t off_t
 #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
 extern int	fls(int mask);
 #endif
diff --git a/src/include/utils/sampling.h b/src/include/utils/sampling.h
index a58d14281b..c0b1c6a645 100644
--- a/src/include/utils/sampling.h
+++ b/src/include/utils/sampling.h
@@ -13,15 +13,14 @@
 #ifndef SAMPLING_H
 #define SAMPLING_H
 
+#include "common/pg_prng.h"
 #include "storage/block.h"		/* for typedef BlockNumber */
 
 
 /* Random generator for sampling code */
-typedef unsigned short SamplerRandomState[3];
-
 extern void sampler_random_init_state(long seed,
-									  SamplerRandomState randstate);
-extern double sampler_random_fract(SamplerRandomState randstate);
+									  pg_prng_state *randstate);
+extern double sampler_random_fract(pg_prng_state *randstate);
 
 /* Block sampling methods */
 
@@ -32,7 +31,7 @@ typedef struct
 	int			n;				/* desired sample size */
 	BlockNumber t;				/* current block number */
 	int			m;				/* blocks selected so far */
-	SamplerRandomState randstate;	/* random generator state */
+	pg_prng_state randstate;	/* random generator state */
 } BlockSamplerData;
 
 typedef BlockSamplerData *BlockSampler;
@@ -46,8 +45,9 @@ extern BlockNumber BlockSampler_Next(BlockSampler bs);
 
 typedef struct
 {
-	double		W;
-	SamplerRandomState randstate;	/* random generator state */
+	double			W;
+	pg_prng_state	randstate;	/* random generator state */
+	bool			randstate_initialized;
 } ReservoirStateData;
 
 typedef ReservoirStateData *ReservoirState;
diff --git a/src/port/Makefile b/src/port/Makefile
index 52dbf5783f..b3754d8940 100644
--- a/src/port/Makefile
+++ b/src/port/Makefile
@@ -42,7 +42,6 @@ OBJS = \
 	$(PG_CRC32C_OBJS) \
 	bsearch_arg.o \
 	chklocale.o \
-	erand48.o \
 	inet_net_ntop.o \
 	noblock.o \
 	path.o \
diff --git a/src/port/erand48.c b/src/port/erand48.c
deleted file mode 100644
index a82ea94220..0000000000
--- a/src/port/erand48.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/port/random.c b/src/port/random.c
deleted file mode 100644
index 2dd59a0829..0000000000
--- a/src/port/random.c
+++ /dev/null
@@ -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();
-}
diff --git a/src/port/srandom.c b/src/port/srandom.c
deleted file mode 100644
index cf1007b2ef..0000000000
--- a/src/port/srandom.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/test/modules/test_integerset/test_integerset.c b/src/test/modules/test_integerset/test_integerset.c
index 21c6f49b37..8935a81284 100644
--- a/src/test/modules/test_integerset/test_integerset.c
+++ b/src/test/modules/test_integerset/test_integerset.c
@@ -12,6 +12,7 @@
  */
 #include "postgres.h"
 
+#include "common/pg_prng.h"
 #include "fmgr.h"
 #include "lib/integerset.h"
 #include "miscadmin.h"
@@ -99,12 +100,16 @@ static void check_with_filler(IntegerSet *intset, uint64 x, uint64 value, uint64
 static void test_single_value_and_filler(uint64 value, uint64 filler_min, uint64 filler_max);
 static void test_huge_distances(void);
 
+static pg_prng_state	pr_state;
+
 /*
  * SQL-callable entry point to perform all tests.
  */
 Datum
 test_integerset(PG_FUNCTION_ARGS)
 {
+	pg_prng_strong_seed(&pr_state);
+
 	/* Tests for various corner cases */
 	test_empty();
 	test_huge_distances();
@@ -248,7 +253,7 @@ test_pattern(const test_spec *spec)
 		 * only a small part of the integer space is used.  We would very
 		 * rarely hit values that are actually in the set.
 		 */
-		x = (pg_lrand48() << 31) | pg_lrand48();
+		x = pg_prng_u64(&pr_state);
 		x = x % (last_int + 1000);
 
 		/* Do we expect this value to be present in the set? */
@@ -571,7 +576,7 @@ test_huge_distances(void)
 	 */
 	while (num_values < 1000)
 	{
-		val += pg_lrand48();
+		val += pg_prng_i32(&pr_state);
 		values[num_values++] = val;
 	}
 
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 233ddbf4c2..29d0f467cb 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -103,9 +103,9 @@ sub mkvcbuild
 	$solution = CreateSolution($vsVersion, $config);
 
 	our @pgportfiles = qw(
-	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c random.c
-	  srandom.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
+	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c
+	  getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
+	  snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
 	  dirent.c dlopen.c getopt.c getopt_long.c link.c
 	  pread.c preadv.c pwrite.c pwritev.c pg_bitutils.c
 	  pg_strong_random.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c
@@ -131,9 +131,9 @@ sub mkvcbuild
 	  config_info.c controldata_utils.c d2s.c encnames.c exec.c
 	  f2s.c file_perm.c file_utils.c hashfn.c hex.c ip.c jsonapi.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
-	  saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c username.c
-	  wait_error.c wchar.c);
+	  pg_get_line.c pg_lzcompress.c pg_prng.c pgfnames.c psprintf.c relpath.c
+	  rmtree.c saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c
+	  username.c wait_error.c wchar.c);
 
 	if ($solution->{options}->{openssl})
 	{
#38Dean Rasheed
dean.a.rasheed@gmail.com
In reply to: Fabien COELHO (#37)
Re: rand48 replacement

On Thu, 8 Jul 2021 at 09:26, Fabien COELHO <coelho@cri.ensmp.fr> wrote:

Finally, I think it would be better to treat the upper bound of the
range as inclusive.

This bothered me as well, but the usual approach seems to use range as the
number of values, so I was hesitant to depart from that. I'm still
hesitant to go that way.

Yeah, that bothered me too.

For example, java.util.Random.nextInt(bound) returns a value in the
range [0,bound).

But other implementations are not all like that. For example python's
random.randint(a,b) returns a value in the range [a,b].

Python also has random.randrange(start,stop[,step]), which is designed
for compatibility with their range(start,stop[,step]) function, which
treats "stop" as exclusive.

However, Postgres tends to go the other way, and treat the upper bound
as inclusive, as in, for example, generate_series() and pgbench's
random() function.

I think it makes more sense to do it that way, because then such
functions can work all the way up to and including the limit of the
bound's datatype, which makes them more general.

Regards,
Dean

#39Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Dean Rasheed (#38)
1 attachment(s)
Re: rand48 replacement

Finally, I think it would be better to treat the upper bound of the
range as inclusive.

This bothered me as well, but the usual approach seems to use range as the
number of values, so I was hesitant to depart from that. I'm still
hesitant to go that way.

Yeah, that bothered me too.

For example, java.util.Random.nextInt(bound) returns a value in the
range [0,bound).

But other implementations are not all like that. For example python's
random.randint(a,b) returns a value in the range [a,b].

Python also has random.randrange(start,stop[,step]), which is designed
for compatibility with their range(start,stop[,step]) function, which
treats "stop" as exclusive.

However, Postgres tends to go the other way, and treat the upper bound
as inclusive, as in, for example, generate_series() and pgbench's
random() function.

I think it makes more sense to do it that way, because then such
functions can work all the way up to and including the limit of the
bound's datatype, which makes them more general.

Yep. Still, with one argument:

- C#: Random Next is exclusive
- Go: rand Intn is exclusive
- Rust: rand gen_range is exclusive
- Erlang: rand uniform is inclusive, BUT values start from 1

The rule seems to be: one parameter is usually the number of values, thus
is exclusive, 2 parameters describes the range, this is inclusive.

Attached a v10 which is some kind of compromise where the interface uses
inclusive min and max bounds, so that all values can be reached.

--
Fabien.

Attachments:

prng-10.patchtext/x-diff; name=prng-10.patchDownload
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 2c2f149fb0..146b524076 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -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]);
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index f15c97ad7a..3f9e954bd2 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -5146,7 +5146,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]);
 		}
diff --git a/contrib/tsm_system_rows/tsm_system_rows.c b/contrib/tsm_system_rows/tsm_system_rows.c
index 4996612902..1a46d4b143 100644
--- a/contrib/tsm_system_rows/tsm_system_rows.c
+++ b/contrib/tsm_system_rows/tsm_system_rows.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/contrib/tsm_system_time/tsm_system_time.c b/contrib/tsm_system_time/tsm_system_time.c
index 788d8f9a68..36acc6c106 100644
--- a/contrib/tsm_system_time/tsm_system_time.c
+++ b/contrib/tsm_system_time/tsm_system_time.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 0c9591415e..1ddbeed2dc 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1308,7 +1308,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 					 * Found a suitable tuple, so save it, replacing one old
 					 * tuple at random
 					 */
-					int			k = (int) (targrows * sampler_random_fract(rstate.randstate));
+					int			k = (int) (targrows * sampler_random_fract(&rstate.randstate));
 
 					Assert(k >= 0 && k < targrows);
 					heap_freetuple(rows[k]);
diff --git a/src/backend/optimizer/geqo/geqo_random.c b/src/backend/optimizer/geqo/geqo_random.c
index f21bc047e6..8b42a9ffba 100644
--- a/src/backend/optimizer/geqo/geqo_random.c
+++ b/src/backend/optimizer/geqo/geqo_random.c
@@ -21,14 +21,7 @@ geqo_set_seed(PlannerInfo *root, double seed)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	/*
-	 * XXX. This seeding algorithm could certainly be improved - but it is not
-	 * critical to do so.
-	 */
-	memset(private->random_state, 0, sizeof(private->random_state));
-	memcpy(private->random_state,
-		   &seed,
-		   Min(sizeof(private->random_state), sizeof(seed)));
+	pg_prng_fseed(&private->random_state, seed);
 }
 
 double
@@ -36,5 +29,5 @@ geqo_rand(PlannerInfo *root)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	return pg_erand48(private->random_state);
+	return pg_prng_f64(&private->random_state);
 }
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 098bbb372b..9fc0b108dd 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -21,6 +21,7 @@
 
 #include "catalog/pg_type.h"
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/shortest_dec.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
@@ -65,7 +66,7 @@ float8		degree_c_one = 1.0;
 
 /* State for drandom() and setseed() */
 static bool drandom_seed_set = false;
-static unsigned short drandom_seed[3] = {0, 0, 0};
+static pg_prng_state drandom_seed = PG_PRNG_DEFAULT_STATE;
 
 /* Local function prototypes */
 static double sind_q1(double x);
@@ -2762,22 +2763,20 @@ drandom(PG_FUNCTION_ARGS)
 		 * Should that fail for some reason, we fall back on a lower-quality
 		 * seed based on current time and PID.
 		 */
-		if (!pg_strong_random(drandom_seed, sizeof(drandom_seed)))
+		if (!pg_strong_random(&drandom_seed, sizeof(drandom_seed)))
 		{
 			TimestampTz now = GetCurrentTimestamp();
 			uint64		iseed;
 
 			/* Mix the PID with the most predictable bits of the timestamp */
 			iseed = (uint64) now ^ ((uint64) MyProcPid << 32);
-			drandom_seed[0] = (unsigned short) iseed;
-			drandom_seed[1] = (unsigned short) (iseed >> 16);
-			drandom_seed[2] = (unsigned short) (iseed >> 32);
+			pg_prng_seed(&drandom_seed, iseed);
 		}
 		drandom_seed_set = true;
 	}
 
-	/* pg_erand48 produces desired result range [0.0 - 1.0) */
-	result = pg_erand48(drandom_seed);
+	/* produces desired result range [0.0 - 1.0) */
+	result = pg_prng_f64(&drandom_seed);
 
 	PG_RETURN_FLOAT8(result);
 }
@@ -2790,7 +2789,6 @@ Datum
 setseed(PG_FUNCTION_ARGS)
 {
 	float8		seed = PG_GETARG_FLOAT8(0);
-	uint64		iseed;
 
 	if (seed < -1 || seed > 1 || isnan(seed))
 		ereport(ERROR,
@@ -2798,11 +2796,7 @@ setseed(PG_FUNCTION_ARGS)
 				 errmsg("setseed parameter %g is out of allowed range [-1,1]",
 						seed)));
 
-	/* Use sign bit + 47 fractional bits to fill drandom_seed[] */
-	iseed = (int64) (seed * (float8) UINT64CONST(0x7FFFFFFFFFFF));
-	drandom_seed[0] = (unsigned short) iseed;
-	drandom_seed[1] = (unsigned short) (iseed >> 16);
-	drandom_seed[2] = (unsigned short) (iseed >> 32);
+	pg_prng_fseed(&drandom_seed, seed);
 	drandom_seed_set = true;
 
 	PG_RETURN_VOID();
diff --git a/src/backend/utils/misc/sampling.c b/src/backend/utils/misc/sampling.c
index 0c327e823f..7b59128878 100644
--- a/src/backend/utils/misc/sampling.c
+++ b/src/backend/utils/misc/sampling.c
@@ -49,7 +49,7 @@ BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize,
 	bs->t = 0;					/* blocks scanned so far */
 	bs->m = 0;					/* blocks selected so far */
 
-	sampler_random_init_state(randseed, bs->randstate);
+	sampler_random_init_state(randseed, &bs->randstate);
 
 	return Min(bs->n, bs->N);
 }
@@ -98,7 +98,7 @@ BlockSampler_Next(BlockSampler bs)
 	 * less than k, which means that we cannot fail to select enough blocks.
 	 *----------
 	 */
-	V = sampler_random_fract(bs->randstate);
+	V = sampler_random_fract(&bs->randstate);
 	p = 1.0 - (double) k / (double) K;
 	while (V < p)
 	{
@@ -136,10 +136,10 @@ reservoir_init_selection_state(ReservoirState rs, int n)
 	 * Reservoir sampling is not used anywhere where it would need to return
 	 * repeatable results so we can initialize it randomly.
 	 */
-	sampler_random_init_state(random(), rs->randstate);
+	sampler_random_init_state(random(), &rs->randstate);
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	rs->W = exp(-log(sampler_random_fract(rs->randstate)) / n);
+	rs->W = exp(-log(sampler_random_fract(&rs->randstate)) / n);
 }
 
 double
@@ -154,7 +154,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 		double		V,
 					quot;
 
-		V = sampler_random_fract(rs->randstate);	/* Generate V */
+		V = sampler_random_fract(&rs->randstate);	/* Generate V */
 		S = 0;
 		t += 1;
 		/* Note: "num" in Vitter's code is always equal to t - n */
@@ -186,7 +186,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 						tmp;
 
 			/* Generate U and X */
-			U = sampler_random_fract(rs->randstate);
+			U = sampler_random_fract(&rs->randstate);
 			X = t * (W - 1.0);
 			S = floor(X);		/* S is tentatively set to floor(X) */
 			/* Test if U <= h(S)/cg(X) in the manner of (6.3) */
@@ -215,7 +215,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 				y *= numer / denom;
 				denom -= 1;
 			}
-			W = exp(-log(sampler_random_fract(rs->randstate)) / n); /* Generate W in advance */
+			W = exp(-log(sampler_random_fract(&rs->randstate)) / n); /* Generate W in advance */
 			if (exp(log(y) / n) <= (t + X) / t)
 				break;
 		}
@@ -230,24 +230,22 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
  *----------
  */
 void
-sampler_random_init_state(long seed, SamplerRandomState randstate)
+sampler_random_init_state(long seed, pg_prng_state *randstate)
 {
-	randstate[0] = 0x330e;		/* same as pg_erand48, but could be anything */
-	randstate[1] = (unsigned short) seed;
-	randstate[2] = (unsigned short) (seed >> 16);
+	pg_prng_seed(randstate, (uint64_t) seed);
 }
 
 /* Select a random value R uniformly distributed in (0 - 1) */
 double
-sampler_random_fract(SamplerRandomState randstate)
+sampler_random_fract(pg_prng_state *randstate)
 {
 	double		res;
 
-	/* pg_erand48 returns a value in [0.0 - 1.0), so we must reject 0 */
+	/* pg_prng_f64 returned value in [0.0 - 1.0), so we must reject 0.0 */
 	do
 	{
-		res = pg_erand48(randstate);
-	} while (res == 0.0);
+		res = pg_prng_f64(randstate);
+	} while (unlikely(res == 0.0));
 	return res;
 }
 
@@ -266,22 +264,28 @@ double
 anl_random_fract(void)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(random(), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* and compute a random fraction */
-	return sampler_random_fract(oldrs.randstate);
+	return sampler_random_fract(&oldrs.randstate);
 }
 
 double
 anl_init_selection_state(int n)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(random(), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	return exp(-log(sampler_random_fract(oldrs.randstate)) / n);
+	return exp(-log(sampler_random_fract(&oldrs.randstate)) / n);
 }
 
 double
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 4aeccd93af..91732c4ba5 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -58,6 +58,7 @@
 #endif
 
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/logging.h"
 #include "common/string.h"
 #include "common/username.h"
@@ -343,16 +344,8 @@ typedef struct StatsData
 	SimpleStats lag;
 } StatsData;
 
-/*
- * Struct to keep random state.
- */
-typedef struct RandomState
-{
-	unsigned short xseed[3];
-} RandomState;
-
 /* 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 */
 static THREAD_BARRIER_T barrier;
@@ -454,7 +447,7 @@ typedef struct
 	 * Separate randomness for each client. This is used for random functions
 	 * 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			command;		/* command number in script */
@@ -491,9 +484,9 @@ typedef struct
 	 * random state to make all of them independent of each other and
 	 * therefore deterministic at the thread level.
 	 */
-	RandomState ts_choose_rs;	/* random state for selecting a script */
-	RandomState ts_throttle_rs; /* random state for transaction throttling */
-	RandomState ts_sample_rs;	/* random state for log sampling */
+	pg_prng_state	ts_choose_rs;	/* random state for selecting a script */
+	pg_prng_state	ts_throttle_rs; /* random state for transaction throttling */
+	pg_prng_state	ts_sample_rs;	/* random state for log sampling */
 
 	int64		throttle_trigger;	/* previous/next throttling (us) */
 	FILE	   *logfile;		/* where to log, or NULL */
@@ -891,42 +884,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.
  */
 static void
-initRandomState(RandomState *random_state)
+initRandomState(pg_prng_state *state)
 {
-	random_state->xseed[0] = (unsigned short)
-		(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);
+	pg_prng_seed(state, pg_prng_u64(&base_random_sequence));
 }
 
+
 /*
- * Random number generator: uniform distribution from min to max inclusive.
+ * PRNG: uniform distribution from min to max inclusive.
  *
  * Although the limits are expressed as int64, you can't generate the full
  * int64 range in one call, because the difference of the limits mustn't
- * overflow int64.  In practice it's unwise to ask for more than an int32
- * range, because of the limited precision of pg_erand48().
+ * overflow int64. This is not checked.
  */
 static int64
-getrand(RandomState *random_state, int64 min, int64 max)
+getrand(pg_prng_state *state, int64 min, int64 max)
 {
-	/*
-	 * 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));
+	return min + (int64) pg_prng_u64_range(state, 0, max - min);
 }
 
 /*
@@ -935,8 +914,7 @@ getrand(RandomState *random_state, int64 min, int64 max)
  * value is exp(-parameter).
  */
 static int64
-getExponentialRand(RandomState *random_state, int64 min, int64 max,
-				   double parameter)
+getExponentialRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		cut,
 				uniform,
@@ -946,7 +924,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 	Assert(parameter > 0.0);
 	cut = exp(-parameter);
 	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	uniform = 1.0 - pg_prng_f64(state);
 
 	/*
 	 * inner expression in (cut, 1] (if parameter > 0), rand in [0, 1)
@@ -959,8 +937,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 
 /* random number generator: gaussian distribution from min to max inclusive */
 static int64
-getGaussianRand(RandomState *random_state, int64 min, int64 max,
-				double parameter)
+getGaussianRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		stdev;
 	double		rand;
@@ -983,13 +960,13 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
 	do
 	{
 		/*
-		 * pg_erand48 generates [0,1), but for the basic version of the
+		 * pg_prng_f64 generates [0,1), but for the basic version of the
 		 * Box-Muller transform the two uniformly distributed random numbers
 		 * are expected in (0, 1] (see
 		 * https://en.wikipedia.org/wiki/Box-Muller_transform)
 		 */
-		double		rand1 = 1.0 - pg_erand48(random_state->xseed);
-		double		rand2 = 1.0 - pg_erand48(random_state->xseed);
+		double		rand1 = 1.0 - pg_prng_f64(state);
+		double		rand2 = 1.0 - pg_prng_f64(state);
 
 		/* Box-Muller basic form transform */
 		double		var_sqrt = sqrt(-2.0 * log(rand1));
@@ -1019,7 +996,7 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
  * not be one.
  */
 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
@@ -1027,8 +1004,8 @@ getPoissonRand(RandomState *random_state, double center)
 	 */
 	double		uniform;
 
-	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	/* pseudo-random value in [0, 1), uniform in (0, 1] */
+	uniform = 1.0 - pg_prng_f64(state);
 
 	return (int64) (-log(uniform) * center + 0.5);
 }
@@ -1041,7 +1018,7 @@ getPoissonRand(RandomState *random_state, double center)
  * This works for s > 1.0, but may perform badly for s very close to 1.0.
  */
 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		x,
@@ -1056,8 +1033,8 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 	while (true)
 	{
 		/* random variates */
-		u = pg_erand48(random_state->xseed);
-		v = pg_erand48(random_state->xseed);
+		u = pg_prng_f64(state);
+		v = pg_prng_f64(state);
 
 		x = floor(pow(u, -1.0 / (s - 1.0)));
 
@@ -1071,14 +1048,14 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 
 /* random number generator: zipfian distribution from min to max inclusive */
 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;
 
 	/* abort if parameter is invalid */
 	Assert(MIN_ZIPFIAN_PARAM <= s && s <= MAX_ZIPFIAN_PARAM);
 
-	return min - 1 + computeIterativeZipfian(random_state, n, s);
+	return min - 1 + computeIterativeZipfian(state, n, s);
 }
 
 /*
@@ -1135,7 +1112,7 @@ getHashMurmur2(int64 val, uint64 seed)
  * For small sizes, this generates each of the (size!) possible permutations
  * of integers in the range [0, size) with roughly equal probability.  Once
  * the size is larger than 20, the number of possible permutations exceeds the
- * number of distinct states of the internal pseudorandom number generators,
+ * number of distinct states of the internal pseudorandom number generator,
  * and so not all possible permutations can be generated, but the permutations
  * chosen should continue to give the appearance of being random.
  *
@@ -1145,25 +1122,19 @@ getHashMurmur2(int64 val, uint64 seed)
 static int64
 permute(const int64 val, const int64 isize, const int64 seed)
 {
-	RandomState random_state1;
-	RandomState random_state2;
-	uint64		size;
-	uint64		v;
-	int			masklen;
-	uint64		mask;
-	int			i;
+	/* using a high-end PRNG is probably overkill */
+	pg_prng_state	state;
+	uint64			size;
+	uint64			v;
+	int				masklen;
+	uint64			mask;
+	int				i;
 
 	if (isize < 2)
 		return 0;				/* nothing to permute */
 
-	/* Initialize a pair of random states using the seed */
-	random_state1.xseed[0] = seed & 0xFFFF;
-	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;
+	/* Initialize prng state using the seed */
+	pg_prng_seed(&state, (uint64) seed);
 
 	/* Computations are performed on unsigned values */
 	size = (uint64) isize;
@@ -1209,8 +1180,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 					t;
 
 		/* Random multiply (by an odd number), XOR and rotate of lower half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		if (v <= mask)
 		{
 			v = ((v * m) ^ r) & mask;
@@ -1218,8 +1189,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random multiply (by an odd number), XOR and rotate of upper half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		t = size - 1 - v;
 		if (t <= mask)
 		{
@@ -1229,7 +1200,7 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random offset */
-		r = (uint64) getrand(&random_state2, 0, size - 1);
+		r = pg_prng_u64(&state) % (size - 1);
 		v = (v + r) % size;
 	}
 
@@ -3790,7 +3761,7 @@ doLog(TState *thread, CState *st,
 	 * to the random sample.
 	 */
 	if (sample_rate != 0.0 &&
-		pg_erand48(thread->ts_sample_rs.xseed) > sample_rate)
+		pg_prng_f64(&thread->ts_sample_rs) > sample_rate)
 		return;
 
 	/* should we aggregate the results or not? */
@@ -5714,12 +5685,11 @@ set_random_seed(const char *seed)
 
 	if (seed != NULL)
 		pg_log_info("setting random seed to %llu", (unsigned long long) iseed);
+
 	random_seed = iseed;
 
 	/* Fill base_random_sequence with low-order bits of seed */
-	base_random_sequence.xseed[0] = iseed & 0xFFFF;
-	base_random_sequence.xseed[1] = (iseed >> 16) & 0xFFFF;
-	base_random_sequence.xseed[2] = (iseed >> 32) & 0xFFFF;
+	pg_prng_seed(&base_random_sequence, (uint64_t) iseed);
 
 	return true;
 }
@@ -6405,9 +6375,7 @@ main(int argc, char **argv)
 	/* set default seed for hash functions */
 	if (lookupVariable(&state[0], "default_seed") == NULL)
 	{
-		uint64		seed =
-		((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) |
-		(((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) << 32);
+		uint64		seed = pg_prng_u64(&base_random_sequence);
 
 		for (i = 0; i < nclients; i++)
 			if (!putVariableInt(&state[i], "startup", "default_seed", (int64) seed))
diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl
index 3aa9d5d753..cd91c9ac3a 100644
--- a/src/bin/pgbench/t/001_pgbench_with_server.pl
+++ b/src/bin/pgbench/t/001_pgbench_with_server.pl
@@ -427,9 +427,9 @@ pgbench(
 
 		# After explicit seeding, the four random checks (1-3,20) are
 		# deterministic
-		qr{command=1.: int 13\b},      # uniform random
-		qr{command=2.: int 116\b},     # exponential random
-		qr{command=3.: int 1498\b},    # gaussian random
+		qr{command=1.: int 17\b},      # uniform random
+		qr{command=2.: int 108\b},     # exponential random
+		qr{command=3.: int 1452\b},    # gaussian random
 		qr{command=4.: int 4\b},
 		qr{command=5.: int 5\b},
 		qr{command=6.: int 6\b},
@@ -496,6 +496,7 @@ pgbench(
 		qr{command=109.: boolean true\b},
 		qr{command=110.: boolean true\b},
 		qr{command=111.: boolean true\b},
+		qr{command=113.: boolean true\b},
 	],
 	'pgbench expressions',
 	{
@@ -639,8 +640,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))
 -- actual values
 \set t debug(permute(:v, 1) = 0)
-\set t debug(permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1 and \
-             permute(0, 2, 5435) = 1 and permute(1, 2, 5435) = 0)
+\set t debug(permute(0, 2, 5431) = 1 and permute(1, 2, 5431) = 0 and \
+             permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1)
+-- 63 bits tests for checking permute portability across architectures
+\set size debug(:max - 10)
+\set t debug(permute(:size-1, :size, 5432) = 4352241160009873020 and \
+             permute(:size-2, :size, 5432) = 4990004119691638710 and \
+             permute(:size-3, :size, 5432) = 5868029867256127549 and \
+             permute(:size-4, :size, 5432) = 1238830324886341378 and \
+             permute(:size-5, :size, 5432) = 7415914367102906897 and \
+             permute(:size-6, :size, 5432) = 3214501037984818995 and \
+             permute(:size-7, :size, 5432) =  600660351261124695)
 }
 	});
 
diff --git a/src/common/Makefile b/src/common/Makefile
index 38a8599337..9aee26f694 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -67,6 +67,7 @@ OBJS_COMMON = \
 	md5_common.o \
 	pg_get_line.o \
 	pg_lzcompress.o \
+	pg_prng.o \
 	pgfnames.o \
 	psprintf.o \
 	relpath.o \
diff --git a/src/common/pg_prng.c b/src/common/pg_prng.c
new file mode 100644
index 0000000000..dcbcb5b1ef
--- /dev/null
+++ b/src/common/pg_prng.c
@@ -0,0 +1,239 @@
+/*
+ * Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * Previous version was the 1988 (?) rand48 standard with 48 bits state LCG,
+ * designed for running on 32 bits or even 16 bits architectures, and to produce
+ * 32 bit ints and floats (i.e. *not* doubles).
+ *
+ * The implied 16-bits unpacking/packing may have a detrimental performance impact
+ * on modern 64 bits architectures.
+ *
+ * We (probably) want:
+ * - one reasonable default PRNG for all pg internal uses.
+ * - NOT to invent a new design!
+ * - something fast, close to rand48 (which basically does 2 arithmetic ops)
+ *   no need for something cryptographic though, which would imply slow
+ * - to produce 64 bits integers & doubles with a 52 bits mantissa
+ *   so state size > 64 bits.
+ * - a small state though, because we might generate quite a few of them
+ *   for different purposes so state size <= 256 or even <= 128 bits
+ * - the state to be aligned to whatever, so state size = 128 bits
+ * - 64 bits operations for efficiency on modern architectures,
+ *   but NOT 128 bits operations.
+ * - not to depend on special hardware for speed (eg MMX/SSE/AES).
+ * - not something with obvious known and relevant defects.
+ * - not something with "rights" attached.
+ *
+ * These constraints reduce a lot the available options from
+ * https://en.wikipedia.org/wiki/List_of_random_number_generators
+ *
+ * LCG
+ * - POSIX rand48: m = 2^48, a = 25214903917, c = 11, extract 47...16
+ * - MMIX by Donald Knuth: m = 2^64, a = 6364136223846793005, c = 1442695040888963407
+ * - very bad on low bits, which MUST NOT BE USED
+ *   a 128 bits would do, but no clear standards, and it would imply 128-bit ops,
+ *   see PCG below.
+ *
+ * PCG Family (https://www.pcg-random.org/)
+ * - this is basically an LCG with an improved outpout function
+ *   so as to avoid typical LCG issues and pass stats tests.
+ * - recommands state size to be twice the output size,
+ *   which seems a reasonable requirement.
+ * - this implies 128 bit ops for our target parameters,
+ *   otherwise it is a fairly good candidate for simplicity,
+ *   efficiency and quality.
+ *
+ * Xor-Shift-Rotate PRNGs:
+ * - a generic annoying feature of this generator class is that there is one
+ *   special all-zero state which must not be used, inducing a constraint on
+ *   initialization, and when the generator get close to it (i.e. many zeros
+ *   in the state) it takes some iterations to recover, with small lumps
+ *   of close/related/bad numbers generated in the process.
+ * Xoroshiro128+
+ * - fails statistical linearity tests in the low bits.
+ * Xoroshiro128**
+ *   https://prng.di.unimi.it/xoroshiro128starstar.c
+ * - mild and irrelevant issues: close to zero state, stats issues after some transforms
+ * Xoshiro256**
+ * - it looks good, but some issues (no big deal for pg usage IMO)
+ *   https://www.pcg-random.org/posts/a-quick-look-at-xoshiro256.html
+ *   mild problems getting out of closes-to-zero state
+ *
+ * MT19937-64
+ * + default in many places, standard
+ * - huge 2.5 KiB state (but TinyMT, 127 bits state space)
+ * - slow (but SFMT, twice as fast)
+ * - vulnerabilities on tinyMT?
+ *
+ * WELL
+ * - large state, 32-bit oriented
+ *
+ * ACORN Family (https://en.wikipedia.org/wiki/ACORN_(PRNG))
+ * - Fortran / double oriented, no C implementation found?!
+ * - medium large state "In practice we recommend using k > 10, M = 2^60 (for general application)
+ *   or M = 2^120 (for demanding applications requiring high-quality pseudo-random numbers
+ *   that will consistently pass all the tests in standard test packages such as TestU01)
+ *   and choose any odd value less than M for the seed."
+ *   k = 11 => 88 bytes/704-bits state, too large:-(
+ *
+ * Splitmix?
+ * Others?
+ *
+ * The conclusion is that we use xor-shift-rotate generator below.
+ */
+
+#include "c.h"
+#include "common/pg_prng.h"
+#include "port/pg_bitutils.h"
+#include <math.h>
+
+#define FIRST_BIT_MASK UINT64CONST(0x8000000000000000)
+#define RIGHT_HALF_MASK UINT64CONST(0x00000000FFFFFFFF)
+#define DMANTISSA_MASK UINT64CONST(0x000FFFFFFFFFFFFF)
+
+/* 64-bits rotate left */
+static inline uint64
+rotl(const uint64 x, const int bits)
+{
+	return (x << bits) | (x >> (64 - bits));
+}
+
+/* another 64-bits generator is used for state initialization or iterations */
+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);
+}
+
+/* seed prng state from a 64 bits integer, ensuring non zero */
+void
+pg_prng_seed(pg_prng_state *state, uint64 seed)
+{
+	state->s0 = splitmix64(&seed);
+	state->s1 = splitmix64(&seed);
+}
+
+/* seed with 53 bits (mantissa & sign) from a float */
+void
+pg_prng_fseed(pg_prng_state *state, double fseed)
+{
+	uint64 seed = (int64) (((double) DMANTISSA_MASK) * fseed);
+	pg_prng_seed(state, seed);
+}
+
+/* strong random seeding */
+void
+pg_prng_strong_seed(pg_prng_state *state)
+{
+	pg_strong_random((void *) state, sizeof(pg_prng_state));
+
+	/* avoid zero with Donald Knuth's LCG parameters */
+	if (unlikely(state->s0 == 0 && state->s1 == 0))
+	{
+		/* should it warn that something is amiss if we get there? */
+		pg_prng_state def = PG_PRNG_DEFAULT_STATE;
+		*state = def;
+	}
+}
+
+/* generator & state update */
+static uint64
+xoroshiro128ss(pg_prng_state *state)
+{
+	const 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;
+}
+
+/* u64 generator */
+uint64
+pg_prng_u64(pg_prng_state *state)
+{
+	return xoroshiro128ss(state);
+}
+
+/*
+ * select in a range with bitmask rejection.
+ *
+ * if range is empty, rmin is returned.
+ *
+ * the prng may advance by several states or none,
+ * depending on the range value.
+ */
+uint64
+pg_prng_u64_range(pg_prng_state *state, uint64 rmin, uint64 rmax)
+{
+	uint64 val;
+
+	if (likely(rmax > rmin))
+	{
+		uint64 range = rmax - rmin;
+
+		/* bit position is between 0 and 63, so rshift >= 0 */
+		uint32 rshift = 63 - pg_leftmost_one_pos64(range);
+
+		do
+		{
+			val = xoroshiro128ss(state) >> rshift;
+		}
+		while (unlikely(val > range));
+	}
+	else
+		val = 0;
+
+	return rmin + val;
+}
+
+/* i64 generator */
+int64
+pg_prng_i64(pg_prng_state *state)
+{
+	return (int64) xoroshiro128ss(state);
+}
+
+/* u32 generator */
+uint32
+pg_prng_u32(pg_prng_state *state)
+{
+	const uint64 v = xoroshiro128ss(state);
+	return (uint32) (((v >> 32) ^ v) & RIGHT_HALF_MASK);
+}
+
+/* i32 generator */
+int32
+pg_prng_i32(pg_prng_state *state)
+{
+	const uint64 v = xoroshiro128ss(state);
+	return (int32) (((v >> 32) ^ v) & RIGHT_HALF_MASK);
+}
+
+/* double generator */
+double
+pg_prng_f64(pg_prng_state *state)
+{
+	uint64 v = xoroshiro128ss(state);
+	return ldexp((double) (v & DMANTISSA_MASK), -52);
+}
+
+/* bool generator */
+bool
+pg_prng_bool(pg_prng_state *state)
+{
+	uint64 v = xoroshiro128ss(state);
+	return (v & FIRST_BIT_MASK) == FIRST_BIT_MASK;
+}
diff --git a/src/include/common/pg_prng.h b/src/include/common/pg_prng.h
new file mode 100644
index 0000000000..c2d7830ac8
--- /dev/null
+++ b/src/include/common/pg_prng.h
@@ -0,0 +1,34 @@
+/*-------------------------------------------------------------------------
+ *
+ * PRNG: internal Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * src/include/common/pg_prng.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_PRNG_H
+#define PG_PRNG_H
+
+/* 128 bits state */
+typedef struct pg_prng_state {
+	uint64	s0, s1;
+} pg_prng_state;
+
+/* use Donald Knuth's LCG constants for default state */
+#define PG_PRNG_DEFAULT_STATE \
+	{ UINT64CONST(0x5851F42D4C957F2D), UINT64CONST(0x14057B7EF767814F) }
+
+extern void pg_prng_seed(pg_prng_state *state, uint64 seed);
+extern void pg_prng_fseed(pg_prng_state *state, double fseed);
+extern void pg_prng_strong_seed(pg_prng_state *state);
+extern uint64 pg_prng_u64(pg_prng_state *state);
+extern uint64 pg_prng_u64_range(pg_prng_state *state, uint64 rmin, uint64 rmax);
+extern int64 pg_prng_i64(pg_prng_state *state);
+extern uint32 pg_prng_u32(pg_prng_state *state);
+extern int32 pg_prng_i32(pg_prng_state *state);
+extern double pg_prng_f64(pg_prng_state *state);
+extern bool pg_prng_bool(pg_prng_state *state);
+
+#endif							/* PG_PRNG_H */
diff --git a/src/include/optimizer/geqo.h b/src/include/optimizer/geqo.h
index 24dcdfb6cc..b28e4f084a 100644
--- a/src/include/optimizer/geqo.h
+++ b/src/include/optimizer/geqo.h
@@ -22,6 +22,7 @@
 #ifndef GEQO_H
 #define GEQO_H
 
+#include "common/pg_prng.h"
 #include "nodes/pathnodes.h"
 #include "optimizer/geqo_gene.h"
 
@@ -72,8 +73,8 @@ extern double Geqo_seed;		/* 0 .. 1 */
  */
 typedef struct
 {
-	List	   *initial_rels;	/* the base relations we are joining */
-	unsigned short random_state[3]; /* state for pg_erand48() */
+	List		   *initial_rels;	/* the base relations we are joining */
+	pg_prng_state	random_state;   /* PRNG state */
 } GeqoPrivateData;
 
 
diff --git a/src/include/port.h b/src/include/port.h
index 82f63de325..eb18665196 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -356,11 +356,6 @@ extern int	gettimeofday(struct timeval *tp, struct timezone *tzp);
 #define pgoff_t off_t
 #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
 extern int	fls(int mask);
 #endif
diff --git a/src/include/utils/sampling.h b/src/include/utils/sampling.h
index a58d14281b..c0b1c6a645 100644
--- a/src/include/utils/sampling.h
+++ b/src/include/utils/sampling.h
@@ -13,15 +13,14 @@
 #ifndef SAMPLING_H
 #define SAMPLING_H
 
+#include "common/pg_prng.h"
 #include "storage/block.h"		/* for typedef BlockNumber */
 
 
 /* Random generator for sampling code */
-typedef unsigned short SamplerRandomState[3];
-
 extern void sampler_random_init_state(long seed,
-									  SamplerRandomState randstate);
-extern double sampler_random_fract(SamplerRandomState randstate);
+									  pg_prng_state *randstate);
+extern double sampler_random_fract(pg_prng_state *randstate);
 
 /* Block sampling methods */
 
@@ -32,7 +31,7 @@ typedef struct
 	int			n;				/* desired sample size */
 	BlockNumber t;				/* current block number */
 	int			m;				/* blocks selected so far */
-	SamplerRandomState randstate;	/* random generator state */
+	pg_prng_state randstate;	/* random generator state */
 } BlockSamplerData;
 
 typedef BlockSamplerData *BlockSampler;
@@ -46,8 +45,9 @@ extern BlockNumber BlockSampler_Next(BlockSampler bs);
 
 typedef struct
 {
-	double		W;
-	SamplerRandomState randstate;	/* random generator state */
+	double			W;
+	pg_prng_state	randstate;	/* random generator state */
+	bool			randstate_initialized;
 } ReservoirStateData;
 
 typedef ReservoirStateData *ReservoirState;
diff --git a/src/port/Makefile b/src/port/Makefile
index 52dbf5783f..b3754d8940 100644
--- a/src/port/Makefile
+++ b/src/port/Makefile
@@ -42,7 +42,6 @@ OBJS = \
 	$(PG_CRC32C_OBJS) \
 	bsearch_arg.o \
 	chklocale.o \
-	erand48.o \
 	inet_net_ntop.o \
 	noblock.o \
 	path.o \
diff --git a/src/port/erand48.c b/src/port/erand48.c
deleted file mode 100644
index a82ea94220..0000000000
--- a/src/port/erand48.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/port/random.c b/src/port/random.c
deleted file mode 100644
index 2dd59a0829..0000000000
--- a/src/port/random.c
+++ /dev/null
@@ -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();
-}
diff --git a/src/port/srandom.c b/src/port/srandom.c
deleted file mode 100644
index cf1007b2ef..0000000000
--- a/src/port/srandom.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/test/modules/test_integerset/test_integerset.c b/src/test/modules/test_integerset/test_integerset.c
index 21c6f49b37..8935a81284 100644
--- a/src/test/modules/test_integerset/test_integerset.c
+++ b/src/test/modules/test_integerset/test_integerset.c
@@ -12,6 +12,7 @@
  */
 #include "postgres.h"
 
+#include "common/pg_prng.h"
 #include "fmgr.h"
 #include "lib/integerset.h"
 #include "miscadmin.h"
@@ -99,12 +100,16 @@ static void check_with_filler(IntegerSet *intset, uint64 x, uint64 value, uint64
 static void test_single_value_and_filler(uint64 value, uint64 filler_min, uint64 filler_max);
 static void test_huge_distances(void);
 
+static pg_prng_state	pr_state;
+
 /*
  * SQL-callable entry point to perform all tests.
  */
 Datum
 test_integerset(PG_FUNCTION_ARGS)
 {
+	pg_prng_strong_seed(&pr_state);
+
 	/* Tests for various corner cases */
 	test_empty();
 	test_huge_distances();
@@ -248,7 +253,7 @@ test_pattern(const test_spec *spec)
 		 * only a small part of the integer space is used.  We would very
 		 * rarely hit values that are actually in the set.
 		 */
-		x = (pg_lrand48() << 31) | pg_lrand48();
+		x = pg_prng_u64(&pr_state);
 		x = x % (last_int + 1000);
 
 		/* Do we expect this value to be present in the set? */
@@ -571,7 +576,7 @@ test_huge_distances(void)
 	 */
 	while (num_values < 1000)
 	{
-		val += pg_lrand48();
+		val += pg_prng_i32(&pr_state);
 		values[num_values++] = val;
 	}
 
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 233ddbf4c2..29d0f467cb 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -103,9 +103,9 @@ sub mkvcbuild
 	$solution = CreateSolution($vsVersion, $config);
 
 	our @pgportfiles = qw(
-	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c random.c
-	  srandom.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
+	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c
+	  getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
+	  snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
 	  dirent.c dlopen.c getopt.c getopt_long.c link.c
 	  pread.c preadv.c pwrite.c pwritev.c pg_bitutils.c
 	  pg_strong_random.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c
@@ -131,9 +131,9 @@ sub mkvcbuild
 	  config_info.c controldata_utils.c d2s.c encnames.c exec.c
 	  f2s.c file_perm.c file_utils.c hashfn.c hex.c ip.c jsonapi.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
-	  saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c username.c
-	  wait_error.c wchar.c);
+	  pg_get_line.c pg_lzcompress.c pg_prng.c pgfnames.c psprintf.c relpath.c
+	  rmtree.c saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c
+	  username.c wait_error.c wchar.c);
 
 	if ($solution->{options}->{openssl})
 	{
#40Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Yura Sokolov (#35)
Re: rand48 replacement

Hello Yura,

Given 99.99% cases will be in the likely case, branch predictor should
eliminate decision cost.

Hmmm. ISTM that a branch predictor should predict that unknown < small
should probably be false, so a hint should be given that it is really
true.

Why? Branch predictor is history based:

Hmmm. This means running the compiler with some special options, running
the code on significant and representative data, then recompiling based on
collected branch stats. This is not the usual way pg is built.

if it were usually true here then it will be true this time either.
unknown < small is usually true therefore branch predictor will assume
it is true.

I put `likely` for compiler: compiler then puts `likely` path closer.

Yes, an explicit hint is needed.

And as Dean Rasheed correctly mentioned, mask method will have really bad
pattern for branch predictor if range is not just below or equal to power
of two.

On average the bitmask is the better unbiased method, if the online
figures are to be trusted. Also, as already said, I do not really want
to add code complexity, especially to get lower average performance,
and especially with code like "threshold = - range % range", where
both variables are unsigned, I have a headache just looking at it:-)

If you mention https://www.pcg-random.org/posts/bounded-rands.html then

Indeed, this is the figures I was refering to when saying that bitmask
looks the best method.

1. first graphs are made with not exact Lemire's code.
Last code from https://lemire.me/blog/2016/06/30/fast-random-shuffling/

Ok, other figures, however there is no comparison with the mask method in
this post, it mostly argues agains division/modulo.

By the way, we have 64bit random. If we use 44bit from it for range <=
(1<<20), then bias will be less than 1/(2**24). Could we just ignore it
(given it is not crypto strong random)?

That was my initial opinion, by Dean insists on an unbiased method. I
agree with Dean that performance, if it is not too bad, does not matter
that much, so that I'm trying to keep the code simple as a main objective.

You do not seem ready to buy this argument. Note that despite that my
research is about compiler optimizations, I did bought it:-)

Given the overheads involved in pgbench, the performance impact of best vs
worst case scenario is minimal:

\set i random(0, 7) -- 8 values, good for mask: 4.515 Mtps
\set i random(0, 8) -- 9 values, bad for mask: 4.151 Mtps

sp the performance penalty is about 8%.

if ((range & (range-1) == 0) /* handle all power 2 cases */
return range != 0 ? val & (range-1) : 0;
if (likely(range < (1<<20)))
/*
* While multiply method is biased, bias will be smaller than 1/(1<<24)
for
* such small ranges. Lets ignore it.
*/
return ((val >> 20) * range) >> 44;
/* Apple's mask method */
m = mask_u64(range-1);
val &= m;
while (val >= range)
val = xoroshiro128ss(state) & m;
return val;
}

Hmmm. The code looks "simple" enough, but I do not like optimizing for 20
bits values is worth it, especially as the bitmask method seems the best
anyway. We were discussing 32 bits before.

Anyway, excuse me for heating this discussion cause of such
non-essential issue.

Well, I like to discuss things!

I'll try to control myself and don't proceed it further.

Yep. We have to compromise at some point. The majority opinion seems to be
that we want code simplicity more, so the bitmask it is. I've posted a
v10.

Thanks for the interesting discussion and arguments!

--
Fabien.

#41Aleksander Alekseev
aleksander@timescale.com
In reply to: Fabien COELHO (#40)
Re: rand48 replacement

Hi Fabien,

Attached a v10 which is some kind of compromise where the interface uses
inclusive min and max bounds, so that all values can be reached.

Just wanted to let you know that cfbot [1]http://cfbot.cputube.org/ doesn't seem to be happy with
the patch. Apparently, some tests are falling. To be honest, I didn't
invest too much time into investigating this. Hopefully, it's not a big
deal.

[1]: http://cfbot.cputube.org/

--
Best regards,
Aleksander Alekseev

#42Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Aleksander Alekseev (#41)
1 attachment(s)
Re: rand48 replacement

Hello Aleksander,

Attached a v10 which is some kind of compromise where the interface uses
inclusive min and max bounds, so that all values can be reached.

Just wanted to let you know that cfbot [1] doesn't seem to be happy with
the patch. Apparently, some tests are falling. To be honest, I didn't
invest too much time into investigating this. Hopefully, it's not a big
deal.

[1]: http://cfbot.cputube.org/

Indeed. I wish that these results would be available from the cf
interface.

Attached a v11 which might improve things.

Thanks for the ping!

--
Fabien.

Attachments:

prng-11.patchtext/x-diff; name=prng-11.patchDownload
diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c
index d19f73127c..b250ae912b 100644
--- a/contrib/amcheck/verify_nbtree.c
+++ b/contrib/amcheck/verify_nbtree.c
@@ -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"
@@ -465,8 +466,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();
+		/* Random seed relies on backend prng initialization to avoid repetition */
+		seed = pg_prng_u64(&pg_global_prng_state);
 		/* Create Bloom filter to fingerprint index */
 		state->filter = bloom_create(total_elems, maintenance_work_mem, seed);
 		state->heaptuplespresent = 0;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 2c2f149fb0..146b524076 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -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]);
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 76d4fea21c..c139382170 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -5157,7 +5157,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]);
 		}
diff --git a/contrib/tsm_system_rows/tsm_system_rows.c b/contrib/tsm_system_rows/tsm_system_rows.c
index 4996612902..1a46d4b143 100644
--- a/contrib/tsm_system_rows/tsm_system_rows.c
+++ b/contrib/tsm_system_rows/tsm_system_rows.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/contrib/tsm_system_time/tsm_system_time.c b/contrib/tsm_system_time/tsm_system_time.c
index 788d8f9a68..36acc6c106 100644
--- a/contrib/tsm_system_time/tsm_system_time.c
+++ b/contrib/tsm_system_time/tsm_system_time.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 8bfb2ad958..35eaf3dabd 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1290,7 +1290,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 					 * Found a suitable tuple, so save it, replacing one old
 					 * tuple at random
 					 */
-					int			k = (int) (targrows * sampler_random_fract(rstate.randstate));
+					int			k = (int) (targrows * sampler_random_fract(&rstate.randstate));
 
 					Assert(k >= 0 && k < targrows);
 					heap_freetuple(rows[k]);
diff --git a/src/backend/optimizer/geqo/geqo_random.c b/src/backend/optimizer/geqo/geqo_random.c
index f21bc047e6..8b42a9ffba 100644
--- a/src/backend/optimizer/geqo/geqo_random.c
+++ b/src/backend/optimizer/geqo/geqo_random.c
@@ -21,14 +21,7 @@ geqo_set_seed(PlannerInfo *root, double seed)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	/*
-	 * XXX. This seeding algorithm could certainly be improved - but it is not
-	 * critical to do so.
-	 */
-	memset(private->random_state, 0, sizeof(private->random_state));
-	memcpy(private->random_state,
-		   &seed,
-		   Min(sizeof(private->random_state), sizeof(seed)));
+	pg_prng_fseed(&private->random_state, seed);
 }
 
 double
@@ -36,5 +29,5 @@ geqo_rand(PlannerInfo *root)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	return pg_erand48(private->random_state);
+	return pg_prng_f64(&private->random_state);
 }
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index e2a76ba055..13df3cc2e4 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -98,6 +98,7 @@
 #include "catalog/pg_control.h"
 #include "common/file_perm.h"
 #include "common/ip.h"
+#include "common/pg_prng.h"
 #include "common/string.h"
 #include "lib/ilist.h"
 #include "libpq/auth.h"
@@ -2675,19 +2676,22 @@ ClosePostmasterPorts(bool am_syslogger)
 void
 InitProcessGlobals(void)
 {
-	unsigned int rseed;
-
 	MyProcPid = getpid();
 	MyStartTimestamp = GetCurrentTimestamp();
 	MyStartTime = timestamptz_to_time_t(MyStartTimestamp);
 
 	/*
-	 * Set a different seed for random() in every process.  We want something
+	 * Set a different global seed in every process.  We want something
 	 * unpredictable, so if possible, use high-quality random bits for the
 	 * seed.  Otherwise, fall back to a seed based on timestamp and PID.
 	 */
-	if (!pg_strong_random(&rseed, sizeof(rseed)))
+	if (!pg_prng_strong_seed(&pg_global_prng_state))
 	{
+		uint64	rseed;
+
+		ereport(WARNING,
+				(errmsg_internal("pg_prng_strong_seed() failed, falling back to weaker seeding")));
+
 		/*
 		 * Since PIDs and timestamps tend to change more frequently in their
 		 * least significant bits, shift the timestamp left to allow a larger
@@ -2698,8 +2702,9 @@ InitProcessGlobals(void)
 		rseed = ((uint64) MyProcPid) ^
 			((uint64) MyStartTimestamp << 12) ^
 			((uint64) MyStartTimestamp >> 20);
+
+		pg_prng_seed(&pg_global_prng_state, rseed);
 	}
-	srandom(rseed);
 }
 
 
diff --git a/src/backend/storage/lmgr/s_lock.c b/src/backend/storage/lmgr/s_lock.c
index 91322a40c1..67c488f433 100644
--- a/src/backend/storage/lmgr/s_lock.c
+++ b/src/backend/storage/lmgr/s_lock.c
@@ -50,6 +50,7 @@
 #include <time.h>
 #include <unistd.h>
 
+#include "common/pg_prng.h"
 #include "port/atomics.h"
 #include "storage/s_lock.h"
 
@@ -63,6 +64,7 @@
 slock_t		dummy_spinlock;
 
 static int	spins_per_delay = DEFAULT_SPINS_PER_DELAY;
+static pg_prng_state	prng_state;
 
 
 /*
@@ -144,7 +146,7 @@ perform_spin_delay(SpinDelayStatus *status)
 
 		/* increase delay by a random fraction between 1X and 2X */
 		status->cur_delay += (int) (status->cur_delay *
-									((double) random() / (double) MAX_RANDOM_VALUE) + 0.5);
+									(pg_prng_f64(&prng_state) / (double) MAX_RANDOM_VALUE) + 0.5);
 		/* wrap back to minimum delay when max is exceeded */
 		if (status->cur_delay > MAX_DELAY_USEC)
 			status->cur_delay = MIN_DELAY_USEC;
@@ -303,7 +305,7 @@ volatile struct test_lock_struct test_lock;
 int
 main()
 {
-	srandom((unsigned int) time(NULL));
+	pg_prng_seed(&prng_state, (unsigned int) time(NULL));
 
 	test_lock.pad1 = test_lock.pad2 = 0x44;
 
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 098bbb372b..9fc0b108dd 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -21,6 +21,7 @@
 
 #include "catalog/pg_type.h"
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/shortest_dec.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
@@ -65,7 +66,7 @@ float8		degree_c_one = 1.0;
 
 /* State for drandom() and setseed() */
 static bool drandom_seed_set = false;
-static unsigned short drandom_seed[3] = {0, 0, 0};
+static pg_prng_state drandom_seed = PG_PRNG_DEFAULT_STATE;
 
 /* Local function prototypes */
 static double sind_q1(double x);
@@ -2762,22 +2763,20 @@ drandom(PG_FUNCTION_ARGS)
 		 * Should that fail for some reason, we fall back on a lower-quality
 		 * seed based on current time and PID.
 		 */
-		if (!pg_strong_random(drandom_seed, sizeof(drandom_seed)))
+		if (!pg_strong_random(&drandom_seed, sizeof(drandom_seed)))
 		{
 			TimestampTz now = GetCurrentTimestamp();
 			uint64		iseed;
 
 			/* Mix the PID with the most predictable bits of the timestamp */
 			iseed = (uint64) now ^ ((uint64) MyProcPid << 32);
-			drandom_seed[0] = (unsigned short) iseed;
-			drandom_seed[1] = (unsigned short) (iseed >> 16);
-			drandom_seed[2] = (unsigned short) (iseed >> 32);
+			pg_prng_seed(&drandom_seed, iseed);
 		}
 		drandom_seed_set = true;
 	}
 
-	/* pg_erand48 produces desired result range [0.0 - 1.0) */
-	result = pg_erand48(drandom_seed);
+	/* produces desired result range [0.0 - 1.0) */
+	result = pg_prng_f64(&drandom_seed);
 
 	PG_RETURN_FLOAT8(result);
 }
@@ -2790,7 +2789,6 @@ Datum
 setseed(PG_FUNCTION_ARGS)
 {
 	float8		seed = PG_GETARG_FLOAT8(0);
-	uint64		iseed;
 
 	if (seed < -1 || seed > 1 || isnan(seed))
 		ereport(ERROR,
@@ -2798,11 +2796,7 @@ setseed(PG_FUNCTION_ARGS)
 				 errmsg("setseed parameter %g is out of allowed range [-1,1]",
 						seed)));
 
-	/* Use sign bit + 47 fractional bits to fill drandom_seed[] */
-	iseed = (int64) (seed * (float8) UINT64CONST(0x7FFFFFFFFFFF));
-	drandom_seed[0] = (unsigned short) iseed;
-	drandom_seed[1] = (unsigned short) (iseed >> 16);
-	drandom_seed[2] = (unsigned short) (iseed >> 32);
+	pg_prng_fseed(&drandom_seed, seed);
 	drandom_seed_set = true;
 
 	PG_RETURN_VOID();
diff --git a/src/backend/utils/misc/sampling.c b/src/backend/utils/misc/sampling.c
index 0c327e823f..7b59128878 100644
--- a/src/backend/utils/misc/sampling.c
+++ b/src/backend/utils/misc/sampling.c
@@ -49,7 +49,7 @@ BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize,
 	bs->t = 0;					/* blocks scanned so far */
 	bs->m = 0;					/* blocks selected so far */
 
-	sampler_random_init_state(randseed, bs->randstate);
+	sampler_random_init_state(randseed, &bs->randstate);
 
 	return Min(bs->n, bs->N);
 }
@@ -98,7 +98,7 @@ BlockSampler_Next(BlockSampler bs)
 	 * less than k, which means that we cannot fail to select enough blocks.
 	 *----------
 	 */
-	V = sampler_random_fract(bs->randstate);
+	V = sampler_random_fract(&bs->randstate);
 	p = 1.0 - (double) k / (double) K;
 	while (V < p)
 	{
@@ -136,10 +136,10 @@ reservoir_init_selection_state(ReservoirState rs, int n)
 	 * Reservoir sampling is not used anywhere where it would need to return
 	 * repeatable results so we can initialize it randomly.
 	 */
-	sampler_random_init_state(random(), rs->randstate);
+	sampler_random_init_state(random(), &rs->randstate);
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	rs->W = exp(-log(sampler_random_fract(rs->randstate)) / n);
+	rs->W = exp(-log(sampler_random_fract(&rs->randstate)) / n);
 }
 
 double
@@ -154,7 +154,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 		double		V,
 					quot;
 
-		V = sampler_random_fract(rs->randstate);	/* Generate V */
+		V = sampler_random_fract(&rs->randstate);	/* Generate V */
 		S = 0;
 		t += 1;
 		/* Note: "num" in Vitter's code is always equal to t - n */
@@ -186,7 +186,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 						tmp;
 
 			/* Generate U and X */
-			U = sampler_random_fract(rs->randstate);
+			U = sampler_random_fract(&rs->randstate);
 			X = t * (W - 1.0);
 			S = floor(X);		/* S is tentatively set to floor(X) */
 			/* Test if U <= h(S)/cg(X) in the manner of (6.3) */
@@ -215,7 +215,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 				y *= numer / denom;
 				denom -= 1;
 			}
-			W = exp(-log(sampler_random_fract(rs->randstate)) / n); /* Generate W in advance */
+			W = exp(-log(sampler_random_fract(&rs->randstate)) / n); /* Generate W in advance */
 			if (exp(log(y) / n) <= (t + X) / t)
 				break;
 		}
@@ -230,24 +230,22 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
  *----------
  */
 void
-sampler_random_init_state(long seed, SamplerRandomState randstate)
+sampler_random_init_state(long seed, pg_prng_state *randstate)
 {
-	randstate[0] = 0x330e;		/* same as pg_erand48, but could be anything */
-	randstate[1] = (unsigned short) seed;
-	randstate[2] = (unsigned short) (seed >> 16);
+	pg_prng_seed(randstate, (uint64_t) seed);
 }
 
 /* Select a random value R uniformly distributed in (0 - 1) */
 double
-sampler_random_fract(SamplerRandomState randstate)
+sampler_random_fract(pg_prng_state *randstate)
 {
 	double		res;
 
-	/* pg_erand48 returns a value in [0.0 - 1.0), so we must reject 0 */
+	/* pg_prng_f64 returned value in [0.0 - 1.0), so we must reject 0.0 */
 	do
 	{
-		res = pg_erand48(randstate);
-	} while (res == 0.0);
+		res = pg_prng_f64(randstate);
+	} while (unlikely(res == 0.0));
 	return res;
 }
 
@@ -266,22 +264,28 @@ double
 anl_random_fract(void)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(random(), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* and compute a random fraction */
-	return sampler_random_fract(oldrs.randstate);
+	return sampler_random_fract(&oldrs.randstate);
 }
 
 double
 anl_init_selection_state(int n)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(random(), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	return exp(-log(sampler_random_fract(oldrs.randstate)) / n);
+	return exp(-log(sampler_random_fract(&oldrs.randstate)) / n);
 }
 
 double
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 1ed4808d53..af9f5169e9 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -66,6 +66,7 @@
 #include "common/file_perm.h"
 #include "common/file_utils.h"
 #include "common/logging.h"
+#include "common/pg_prng.h"
 #include "common/restricted_token.h"
 #include "common/string.h"
 #include "common/username.h"
@@ -880,9 +881,10 @@ choose_dsm_implementation(void)
 {
 #ifdef HAVE_SHM_OPEN
 	int			ntries = 10;
+	pg_prng_state	prng_state;
 
-	/* Initialize random(); this function is its only user in this program. */
-	srandom((unsigned int) (getpid() ^ time(NULL)));
+	/* Initialize pgng state this function is its only user in this program. */
+	pg_prng_seed(&prng_state, ((unsigned int) (getpid() ^ time(NULL))));
 
 	while (ntries > 0)
 	{
@@ -890,7 +892,7 @@ choose_dsm_implementation(void)
 		char		name[64];
 		int			fd;
 
-		handle = random();
+		handle = pg_prng_u32(&prng_state);
 		snprintf(name, 64, "/PostgreSQL.%u", handle);
 		if ((fd = shm_open(name, O_CREAT | O_RDWR | O_EXCL, 0600)) != -1)
 		{
diff --git a/src/bin/pg_test_fsync/pg_test_fsync.c b/src/bin/pg_test_fsync/pg_test_fsync.c
index fef31844fa..5b3c197aa6 100644
--- a/src/bin/pg_test_fsync/pg_test_fsync.c
+++ b/src/bin/pg_test_fsync/pg_test_fsync.c
@@ -15,6 +15,7 @@
 
 #include "access/xlogdefs.h"
 #include "common/logging.h"
+#include "common/pg_prng.h"
 #include "getopt_long.h"
 
 /*
@@ -71,6 +72,7 @@ static char full_buf[DEFAULT_XLOG_SEG_SIZE],
 static struct timeval start_t,
 			stop_t;
 static bool alarm_triggered = false;
+static pg_prng_state	prng_state;
 
 
 static void handle_args(int argc, char *argv[]);
@@ -117,6 +119,7 @@ main(int argc, char *argv[])
 	pqsignal(SIGHUP, signal_cleanup);
 #endif
 
+	pg_prng_strong_seed(&prng_state);
 	prepare_buf();
 
 	test_open();
@@ -233,7 +236,7 @@ prepare_buf(void)
 
 	/* write random data into buffer */
 	for (ops = 0; ops < DEFAULT_XLOG_SEG_SIZE; ops++)
-		full_buf[ops] = random();
+		full_buf[ops] = pg_prng_u32(&prng_state);
 
 	buf = (char *) TYPEALIGN(XLOG_BLCKSZ, full_buf);
 }
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 433abd954b..64c7370ff5 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -58,6 +58,7 @@
 #endif
 
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/logging.h"
 #include "common/string.h"
 #include "common/username.h"
@@ -350,16 +351,8 @@ typedef struct StatsData
  */
 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. */
-static RandomState base_random_sequence;
+static pg_prng_state	base_random_sequence;
 
 /* Synchronization barrier for start and connection */
 static THREAD_BARRIER_T barrier;
@@ -461,7 +454,7 @@ typedef struct
 	 * Separate randomness for each client. This is used for random functions
 	 * 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			command;		/* command number in script */
@@ -498,9 +491,9 @@ typedef struct
 	 * random state to make all of them independent of each other and
 	 * therefore deterministic at the thread level.
 	 */
-	RandomState ts_choose_rs;	/* random state for selecting a script */
-	RandomState ts_throttle_rs; /* random state for transaction throttling */
-	RandomState ts_sample_rs;	/* random state for log sampling */
+	pg_prng_state	ts_choose_rs;	/* random state for selecting a script */
+	pg_prng_state	ts_throttle_rs; /* random state for transaction throttling */
+	pg_prng_state	ts_sample_rs;	/* random state for log sampling */
 
 	int64		throttle_trigger;	/* previous/next throttling (us) */
 	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.
  */
 static void
-initRandomState(RandomState *random_state)
+initRandomState(pg_prng_state *state)
 {
-	random_state->xseed[0] = (unsigned short)
-		(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);
+	pg_prng_seed(state, pg_prng_u64(&base_random_sequence));
 }
 
+
 /*
- * Random number generator: uniform distribution from min to max inclusive.
+ * PRNG: uniform distribution from min to max inclusive.
  *
  * Although the limits are expressed as int64, you can't generate the full
  * int64 range in one call, because the difference of the limits mustn't
- * overflow int64.  In practice it's unwise to ask for more than an int32
- * range, because of the limited precision of pg_erand48().
+ * overflow int64. This is not checked.
  */
 static int64
-getrand(RandomState *random_state, int64 min, int64 max)
+getrand(pg_prng_state *state, int64 min, int64 max)
 {
-	/*
-	 * 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));
+	return min + (int64) pg_prng_u64_range(state, 0, max - min);
 }
 
 /*
@@ -942,8 +921,7 @@ getrand(RandomState *random_state, int64 min, int64 max)
  * value is exp(-parameter).
  */
 static int64
-getExponentialRand(RandomState *random_state, int64 min, int64 max,
-				   double parameter)
+getExponentialRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		cut,
 				uniform,
@@ -953,7 +931,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 	Assert(parameter > 0.0);
 	cut = exp(-parameter);
 	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	uniform = 1.0 - pg_prng_f64(state);
 
 	/*
 	 * inner expression in (cut, 1] (if parameter > 0), rand in [0, 1)
@@ -966,8 +944,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 
 /* random number generator: gaussian distribution from min to max inclusive */
 static int64
-getGaussianRand(RandomState *random_state, int64 min, int64 max,
-				double parameter)
+getGaussianRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		stdev;
 	double		rand;
@@ -990,13 +967,13 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
 	do
 	{
 		/*
-		 * pg_erand48 generates [0,1), but for the basic version of the
+		 * pg_prng_f64 generates [0,1), but for the basic version of the
 		 * Box-Muller transform the two uniformly distributed random numbers
 		 * are expected in (0, 1] (see
 		 * https://en.wikipedia.org/wiki/Box-Muller_transform)
 		 */
-		double		rand1 = 1.0 - pg_erand48(random_state->xseed);
-		double		rand2 = 1.0 - pg_erand48(random_state->xseed);
+		double		rand1 = 1.0 - pg_prng_f64(state);
+		double		rand2 = 1.0 - pg_prng_f64(state);
 
 		/* Box-Muller basic form transform */
 		double		var_sqrt = sqrt(-2.0 * log(rand1));
@@ -1026,7 +1003,7 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
  * not be one.
  */
 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
@@ -1034,8 +1011,8 @@ getPoissonRand(RandomState *random_state, double center)
 	 */
 	double		uniform;
 
-	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	/* pseudo-random value in [0, 1), uniform in (0, 1] */
+	uniform = 1.0 - pg_prng_f64(state);
 
 	return (int64) (-log(uniform) * center + 0.5);
 }
@@ -1048,7 +1025,7 @@ getPoissonRand(RandomState *random_state, double center)
  * This works for s > 1.0, but may perform badly for s very close to 1.0.
  */
 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		x,
@@ -1063,8 +1040,8 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 	while (true)
 	{
 		/* random variates */
-		u = pg_erand48(random_state->xseed);
-		v = pg_erand48(random_state->xseed);
+		u = pg_prng_f64(state);
+		v = pg_prng_f64(state);
 
 		x = floor(pow(u, -1.0 / (s - 1.0)));
 
@@ -1078,14 +1055,14 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 
 /* random number generator: zipfian distribution from min to max inclusive */
 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;
 
 	/* abort if parameter is invalid */
 	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 +1119,7 @@ getHashMurmur2(int64 val, uint64 seed)
  * For small sizes, this generates each of the (size!) possible permutations
  * of integers in the range [0, size) with roughly equal probability.  Once
  * the size is larger than 20, the number of possible permutations exceeds the
- * number of distinct states of the internal pseudorandom number generators,
+ * number of distinct states of the internal pseudorandom number generator,
  * and so not all possible permutations can be generated, but the permutations
  * chosen should continue to give the appearance of being random.
  *
@@ -1152,25 +1129,19 @@ getHashMurmur2(int64 val, uint64 seed)
 static int64
 permute(const int64 val, const int64 isize, const int64 seed)
 {
-	RandomState random_state1;
-	RandomState random_state2;
-	uint64		size;
-	uint64		v;
-	int			masklen;
-	uint64		mask;
-	int			i;
+	/* using a high-end PRNG is probably overkill */
+	pg_prng_state	state;
+	uint64			size;
+	uint64			v;
+	int				masklen;
+	uint64			mask;
+	int				i;
 
 	if (isize < 2)
 		return 0;				/* nothing to permute */
 
-	/* Initialize a pair of random states using the seed */
-	random_state1.xseed[0] = seed & 0xFFFF;
-	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;
+	/* Initialize prng state using the seed */
+	pg_prng_seed(&state, (uint64) seed);
 
 	/* Computations are performed on unsigned values */
 	size = (uint64) isize;
@@ -1216,8 +1187,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 					t;
 
 		/* Random multiply (by an odd number), XOR and rotate of lower half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		if (v <= mask)
 		{
 			v = ((v * m) ^ r) & mask;
@@ -1225,8 +1196,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random multiply (by an odd number), XOR and rotate of upper half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		t = size - 1 - v;
 		if (t <= mask)
 		{
@@ -1236,7 +1207,7 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random offset */
-		r = (uint64) getrand(&random_state2, 0, size - 1);
+		r = pg_prng_u64(&state) % (size - 1);
 		v = (v + r) % size;
 	}
 
@@ -3827,7 +3798,7 @@ doLog(TState *thread, CState *st,
 	 * to the random sample.
 	 */
 	if (sample_rate != 0.0 &&
-		pg_erand48(thread->ts_sample_rs.xseed) > sample_rate)
+		pg_prng_f64(&thread->ts_sample_rs) > sample_rate)
 		return;
 
 	/* should we aggregate the results or not? */
@@ -5763,12 +5734,11 @@ set_random_seed(const char *seed)
 
 	if (seed != NULL)
 		pg_log_info("setting random seed to %llu", (unsigned long long) iseed);
+
 	random_seed = iseed;
 
 	/* Fill base_random_sequence with low-order bits of seed */
-	base_random_sequence.xseed[0] = iseed & 0xFFFF;
-	base_random_sequence.xseed[1] = (iseed >> 16) & 0xFFFF;
-	base_random_sequence.xseed[2] = (iseed >> 32) & 0xFFFF;
+	pg_prng_seed(&base_random_sequence, (uint64_t) iseed);
 
 	return true;
 }
@@ -6439,9 +6409,7 @@ main(int argc, char **argv)
 	/* set default seed for hash functions */
 	if (lookupVariable(&state[0], "default_seed") == NULL)
 	{
-		uint64		seed =
-		((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) |
-		(((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) << 32);
+		uint64		seed = pg_prng_u64(&base_random_sequence);
 
 		for (i = 0; i < nclients; i++)
 			if (!putVariableInt(&state[i], "startup", "default_seed", (int64) seed))
diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl
index ef53f6b2d9..13c9204a3d 100644
--- a/src/bin/pgbench/t/001_pgbench_with_server.pl
+++ b/src/bin/pgbench/t/001_pgbench_with_server.pl
@@ -427,9 +427,9 @@ pgbench(
 
 		# After explicit seeding, the four random checks (1-3,20) are
 		# deterministic
-		qr{command=1.: int 13\b},      # uniform random
-		qr{command=2.: int 116\b},     # exponential random
-		qr{command=3.: int 1498\b},    # gaussian random
+		qr{command=1.: int 17\b},      # uniform random
+		qr{command=2.: int 108\b},     # exponential random
+		qr{command=3.: int 1452\b},    # gaussian random
 		qr{command=4.: int 4\b},
 		qr{command=5.: int 5\b},
 		qr{command=6.: int 6\b},
@@ -496,6 +496,7 @@ pgbench(
 		qr{command=109.: boolean true\b},
 		qr{command=110.: boolean true\b},
 		qr{command=111.: boolean true\b},
+		qr{command=113.: boolean true\b},
 	],
 	'pgbench expressions',
 	{
@@ -639,8 +640,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))
 -- actual values
 \set t debug(permute(:v, 1) = 0)
-\set t debug(permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1 and \
-             permute(0, 2, 5435) = 1 and permute(1, 2, 5435) = 0)
+\set t debug(permute(0, 2, 5431) = 1 and permute(1, 2, 5431) = 0 and \
+             permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1)
+-- 63 bits tests for checking permute portability across architectures
+\set size debug(:max - 10)
+\set t debug(permute(:size-1, :size, 5432) = 4352241160009873020 and \
+             permute(:size-2, :size, 5432) = 4990004119691638710 and \
+             permute(:size-3, :size, 5432) = 5868029867256127549 and \
+             permute(:size-4, :size, 5432) = 1238830324886341378 and \
+             permute(:size-5, :size, 5432) = 7415914367102906897 and \
+             permute(:size-6, :size, 5432) = 3214501037984818995 and \
+             permute(:size-7, :size, 5432) =  600660351261124695)
 }
 	});
 
diff --git a/src/common/Makefile b/src/common/Makefile
index 880722fcf5..31c0dd366d 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -66,6 +66,7 @@ OBJS_COMMON = \
 	md5_common.o \
 	pg_get_line.o \
 	pg_lzcompress.o \
+	pg_prng.o \
 	pgfnames.o \
 	psprintf.o \
 	relpath.o \
diff --git a/src/common/pg_prng.c b/src/common/pg_prng.c
new file mode 100644
index 0000000000..df2054af79
--- /dev/null
+++ b/src/common/pg_prng.c
@@ -0,0 +1,244 @@
+/*
+ * Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * Previous version was the 1988 (?) rand48 standard with 48 bits state LCG,
+ * designed for running on 32 bits or even 16 bits architectures, and to produce
+ * 32 bit ints and floats (i.e. *not* doubles).
+ *
+ * The implied 16-bits unpacking/packing may have a detrimental performance impact
+ * on modern 64 bits architectures.
+ *
+ * We (probably) want:
+ * - one reasonable default PRNG for all pg internal uses.
+ * - NOT to invent a new design!
+ * - something fast, close to rand48 (which basically does 2 arithmetic ops)
+ *   no need for something cryptographic though, which would imply slow
+ * - to produce 64 bits integers & doubles with a 52 bits mantissa
+ *   so state size > 64 bits.
+ * - a small state though, because we might generate quite a few of them
+ *   for different purposes so state size <= 256 or even <= 128 bits
+ * - the state to be aligned to whatever, so state size = 128 bits
+ * - 64 bits operations for efficiency on modern architectures,
+ *   but NOT 128 bits operations.
+ * - not to depend on special hardware for speed (eg MMX/SSE/AES).
+ * - not something with obvious known and relevant defects.
+ * - not something with "rights" attached.
+ *
+ * These constraints reduce a lot the available options from
+ * https://en.wikipedia.org/wiki/List_of_random_number_generators
+ *
+ * LCG
+ * - POSIX rand48: m = 2^48, a = 25214903917, c = 11, extract 47...16
+ * - MMIX by Donald Knuth: m = 2^64, a = 6364136223846793005, c = 1442695040888963407
+ * - very bad on low bits, which MUST NOT BE USED
+ *   a 128 bits would do, but no clear standards, and it would imply 128-bit ops,
+ *   see PCG below.
+ *
+ * PCG Family (https://www.pcg-random.org/)
+ * - this is basically an LCG with an improved outpout function
+ *   so as to avoid typical LCG issues and pass stats tests.
+ * - recommands state size to be twice the output size,
+ *   which seems a reasonable requirement.
+ * - this implies 128 bit ops for our target parameters,
+ *   otherwise it is a fairly good candidate for simplicity,
+ *   efficiency and quality.
+ *
+ * Xor-Shift-Rotate PRNGs:
+ * - a generic annoying feature of this generator class is that there is one
+ *   special all-zero state which must not be used, inducing a constraint on
+ *   initialization, and when the generator get close to it (i.e. many zeros
+ *   in the state) it takes some iterations to recover, with small lumps
+ *   of close/related/bad numbers generated in the process.
+ * Xoroshiro128+
+ * - fails statistical linearity tests in the low bits.
+ * Xoroshiro128**
+ *   https://prng.di.unimi.it/xoroshiro128starstar.c
+ * - mild and irrelevant issues: close to zero state, stats issues after some transforms
+ * Xoshiro256**
+ * - it looks good, but some issues (no big deal for pg usage IMO)
+ *   https://www.pcg-random.org/posts/a-quick-look-at-xoshiro256.html
+ *   mild problems getting out of closes-to-zero state
+ *
+ * MT19937-64
+ * + default in many places, standard
+ * - huge 2.5 KiB state (but TinyMT, 127 bits state space)
+ * - slow (but SFMT, twice as fast)
+ * - vulnerabilities on tinyMT?
+ *
+ * WELL
+ * - large state, 32-bit oriented
+ *
+ * ACORN Family (https://en.wikipedia.org/wiki/ACORN_(PRNG))
+ * - Fortran / double oriented, no C implementation found?!
+ * - medium large state "In practice we recommend using k > 10, M = 2^60 (for general application)
+ *   or M = 2^120 (for demanding applications requiring high-quality pseudo-random numbers
+ *   that will consistently pass all the tests in standard test packages such as TestU01)
+ *   and choose any odd value less than M for the seed."
+ *   k = 11 => 88 bytes/704-bits state, too large:-(
+ *
+ * Splitmix?
+ * Others?
+ *
+ * The conclusion is that we use xor-shift-rotate generator below.
+ */
+
+#include "c.h"
+#include "common/pg_prng.h"
+#include "port/pg_bitutils.h"
+#include <math.h>
+
+/* global state */
+pg_prng_state	pg_global_prng_state;
+
+#define FIRST_BIT_MASK UINT64CONST(0x8000000000000000)
+#define RIGHT_HALF_MASK UINT64CONST(0x00000000FFFFFFFF)
+#define DMANTISSA_MASK UINT64CONST(0x000FFFFFFFFFFFFF)
+
+/* 64-bits rotate left */
+static inline uint64
+rotl(const uint64 x, const int bits)
+{
+	return (x << bits) | (x >> (64 - bits));
+}
+
+/* another 64-bits generator is used for state initialization or iterations */
+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);
+}
+
+/* seed prng state from a 64 bits integer, ensuring non zero */
+void
+pg_prng_seed(pg_prng_state *state, uint64 seed)
+{
+	state->s0 = splitmix64(&seed);
+	state->s1 = splitmix64(&seed);
+}
+
+/* seed with 53 bits (mantissa & sign) from a float */
+void
+pg_prng_fseed(pg_prng_state *state, double fseed)
+{
+	uint64 seed = (int64) (((double) DMANTISSA_MASK) * fseed);
+	pg_prng_seed(state, seed);
+}
+
+/* strong random seeding */
+bool
+pg_prng_strong_seed(pg_prng_state *state)
+{
+	bool ok = pg_strong_random((void *) state, sizeof(pg_prng_state));
+
+	/* avoid zero with Donald Knuth's LCG parameters */
+	if (unlikely(state->s0 == 0 && state->s1 == 0))
+	{
+		/* should it warn that something is amiss if we get there? */
+		pg_prng_state def = PG_PRNG_DEFAULT_STATE;
+		*state = def;
+	}
+
+	return ok;
+}
+
+/* generator & state update */
+static uint64
+xoroshiro128ss(pg_prng_state *state)
+{
+	const 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;
+}
+
+/* u64 generator */
+uint64
+pg_prng_u64(pg_prng_state *state)
+{
+	return xoroshiro128ss(state);
+}
+
+/*
+ * select in a range with bitmask rejection.
+ *
+ * if range is empty, rmin is returned.
+ *
+ * the prng may advance by several states or none,
+ * depending on the range value.
+ */
+uint64
+pg_prng_u64_range(pg_prng_state *state, uint64 rmin, uint64 rmax)
+{
+	uint64 val;
+
+	if (likely(rmax > rmin))
+	{
+		uint64 range = rmax - rmin;
+
+		/* bit position is between 0 and 63, so rshift >= 0 */
+		uint32 rshift = 63 - pg_leftmost_one_pos64(range);
+
+		do
+		{
+			val = xoroshiro128ss(state) >> rshift;
+		}
+		while (unlikely(val > range));
+	}
+	else
+		val = 0;
+
+	return rmin + val;
+}
+
+/* i64 generator */
+int64
+pg_prng_i64(pg_prng_state *state)
+{
+	return (int64) xoroshiro128ss(state);
+}
+
+/* u32 generator */
+uint32
+pg_prng_u32(pg_prng_state *state)
+{
+	const uint64 v = xoroshiro128ss(state);
+	return (uint32) (((v >> 32) ^ v) & RIGHT_HALF_MASK);
+}
+
+/* i32 generator */
+int32
+pg_prng_i32(pg_prng_state *state)
+{
+	const uint64 v = xoroshiro128ss(state);
+	return (int32) (((v >> 32) ^ v) & RIGHT_HALF_MASK);
+}
+
+/* double generator */
+double
+pg_prng_f64(pg_prng_state *state)
+{
+	uint64 v = xoroshiro128ss(state);
+	return ldexp((double) (v & DMANTISSA_MASK), -52);
+}
+
+/* bool generator */
+bool
+pg_prng_bool(pg_prng_state *state)
+{
+	uint64 v = xoroshiro128ss(state);
+	return (v & FIRST_BIT_MASK) == FIRST_BIT_MASK;
+}
diff --git a/src/include/common/pg_prng.h b/src/include/common/pg_prng.h
new file mode 100644
index 0000000000..55f0e3d6cb
--- /dev/null
+++ b/src/include/common/pg_prng.h
@@ -0,0 +1,37 @@
+/*-------------------------------------------------------------------------
+ *
+ * PRNG: internal Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * src/include/common/pg_prng.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_PRNG_H
+#define PG_PRNG_H
+
+/* 128 bits state */
+typedef struct pg_prng_state {
+	uint64	s0, s1;
+} pg_prng_state;
+
+/* global pseudo-random state */
+extern pg_prng_state	pg_global_prng_state;
+
+/* use Donald Knuth's LCG constants for default state */
+#define PG_PRNG_DEFAULT_STATE \
+	{ UINT64CONST(0x5851F42D4C957F2D), UINT64CONST(0x14057B7EF767814F) }
+
+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_strong_seed(pg_prng_state *state);
+extern uint64 pg_prng_u64(pg_prng_state *state);
+extern uint64 pg_prng_u64_range(pg_prng_state *state, uint64 rmin, uint64 rmax);
+extern int64 pg_prng_i64(pg_prng_state *state);
+extern uint32 pg_prng_u32(pg_prng_state *state);
+extern int32 pg_prng_i32(pg_prng_state *state);
+extern double pg_prng_f64(pg_prng_state *state);
+extern bool pg_prng_bool(pg_prng_state *state);
+
+#endif							/* PG_PRNG_H */
diff --git a/src/include/optimizer/geqo.h b/src/include/optimizer/geqo.h
index 24dcdfb6cc..b28e4f084a 100644
--- a/src/include/optimizer/geqo.h
+++ b/src/include/optimizer/geqo.h
@@ -22,6 +22,7 @@
 #ifndef GEQO_H
 #define GEQO_H
 
+#include "common/pg_prng.h"
 #include "nodes/pathnodes.h"
 #include "optimizer/geqo_gene.h"
 
@@ -72,8 +73,8 @@ extern double Geqo_seed;		/* 0 .. 1 */
  */
 typedef struct
 {
-	List	   *initial_rels;	/* the base relations we are joining */
-	unsigned short random_state[3]; /* state for pg_erand48() */
+	List		   *initial_rels;	/* the base relations we are joining */
+	pg_prng_state	random_state;   /* PRNG state */
 } GeqoPrivateData;
 
 
diff --git a/src/include/port.h b/src/include/port.h
index 82f63de325..eb18665196 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -356,11 +356,6 @@ extern int	gettimeofday(struct timeval *tp, struct timezone *tzp);
 #define pgoff_t off_t
 #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
 extern int	fls(int mask);
 #endif
diff --git a/src/include/utils/sampling.h b/src/include/utils/sampling.h
index a58d14281b..c0b1c6a645 100644
--- a/src/include/utils/sampling.h
+++ b/src/include/utils/sampling.h
@@ -13,15 +13,14 @@
 #ifndef SAMPLING_H
 #define SAMPLING_H
 
+#include "common/pg_prng.h"
 #include "storage/block.h"		/* for typedef BlockNumber */
 
 
 /* Random generator for sampling code */
-typedef unsigned short SamplerRandomState[3];
-
 extern void sampler_random_init_state(long seed,
-									  SamplerRandomState randstate);
-extern double sampler_random_fract(SamplerRandomState randstate);
+									  pg_prng_state *randstate);
+extern double sampler_random_fract(pg_prng_state *randstate);
 
 /* Block sampling methods */
 
@@ -32,7 +31,7 @@ typedef struct
 	int			n;				/* desired sample size */
 	BlockNumber t;				/* current block number */
 	int			m;				/* blocks selected so far */
-	SamplerRandomState randstate;	/* random generator state */
+	pg_prng_state randstate;	/* random generator state */
 } BlockSamplerData;
 
 typedef BlockSamplerData *BlockSampler;
@@ -46,8 +45,9 @@ extern BlockNumber BlockSampler_Next(BlockSampler bs);
 
 typedef struct
 {
-	double		W;
-	SamplerRandomState randstate;	/* random generator state */
+	double			W;
+	pg_prng_state	randstate;	/* random generator state */
+	bool			randstate_initialized;
 } ReservoirStateData;
 
 typedef ReservoirStateData *ReservoirState;
diff --git a/src/port/Makefile b/src/port/Makefile
index 52dbf5783f..b3754d8940 100644
--- a/src/port/Makefile
+++ b/src/port/Makefile
@@ -42,7 +42,6 @@ OBJS = \
 	$(PG_CRC32C_OBJS) \
 	bsearch_arg.o \
 	chklocale.o \
-	erand48.o \
 	inet_net_ntop.o \
 	noblock.o \
 	path.o \
diff --git a/src/port/erand48.c b/src/port/erand48.c
deleted file mode 100644
index a82ea94220..0000000000
--- a/src/port/erand48.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/port/random.c b/src/port/random.c
deleted file mode 100644
index 2dd59a0829..0000000000
--- a/src/port/random.c
+++ /dev/null
@@ -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();
-}
diff --git a/src/port/srandom.c b/src/port/srandom.c
deleted file mode 100644
index cf1007b2ef..0000000000
--- a/src/port/srandom.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/test/modules/test_integerset/test_integerset.c b/src/test/modules/test_integerset/test_integerset.c
index 21c6f49b37..3e8cb7a005 100644
--- a/src/test/modules/test_integerset/test_integerset.c
+++ b/src/test/modules/test_integerset/test_integerset.c
@@ -12,6 +12,7 @@
  */
 #include "postgres.h"
 
+#include "common/pg_prng.h"
 #include "fmgr.h"
 #include "lib/integerset.h"
 #include "miscadmin.h"
@@ -99,12 +100,16 @@ static void check_with_filler(IntegerSet *intset, uint64 x, uint64 value, uint64
 static void test_single_value_and_filler(uint64 value, uint64 filler_min, uint64 filler_max);
 static void test_huge_distances(void);
 
+static pg_prng_state	pr_state;
+
 /*
  * SQL-callable entry point to perform all tests.
  */
 Datum
 test_integerset(PG_FUNCTION_ARGS)
 {
+	pg_prng_strong_seed(&pr_state);
+
 	/* Tests for various corner cases */
 	test_empty();
 	test_huge_distances();
@@ -248,8 +253,7 @@ test_pattern(const test_spec *spec)
 		 * only a small part of the integer space is used.  We would very
 		 * rarely hit values that are actually in the set.
 		 */
-		x = (pg_lrand48() << 31) | pg_lrand48();
-		x = x % (last_int + 1000);
+		x = pg_prng_u64(&pr_state) % (last_int + 1000);
 
 		/* Do we expect this value to be present in the set? */
 		if (x >= last_int)
@@ -571,7 +575,7 @@ test_huge_distances(void)
 	 */
 	while (num_values < 1000)
 	{
-		val += pg_lrand48();
+		val += pg_prng_u32(&pr_state);
 		values[num_values++] = val;
 	}
 
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 4362bd44fd..aa048d949f 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -99,9 +99,9 @@ sub mkvcbuild
 	$solution = CreateSolution($vsVersion, $config);
 
 	our @pgportfiles = qw(
-	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c random.c
-	  srandom.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
+	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c
+	  getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
+	  snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
 	  dirent.c dlopen.c getopt.c getopt_long.c link.c
 	  pread.c preadv.c pwrite.c pwritev.c pg_bitutils.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
 	  f2s.c file_perm.c file_utils.c hashfn.c ip.c jsonapi.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
-	  saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c username.c
-	  wait_error.c wchar.c);
+	  pg_get_line.c pg_lzcompress.c pg_prng.c pgfnames.c psprintf.c relpath.c
+	  rmtree.c saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c
+	  username.c wait_error.c wchar.c);
 
 	if ($solution->{options}->{openssl})
 	{
diff --git a/src/tools/testint128.c b/src/tools/testint128.c
index 71c345969a..43d569aeb5 100644
--- a/src/tools/testint128.c
+++ b/src/tools/testint128.c
@@ -27,6 +27,7 @@
 #endif
 
 #include "common/int128.h"
+#include "common/pg_prng.h"
 
 /*
  * We assume the parts of this union are laid out compatibly.
@@ -61,27 +62,11 @@ my_int128_compare(int128 x, int128 y)
 	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.
  *
  * 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 fix the prng state (hmmm...).
  *
  * 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;
 
+	pg_prng_seed(&pg_global_prng_state, 0);
+
 	if (argc >= 2)
 		count = strtol(argv[1], NULL, 0);
 	else
@@ -97,9 +84,9 @@ main(int argc, char **argv)
 
 	while (count-- > 0)
 	{
-		int64		x = get_random_uint64();
-		int64		y = get_random_uint64();
-		int64		z = get_random_uint64();
+		int64		x = pg_prng_u64(&pg_global_prng_state);
+		int64		y = pg_prng_u64(&pg_global_prng_state);
+		int64		z = pg_prng_u64(&pg_global_prng_state);
 		test128		t1;
 		test128		t2;
 
#43Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Fabien COELHO (#42)
1 attachment(s)
Re: rand48 replacement

Hello again,

Just wanted to let you know that cfbot [1] doesn't seem to be happy with
the patch. Apparently, some tests are falling. To be honest, I didn't
invest too much time into investigating this. Hopefully, it's not a big
deal.

[1]: http://cfbot.cputube.org/

Indeed. I wish that these results would be available from the cf interface.

Attached a v11 which might improve things.

Not enough. Here is a v12 which might improve things further.

--
Fabien.

Attachments:

prng-12.patchtext/x-diff; name=prng-12.patchDownload
diff --git a/configure b/configure
index 7542fe30a1..7dc95e3960 100755
--- a/configure
+++ b/configure
@@ -800,6 +800,7 @@ infodir
 docdir
 oldincludedir
 includedir
+runstatedir
 localstatedir
 sharedstatedir
 sysconfdir
@@ -941,6 +942,7 @@ datadir='${datarootdir}'
 sysconfdir='${prefix}/etc'
 sharedstatedir='${prefix}/com'
 localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
 includedir='${prefix}/include'
 oldincludedir='/usr/include'
 docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@@ -1193,6 +1195,15 @@ do
   | -silent | --silent | --silen | --sile | --sil)
     silent=yes ;;
 
+  -runstatedir | --runstatedir | --runstatedi | --runstated \
+  | --runstate | --runstat | --runsta | --runst | --runs \
+  | --run | --ru | --r)
+    ac_prev=runstatedir ;;
+  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+  | --run=* | --ru=* | --r=*)
+    runstatedir=$ac_optarg ;;
+
   -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
     ac_prev=sbindir ;;
   -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1330,7 +1341,7 @@ fi
 for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
 		datadir sysconfdir sharedstatedir localstatedir includedir \
 		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
-		libdir localedir mandir
+		libdir localedir mandir runstatedir
 do
   eval ac_val=\$$ac_var
   # Remove trailing slashes.
@@ -1483,6 +1494,7 @@ Fine tuning of the installation directories:
   --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
   --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
   --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
   --libdir=DIR            object code libraries [EPREFIX/lib]
   --includedir=DIR        C header files [PREFIX/include]
   --oldincludedir=DIR     C header files for non-gcc [/usr/include]
@@ -15026,7 +15038,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -15072,7 +15084,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -15096,7 +15108,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -15141,7 +15153,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -15165,7 +15177,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -16301,32 +16313,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
diff --git a/configure.ac b/configure.ac
index ed3cdb9a8e..b36b32ed94 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1822,8 +1822,6 @@ AC_REPLACE_FUNCS(m4_normalize([
 	mkdtemp
 	pread
 	pwrite
-	random
-	srandom
 	strlcat
 	strlcpy
 	strnlen
diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c
index d19f73127c..b250ae912b 100644
--- a/contrib/amcheck/verify_nbtree.c
+++ b/contrib/amcheck/verify_nbtree.c
@@ -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"
@@ -465,8 +466,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();
+		/* Random seed relies on backend prng initialization to avoid repetition */
+		seed = pg_prng_u64(&pg_global_prng_state);
 		/* Create Bloom filter to fingerprint index */
 		state->filter = bloom_create(total_elems, maintenance_work_mem, seed);
 		state->heaptuplespresent = 0;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 2c2f149fb0..146b524076 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -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]);
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 76d4fea21c..c139382170 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -5157,7 +5157,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]);
 		}
diff --git a/contrib/tablefunc/tablefunc.c b/contrib/tablefunc/tablefunc.c
index 779bd4415e..fdd117f81e 100644
--- a/contrib/tablefunc/tablefunc.c
+++ b/contrib/tablefunc/tablefunc.c
@@ -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"
@@ -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_f64(&pg_global_prng_state);
+		u2 = pg_prng_f64(&pg_global_prng_state);
 
 		v1 = (2.0 * u1) - 1.0;
 		v2 = (2.0 * u2) - 1.0;
diff --git a/contrib/tsm_system_rows/tsm_system_rows.c b/contrib/tsm_system_rows/tsm_system_rows.c
index 4996612902..1a46d4b143 100644
--- a/contrib/tsm_system_rows/tsm_system_rows.c
+++ b/contrib/tsm_system_rows/tsm_system_rows.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/contrib/tsm_system_time/tsm_system_time.c b/contrib/tsm_system_time/tsm_system_time.c
index 788d8f9a68..36acc6c106 100644
--- a/contrib/tsm_system_time/tsm_system_time.c
+++ b/contrib/tsm_system_time/tsm_system_time.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index 03191e016c..3c2f24b653 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -16,6 +16,7 @@
 
 #include "access/gin_private.h"
 #include "access/relscan.h"
+#include "common/pg_prng.h"
 #include "miscadmin.h"
 #include "storage/predicate.h"
 #include "utils/datum.h"
@@ -787,7 +788,7 @@ entryLoadMoreItems(GinState *ginstate, GinScanEntry entry,
 	}
 }
 
-#define gin_rand() (((double) random()) / ((double) MAX_RANDOM_VALUE))
+#define gin_rand() pg_prng_f64(&pg_global_prng_state)
 #define dropItem(e) ( gin_rand() > ((double)GinFuzzySearchLimit)/((double)((e)->predictNumberResult)) )
 
 /*
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index 7355e1dba1..37d8840ce0 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -19,6 +19,7 @@
 #include "access/nbtxlog.h"
 #include "access/transam.h"
 #include "access/xloginsert.h"
+#include "common/pg_prng.h"
 #include "lib/qunique.h"
 #include "miscadmin.h"
 #include "storage/lmgr.h"
@@ -968,7 +969,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_f64(&pg_global_prng_state) <= 0.01)
 				break;
 
 			_bt_stepright(rel, insertstate, stack);
diff --git a/src/backend/access/spgist/spgdoinsert.c b/src/backend/access/spgist/spgdoinsert.c
index 70557bcf3d..5389c93366 100644
--- a/src/backend/access/spgist/spgdoinsert.c
+++ b/src/backend/access/spgist/spgdoinsert.c
@@ -19,6 +19,7 @@
 #include "access/spgist_private.h"
 #include "access/spgxlog.h"
 #include "access/xloginsert.h"
+#include "common/pg_prng.h"
 #include "miscadmin.h"
 #include "storage/bufmgr.h"
 #include "utils/rel.h"
@@ -2210,7 +2211,7 @@ 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_u32(&pg_global_prng_state) % innerTuple->nNodes;
 			}
 
 			switch (out.resultType)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 6597ec45a9..d0557be7c9 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -37,6 +37,7 @@
 #include "commands/async.h"
 #include "commands/tablecmds.h"
 #include "commands/trigger.h"
+#include "common/pg_prng.h"
 #include "executor/spi.h"
 #include "libpq/be-fsstubs.h"
 #include "libpq/pqsignal.h"
@@ -1939,7 +1940,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_f64(&pg_global_prng_state) <= log_xact_sample_rate);
 
 	/*
 	 * initialize current transaction state fields
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 8bfb2ad958..fe623393d7 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -38,6 +38,7 @@
 #include "commands/progress.h"
 #include "commands/tablecmds.h"
 #include "commands/vacuum.h"
+#include "common/pg_prng.h"
 #include "executor/executor.h"
 #include "foreign/fdwapi.h"
 #include "miscadmin.h"
@@ -1173,7 +1174,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 	OldestXmin = GetOldestNonRemovableTransactionId(onerel);
 
 	/* Prepare for sampling block numbers */
-	randseed = random();
+	randseed = pg_prng_i32(&pg_global_prng_state);
 	nblocks = BlockSampler_Init(&bs, totalblocks, targrows, randseed);
 
 #ifdef USE_PREFETCH
@@ -1290,7 +1291,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 					 * Found a suitable tuple, so save it, replacing one old
 					 * tuple at random
 					 */
-					int			k = (int) (targrows * sampler_random_fract(rstate.randstate));
+					int			k = (int) (targrows * sampler_random_fract(&rstate.randstate));
 
 					Assert(k >= 0 && k < targrows);
 					heap_freetuple(rows[k]);
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 44232d50d0..09c34e96fa 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -17,6 +17,7 @@
 #include "access/relscan.h"
 #include "access/tableam.h"
 #include "access/tsmapi.h"
+#include "common/pg_prng.h"
 #include "executor/executor.h"
 #include "executor/nodeSamplescan.h"
 #include "miscadmin.h"
@@ -154,7 +155,7 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	 * do this just once, since the seed shouldn't change over rescans.
 	 */
 	if (tsc->repeatable == NULL)
-		scanstate->seed = random();
+		scanstate->seed = pg_prng_i32(&pg_global_prng_state);
 
 	/*
 	 * Finally, initialize the TABLESAMPLE method handler.
diff --git a/src/backend/optimizer/geqo/geqo_random.c b/src/backend/optimizer/geqo/geqo_random.c
index f21bc047e6..8b42a9ffba 100644
--- a/src/backend/optimizer/geqo/geqo_random.c
+++ b/src/backend/optimizer/geqo/geqo_random.c
@@ -21,14 +21,7 @@ geqo_set_seed(PlannerInfo *root, double seed)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	/*
-	 * XXX. This seeding algorithm could certainly be improved - but it is not
-	 * critical to do so.
-	 */
-	memset(private->random_state, 0, sizeof(private->random_state));
-	memcpy(private->random_state,
-		   &seed,
-		   Min(sizeof(private->random_state), sizeof(seed)));
+	pg_prng_fseed(&private->random_state, seed);
 }
 
 double
@@ -36,5 +29,5 @@ geqo_rand(PlannerInfo *root)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	return pg_erand48(private->random_state);
+	return pg_prng_f64(&private->random_state);
 }
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index e2a76ba055..13df3cc2e4 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -98,6 +98,7 @@
 #include "catalog/pg_control.h"
 #include "common/file_perm.h"
 #include "common/ip.h"
+#include "common/pg_prng.h"
 #include "common/string.h"
 #include "lib/ilist.h"
 #include "libpq/auth.h"
@@ -2675,19 +2676,22 @@ ClosePostmasterPorts(bool am_syslogger)
 void
 InitProcessGlobals(void)
 {
-	unsigned int rseed;
-
 	MyProcPid = getpid();
 	MyStartTimestamp = GetCurrentTimestamp();
 	MyStartTime = timestamptz_to_time_t(MyStartTimestamp);
 
 	/*
-	 * Set a different seed for random() in every process.  We want something
+	 * Set a different global seed in every process.  We want something
 	 * unpredictable, so if possible, use high-quality random bits for the
 	 * seed.  Otherwise, fall back to a seed based on timestamp and PID.
 	 */
-	if (!pg_strong_random(&rseed, sizeof(rseed)))
+	if (!pg_prng_strong_seed(&pg_global_prng_state))
 	{
+		uint64	rseed;
+
+		ereport(WARNING,
+				(errmsg_internal("pg_prng_strong_seed() failed, falling back to weaker seeding")));
+
 		/*
 		 * Since PIDs and timestamps tend to change more frequently in their
 		 * least significant bits, shift the timestamp left to allow a larger
@@ -2698,8 +2702,9 @@ InitProcessGlobals(void)
 		rseed = ((uint64) MyProcPid) ^
 			((uint64) MyStartTimestamp << 12) ^
 			((uint64) MyStartTimestamp >> 20);
+
+		pg_prng_seed(&pg_global_prng_state, rseed);
 	}
-	srandom(rseed);
 }
 
 
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index f9cda6906d..431931bcfe 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -92,6 +92,7 @@
 #include "catalog/pg_tablespace.h"
 #include "common/file_perm.h"
 #include "common/file_utils.h"
+#include "common/pg_prng.h"
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "port/pg_iovec.h"
@@ -2938,7 +2939,7 @@ SetTempTablespaces(Oid *tableSpaces, int numSpaces)
 	 * available tablespaces.
 	 */
 	if (numSpaces > 1)
-		nextTempTableSpace = random() % numSpaces;
+		nextTempTableSpace = pg_prng_u32(&pg_global_prng_state) % numSpaces;
 	else
 		nextTempTableSpace = 0;
 }
diff --git a/src/backend/storage/ipc/dsm.c b/src/backend/storage/ipc/dsm.c
index b461a5f7e9..e97844614e 100644
--- a/src/backend/storage/ipc/dsm.c
+++ b/src/backend/storage/ipc/dsm.c
@@ -33,6 +33,7 @@
 #endif
 #include <sys/stat.h>
 
+#include "common/pg_prng.h"
 #include "lib/ilist.h"
 #include "miscadmin.h"
 #include "port/pg_bitutils.h"
@@ -180,7 +181,7 @@ dsm_postmaster_startup(PGShmemHeader *shim)
 	{
 		Assert(dsm_control_address == NULL);
 		Assert(dsm_control_mapped_size == 0);
-		dsm_control_handle = random() << 1; /* Even numbers only */
+		dsm_control_handle = pg_prng_u32(&pg_global_prng_state) << 1; /* Even numbers only */
 		if (dsm_control_handle == DSM_HANDLE_INVALID)
 			continue;
 		if (dsm_impl_op(DSM_OP_CREATE, dsm_control_handle, segsize,
@@ -536,7 +537,7 @@ dsm_create(Size size, int flags)
 		for (;;)
 		{
 			Assert(seg->mapped_address == NULL && seg->mapped_size == 0);
-			seg->handle = random() << 1;	/* Even numbers only */
+			seg->handle = pg_prng_u32(&pg_global_prng_state) << 1;	/* Even numbers only */
 			if (seg->handle == DSM_HANDLE_INVALID)	/* Reserve sentinel */
 				continue;
 			if (dsm_impl_op(DSM_OP_CREATE, seg->handle, size, &seg->impl_private,
@@ -1237,7 +1238,7 @@ make_main_region_dsm_handle(int slot)
 	 */
 	handle = 1;
 	handle |= slot << 1;
-	handle |= random() << (pg_leftmost_one_pos32(dsm_control->maxitems) + 1);
+	handle |= pg_prng_i32(&pg_global_prng_state) << (pg_leftmost_one_pos32(dsm_control->maxitems) + 1);
 	return handle;
 }
 
diff --git a/src/backend/storage/lmgr/s_lock.c b/src/backend/storage/lmgr/s_lock.c
index 91322a40c1..74c6f251ce 100644
--- a/src/backend/storage/lmgr/s_lock.c
+++ b/src/backend/storage/lmgr/s_lock.c
@@ -50,6 +50,7 @@
 #include <time.h>
 #include <unistd.h>
 
+#include "common/pg_prng.h"
 #include "port/atomics.h"
 #include "storage/s_lock.h"
 
@@ -63,6 +64,7 @@
 slock_t		dummy_spinlock;
 
 static int	spins_per_delay = DEFAULT_SPINS_PER_DELAY;
+static pg_prng_state	prng_state;
 
 
 /*
@@ -143,8 +145,7 @@ perform_spin_delay(SpinDelayStatus *status)
 #endif
 
 		/* increase delay by a random fraction between 1X and 2X */
-		status->cur_delay += (int) (status->cur_delay *
-									((double) random() / (double) MAX_RANDOM_VALUE) + 0.5);
+		status->cur_delay += (int) (status->cur_delay * (pg_prng_f64(&prng_state) + 0.5));
 		/* wrap back to minimum delay when max is exceeded */
 		if (status->cur_delay > MAX_DELAY_USEC)
 			status->cur_delay = MIN_DELAY_USEC;
@@ -303,7 +304,7 @@ volatile struct test_lock_struct test_lock;
 int
 main()
 {
-	srandom((unsigned int) time(NULL));
+	pg_prng_seed(&prng_state, (unsigned int) time(NULL));
 
 	test_lock.pad1 = test_lock.pad2 = 0x44;
 
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 0775abe35d..ef6c27dade 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -42,6 +42,7 @@
 #include "catalog/pg_type.h"
 #include "commands/async.h"
 #include "commands/prepare.h"
+#include "common/pg_prng.h"
 #include "executor/spi.h"
 #include "jit/jit.h"
 #include "libpq/libpq.h"
@@ -2355,13 +2356,13 @@ check_log_duration(char *msec_str, bool was_logged)
 
 		/*
 		 * Do not log if log_statement_sample_rate = 0. Log a sample if
-		 * log_statement_sample_rate <= 1 and avoid unnecessary random() call
+		 * log_statement_sample_rate <= 1 and avoid unnecessary random call
 		 * if log_statement_sample_rate = 1.
 		 */
 		if (exceeded_sample_duration)
 			in_sample = log_statement_sample_rate != 0 &&
 				(log_statement_sample_rate == 1 ||
-				 random() <= log_statement_sample_rate * MAX_RANDOM_VALUE);
+				 pg_prng_f64(&pg_global_prng_state) <= log_statement_sample_rate);
 
 		if (exceeded_duration || in_sample || log_duration || xact_is_sampled)
 		{
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 098bbb372b..9fc0b108dd 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -21,6 +21,7 @@
 
 #include "catalog/pg_type.h"
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/shortest_dec.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
@@ -65,7 +66,7 @@ float8		degree_c_one = 1.0;
 
 /* State for drandom() and setseed() */
 static bool drandom_seed_set = false;
-static unsigned short drandom_seed[3] = {0, 0, 0};
+static pg_prng_state drandom_seed = PG_PRNG_DEFAULT_STATE;
 
 /* Local function prototypes */
 static double sind_q1(double x);
@@ -2762,22 +2763,20 @@ drandom(PG_FUNCTION_ARGS)
 		 * Should that fail for some reason, we fall back on a lower-quality
 		 * seed based on current time and PID.
 		 */
-		if (!pg_strong_random(drandom_seed, sizeof(drandom_seed)))
+		if (!pg_strong_random(&drandom_seed, sizeof(drandom_seed)))
 		{
 			TimestampTz now = GetCurrentTimestamp();
 			uint64		iseed;
 
 			/* Mix the PID with the most predictable bits of the timestamp */
 			iseed = (uint64) now ^ ((uint64) MyProcPid << 32);
-			drandom_seed[0] = (unsigned short) iseed;
-			drandom_seed[1] = (unsigned short) (iseed >> 16);
-			drandom_seed[2] = (unsigned short) (iseed >> 32);
+			pg_prng_seed(&drandom_seed, iseed);
 		}
 		drandom_seed_set = true;
 	}
 
-	/* pg_erand48 produces desired result range [0.0 - 1.0) */
-	result = pg_erand48(drandom_seed);
+	/* produces desired result range [0.0 - 1.0) */
+	result = pg_prng_f64(&drandom_seed);
 
 	PG_RETURN_FLOAT8(result);
 }
@@ -2790,7 +2789,6 @@ Datum
 setseed(PG_FUNCTION_ARGS)
 {
 	float8		seed = PG_GETARG_FLOAT8(0);
-	uint64		iseed;
 
 	if (seed < -1 || seed > 1 || isnan(seed))
 		ereport(ERROR,
@@ -2798,11 +2796,7 @@ setseed(PG_FUNCTION_ARGS)
 				 errmsg("setseed parameter %g is out of allowed range [-1,1]",
 						seed)));
 
-	/* Use sign bit + 47 fractional bits to fill drandom_seed[] */
-	iseed = (int64) (seed * (float8) UINT64CONST(0x7FFFFFFFFFFF));
-	drandom_seed[0] = (unsigned short) iseed;
-	drandom_seed[1] = (unsigned short) (iseed >> 16);
-	drandom_seed[2] = (unsigned short) (iseed >> 32);
+	pg_prng_fseed(&drandom_seed, seed);
 	drandom_seed_set = true;
 
 	PG_RETURN_VOID();
diff --git a/src/backend/utils/misc/sampling.c b/src/backend/utils/misc/sampling.c
index 0c327e823f..f1c86150d8 100644
--- a/src/backend/utils/misc/sampling.c
+++ b/src/backend/utils/misc/sampling.c
@@ -49,7 +49,7 @@ BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize,
 	bs->t = 0;					/* blocks scanned so far */
 	bs->m = 0;					/* blocks selected so far */
 
-	sampler_random_init_state(randseed, bs->randstate);
+	sampler_random_init_state(randseed, &bs->randstate);
 
 	return Min(bs->n, bs->N);
 }
@@ -98,7 +98,7 @@ BlockSampler_Next(BlockSampler bs)
 	 * less than k, which means that we cannot fail to select enough blocks.
 	 *----------
 	 */
-	V = sampler_random_fract(bs->randstate);
+	V = sampler_random_fract(&bs->randstate);
 	p = 1.0 - (double) k / (double) K;
 	while (V < p)
 	{
@@ -136,10 +136,10 @@ reservoir_init_selection_state(ReservoirState rs, int n)
 	 * Reservoir sampling is not used anywhere where it would need to return
 	 * repeatable results so we can initialize it randomly.
 	 */
-	sampler_random_init_state(random(), rs->randstate);
+	sampler_random_init_state(pg_prng_u32(&pg_global_prng_state), &rs->randstate);
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	rs->W = exp(-log(sampler_random_fract(rs->randstate)) / n);
+	rs->W = exp(-log(sampler_random_fract(&rs->randstate)) / n);
 }
 
 double
@@ -154,7 +154,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 		double		V,
 					quot;
 
-		V = sampler_random_fract(rs->randstate);	/* Generate V */
+		V = sampler_random_fract(&rs->randstate);	/* Generate V */
 		S = 0;
 		t += 1;
 		/* Note: "num" in Vitter's code is always equal to t - n */
@@ -186,7 +186,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 						tmp;
 
 			/* Generate U and X */
-			U = sampler_random_fract(rs->randstate);
+			U = sampler_random_fract(&rs->randstate);
 			X = t * (W - 1.0);
 			S = floor(X);		/* S is tentatively set to floor(X) */
 			/* Test if U <= h(S)/cg(X) in the manner of (6.3) */
@@ -215,7 +215,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 				y *= numer / denom;
 				denom -= 1;
 			}
-			W = exp(-log(sampler_random_fract(rs->randstate)) / n); /* Generate W in advance */
+			W = exp(-log(sampler_random_fract(&rs->randstate)) / n); /* Generate W in advance */
 			if (exp(log(y) / n) <= (t + X) / t)
 				break;
 		}
@@ -230,24 +230,22 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
  *----------
  */
 void
-sampler_random_init_state(long seed, SamplerRandomState randstate)
+sampler_random_init_state(long seed, pg_prng_state *randstate)
 {
-	randstate[0] = 0x330e;		/* same as pg_erand48, but could be anything */
-	randstate[1] = (unsigned short) seed;
-	randstate[2] = (unsigned short) (seed >> 16);
+	pg_prng_seed(randstate, (uint64_t) seed);
 }
 
 /* Select a random value R uniformly distributed in (0 - 1) */
 double
-sampler_random_fract(SamplerRandomState randstate)
+sampler_random_fract(pg_prng_state *randstate)
 {
 	double		res;
 
-	/* pg_erand48 returns a value in [0.0 - 1.0), so we must reject 0 */
+	/* pg_prng_f64 returned value in [0.0 - 1.0), so we must reject 0.0 */
 	do
 	{
-		res = pg_erand48(randstate);
-	} while (res == 0.0);
+		res = pg_prng_f64(randstate);
+	} while (unlikely(res == 0.0));
 	return res;
 }
 
@@ -266,22 +264,28 @@ double
 anl_random_fract(void)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(pg_prng_u32(&pg_global_prng_state), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* and compute a random fraction */
-	return sampler_random_fract(oldrs.randstate);
+	return sampler_random_fract(&oldrs.randstate);
 }
 
 double
 anl_init_selection_state(int n)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(pg_prng_u32(&pg_global_prng_state), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	return exp(-log(sampler_random_fract(oldrs.randstate)) / n);
+	return exp(-log(sampler_random_fract(&oldrs.randstate)) / n);
 }
 
 double
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 1ed4808d53..af9f5169e9 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -66,6 +66,7 @@
 #include "common/file_perm.h"
 #include "common/file_utils.h"
 #include "common/logging.h"
+#include "common/pg_prng.h"
 #include "common/restricted_token.h"
 #include "common/string.h"
 #include "common/username.h"
@@ -880,9 +881,10 @@ choose_dsm_implementation(void)
 {
 #ifdef HAVE_SHM_OPEN
 	int			ntries = 10;
+	pg_prng_state	prng_state;
 
-	/* Initialize random(); this function is its only user in this program. */
-	srandom((unsigned int) (getpid() ^ time(NULL)));
+	/* Initialize pgng state this function is its only user in this program. */
+	pg_prng_seed(&prng_state, ((unsigned int) (getpid() ^ time(NULL))));
 
 	while (ntries > 0)
 	{
@@ -890,7 +892,7 @@ choose_dsm_implementation(void)
 		char		name[64];
 		int			fd;
 
-		handle = random();
+		handle = pg_prng_u32(&prng_state);
 		snprintf(name, 64, "/PostgreSQL.%u", handle);
 		if ((fd = shm_open(name, O_CREAT | O_RDWR | O_EXCL, 0600)) != -1)
 		{
diff --git a/src/bin/pg_test_fsync/pg_test_fsync.c b/src/bin/pg_test_fsync/pg_test_fsync.c
index fef31844fa..5b3c197aa6 100644
--- a/src/bin/pg_test_fsync/pg_test_fsync.c
+++ b/src/bin/pg_test_fsync/pg_test_fsync.c
@@ -15,6 +15,7 @@
 
 #include "access/xlogdefs.h"
 #include "common/logging.h"
+#include "common/pg_prng.h"
 #include "getopt_long.h"
 
 /*
@@ -71,6 +72,7 @@ static char full_buf[DEFAULT_XLOG_SEG_SIZE],
 static struct timeval start_t,
 			stop_t;
 static bool alarm_triggered = false;
+static pg_prng_state	prng_state;
 
 
 static void handle_args(int argc, char *argv[]);
@@ -117,6 +119,7 @@ main(int argc, char *argv[])
 	pqsignal(SIGHUP, signal_cleanup);
 #endif
 
+	pg_prng_strong_seed(&prng_state);
 	prepare_buf();
 
 	test_open();
@@ -233,7 +236,7 @@ prepare_buf(void)
 
 	/* write random data into buffer */
 	for (ops = 0; ops < DEFAULT_XLOG_SEG_SIZE; ops++)
-		full_buf[ops] = random();
+		full_buf[ops] = pg_prng_u32(&prng_state);
 
 	buf = (char *) TYPEALIGN(XLOG_BLCKSZ, full_buf);
 }
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 433abd954b..64c7370ff5 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -58,6 +58,7 @@
 #endif
 
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/logging.h"
 #include "common/string.h"
 #include "common/username.h"
@@ -350,16 +351,8 @@ typedef struct StatsData
  */
 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. */
-static RandomState base_random_sequence;
+static pg_prng_state	base_random_sequence;
 
 /* Synchronization barrier for start and connection */
 static THREAD_BARRIER_T barrier;
@@ -461,7 +454,7 @@ typedef struct
 	 * Separate randomness for each client. This is used for random functions
 	 * 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			command;		/* command number in script */
@@ -498,9 +491,9 @@ typedef struct
 	 * random state to make all of them independent of each other and
 	 * therefore deterministic at the thread level.
 	 */
-	RandomState ts_choose_rs;	/* random state for selecting a script */
-	RandomState ts_throttle_rs; /* random state for transaction throttling */
-	RandomState ts_sample_rs;	/* random state for log sampling */
+	pg_prng_state	ts_choose_rs;	/* random state for selecting a script */
+	pg_prng_state	ts_throttle_rs; /* random state for transaction throttling */
+	pg_prng_state	ts_sample_rs;	/* random state for log sampling */
 
 	int64		throttle_trigger;	/* previous/next throttling (us) */
 	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.
  */
 static void
-initRandomState(RandomState *random_state)
+initRandomState(pg_prng_state *state)
 {
-	random_state->xseed[0] = (unsigned short)
-		(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);
+	pg_prng_seed(state, pg_prng_u64(&base_random_sequence));
 }
 
+
 /*
- * Random number generator: uniform distribution from min to max inclusive.
+ * PRNG: uniform distribution from min to max inclusive.
  *
  * Although the limits are expressed as int64, you can't generate the full
  * int64 range in one call, because the difference of the limits mustn't
- * overflow int64.  In practice it's unwise to ask for more than an int32
- * range, because of the limited precision of pg_erand48().
+ * overflow int64. This is not checked.
  */
 static int64
-getrand(RandomState *random_state, int64 min, int64 max)
+getrand(pg_prng_state *state, int64 min, int64 max)
 {
-	/*
-	 * 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));
+	return min + (int64) pg_prng_u64_range(state, 0, max - min);
 }
 
 /*
@@ -942,8 +921,7 @@ getrand(RandomState *random_state, int64 min, int64 max)
  * value is exp(-parameter).
  */
 static int64
-getExponentialRand(RandomState *random_state, int64 min, int64 max,
-				   double parameter)
+getExponentialRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		cut,
 				uniform,
@@ -953,7 +931,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 	Assert(parameter > 0.0);
 	cut = exp(-parameter);
 	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	uniform = 1.0 - pg_prng_f64(state);
 
 	/*
 	 * inner expression in (cut, 1] (if parameter > 0), rand in [0, 1)
@@ -966,8 +944,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 
 /* random number generator: gaussian distribution from min to max inclusive */
 static int64
-getGaussianRand(RandomState *random_state, int64 min, int64 max,
-				double parameter)
+getGaussianRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		stdev;
 	double		rand;
@@ -990,13 +967,13 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
 	do
 	{
 		/*
-		 * pg_erand48 generates [0,1), but for the basic version of the
+		 * pg_prng_f64 generates [0,1), but for the basic version of the
 		 * Box-Muller transform the two uniformly distributed random numbers
 		 * are expected in (0, 1] (see
 		 * https://en.wikipedia.org/wiki/Box-Muller_transform)
 		 */
-		double		rand1 = 1.0 - pg_erand48(random_state->xseed);
-		double		rand2 = 1.0 - pg_erand48(random_state->xseed);
+		double		rand1 = 1.0 - pg_prng_f64(state);
+		double		rand2 = 1.0 - pg_prng_f64(state);
 
 		/* Box-Muller basic form transform */
 		double		var_sqrt = sqrt(-2.0 * log(rand1));
@@ -1026,7 +1003,7 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
  * not be one.
  */
 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
@@ -1034,8 +1011,8 @@ getPoissonRand(RandomState *random_state, double center)
 	 */
 	double		uniform;
 
-	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	/* pseudo-random value in [0, 1), uniform in (0, 1] */
+	uniform = 1.0 - pg_prng_f64(state);
 
 	return (int64) (-log(uniform) * center + 0.5);
 }
@@ -1048,7 +1025,7 @@ getPoissonRand(RandomState *random_state, double center)
  * This works for s > 1.0, but may perform badly for s very close to 1.0.
  */
 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		x,
@@ -1063,8 +1040,8 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 	while (true)
 	{
 		/* random variates */
-		u = pg_erand48(random_state->xseed);
-		v = pg_erand48(random_state->xseed);
+		u = pg_prng_f64(state);
+		v = pg_prng_f64(state);
 
 		x = floor(pow(u, -1.0 / (s - 1.0)));
 
@@ -1078,14 +1055,14 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 
 /* random number generator: zipfian distribution from min to max inclusive */
 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;
 
 	/* abort if parameter is invalid */
 	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 +1119,7 @@ getHashMurmur2(int64 val, uint64 seed)
  * For small sizes, this generates each of the (size!) possible permutations
  * of integers in the range [0, size) with roughly equal probability.  Once
  * the size is larger than 20, the number of possible permutations exceeds the
- * number of distinct states of the internal pseudorandom number generators,
+ * number of distinct states of the internal pseudorandom number generator,
  * and so not all possible permutations can be generated, but the permutations
  * chosen should continue to give the appearance of being random.
  *
@@ -1152,25 +1129,19 @@ getHashMurmur2(int64 val, uint64 seed)
 static int64
 permute(const int64 val, const int64 isize, const int64 seed)
 {
-	RandomState random_state1;
-	RandomState random_state2;
-	uint64		size;
-	uint64		v;
-	int			masklen;
-	uint64		mask;
-	int			i;
+	/* using a high-end PRNG is probably overkill */
+	pg_prng_state	state;
+	uint64			size;
+	uint64			v;
+	int				masklen;
+	uint64			mask;
+	int				i;
 
 	if (isize < 2)
 		return 0;				/* nothing to permute */
 
-	/* Initialize a pair of random states using the seed */
-	random_state1.xseed[0] = seed & 0xFFFF;
-	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;
+	/* Initialize prng state using the seed */
+	pg_prng_seed(&state, (uint64) seed);
 
 	/* Computations are performed on unsigned values */
 	size = (uint64) isize;
@@ -1216,8 +1187,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 					t;
 
 		/* Random multiply (by an odd number), XOR and rotate of lower half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		if (v <= mask)
 		{
 			v = ((v * m) ^ r) & mask;
@@ -1225,8 +1196,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random multiply (by an odd number), XOR and rotate of upper half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		t = size - 1 - v;
 		if (t <= mask)
 		{
@@ -1236,7 +1207,7 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random offset */
-		r = (uint64) getrand(&random_state2, 0, size - 1);
+		r = pg_prng_u64(&state) % (size - 1);
 		v = (v + r) % size;
 	}
 
@@ -3827,7 +3798,7 @@ doLog(TState *thread, CState *st,
 	 * to the random sample.
 	 */
 	if (sample_rate != 0.0 &&
-		pg_erand48(thread->ts_sample_rs.xseed) > sample_rate)
+		pg_prng_f64(&thread->ts_sample_rs) > sample_rate)
 		return;
 
 	/* should we aggregate the results or not? */
@@ -5763,12 +5734,11 @@ set_random_seed(const char *seed)
 
 	if (seed != NULL)
 		pg_log_info("setting random seed to %llu", (unsigned long long) iseed);
+
 	random_seed = iseed;
 
 	/* Fill base_random_sequence with low-order bits of seed */
-	base_random_sequence.xseed[0] = iseed & 0xFFFF;
-	base_random_sequence.xseed[1] = (iseed >> 16) & 0xFFFF;
-	base_random_sequence.xseed[2] = (iseed >> 32) & 0xFFFF;
+	pg_prng_seed(&base_random_sequence, (uint64_t) iseed);
 
 	return true;
 }
@@ -6439,9 +6409,7 @@ main(int argc, char **argv)
 	/* set default seed for hash functions */
 	if (lookupVariable(&state[0], "default_seed") == NULL)
 	{
-		uint64		seed =
-		((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) |
-		(((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) << 32);
+		uint64		seed = pg_prng_u64(&base_random_sequence);
 
 		for (i = 0; i < nclients; i++)
 			if (!putVariableInt(&state[i], "startup", "default_seed", (int64) seed))
diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl
index ef53f6b2d9..13c9204a3d 100644
--- a/src/bin/pgbench/t/001_pgbench_with_server.pl
+++ b/src/bin/pgbench/t/001_pgbench_with_server.pl
@@ -427,9 +427,9 @@ pgbench(
 
 		# After explicit seeding, the four random checks (1-3,20) are
 		# deterministic
-		qr{command=1.: int 13\b},      # uniform random
-		qr{command=2.: int 116\b},     # exponential random
-		qr{command=3.: int 1498\b},    # gaussian random
+		qr{command=1.: int 17\b},      # uniform random
+		qr{command=2.: int 108\b},     # exponential random
+		qr{command=3.: int 1452\b},    # gaussian random
 		qr{command=4.: int 4\b},
 		qr{command=5.: int 5\b},
 		qr{command=6.: int 6\b},
@@ -496,6 +496,7 @@ pgbench(
 		qr{command=109.: boolean true\b},
 		qr{command=110.: boolean true\b},
 		qr{command=111.: boolean true\b},
+		qr{command=113.: boolean true\b},
 	],
 	'pgbench expressions',
 	{
@@ -639,8 +640,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))
 -- actual values
 \set t debug(permute(:v, 1) = 0)
-\set t debug(permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1 and \
-             permute(0, 2, 5435) = 1 and permute(1, 2, 5435) = 0)
+\set t debug(permute(0, 2, 5431) = 1 and permute(1, 2, 5431) = 0 and \
+             permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1)
+-- 63 bits tests for checking permute portability across architectures
+\set size debug(:max - 10)
+\set t debug(permute(:size-1, :size, 5432) = 4352241160009873020 and \
+             permute(:size-2, :size, 5432) = 4990004119691638710 and \
+             permute(:size-3, :size, 5432) = 5868029867256127549 and \
+             permute(:size-4, :size, 5432) = 1238830324886341378 and \
+             permute(:size-5, :size, 5432) = 7415914367102906897 and \
+             permute(:size-6, :size, 5432) = 3214501037984818995 and \
+             permute(:size-7, :size, 5432) =  600660351261124695)
 }
 	});
 
diff --git a/src/common/Makefile b/src/common/Makefile
index 880722fcf5..31c0dd366d 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -66,6 +66,7 @@ OBJS_COMMON = \
 	md5_common.o \
 	pg_get_line.o \
 	pg_lzcompress.o \
+	pg_prng.o \
 	pgfnames.o \
 	psprintf.o \
 	relpath.o \
diff --git a/src/common/pg_prng.c b/src/common/pg_prng.c
new file mode 100644
index 0000000000..df2054af79
--- /dev/null
+++ b/src/common/pg_prng.c
@@ -0,0 +1,244 @@
+/*
+ * Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * Previous version was the 1988 (?) rand48 standard with 48 bits state LCG,
+ * designed for running on 32 bits or even 16 bits architectures, and to produce
+ * 32 bit ints and floats (i.e. *not* doubles).
+ *
+ * The implied 16-bits unpacking/packing may have a detrimental performance impact
+ * on modern 64 bits architectures.
+ *
+ * We (probably) want:
+ * - one reasonable default PRNG for all pg internal uses.
+ * - NOT to invent a new design!
+ * - something fast, close to rand48 (which basically does 2 arithmetic ops)
+ *   no need for something cryptographic though, which would imply slow
+ * - to produce 64 bits integers & doubles with a 52 bits mantissa
+ *   so state size > 64 bits.
+ * - a small state though, because we might generate quite a few of them
+ *   for different purposes so state size <= 256 or even <= 128 bits
+ * - the state to be aligned to whatever, so state size = 128 bits
+ * - 64 bits operations for efficiency on modern architectures,
+ *   but NOT 128 bits operations.
+ * - not to depend on special hardware for speed (eg MMX/SSE/AES).
+ * - not something with obvious known and relevant defects.
+ * - not something with "rights" attached.
+ *
+ * These constraints reduce a lot the available options from
+ * https://en.wikipedia.org/wiki/List_of_random_number_generators
+ *
+ * LCG
+ * - POSIX rand48: m = 2^48, a = 25214903917, c = 11, extract 47...16
+ * - MMIX by Donald Knuth: m = 2^64, a = 6364136223846793005, c = 1442695040888963407
+ * - very bad on low bits, which MUST NOT BE USED
+ *   a 128 bits would do, but no clear standards, and it would imply 128-bit ops,
+ *   see PCG below.
+ *
+ * PCG Family (https://www.pcg-random.org/)
+ * - this is basically an LCG with an improved outpout function
+ *   so as to avoid typical LCG issues and pass stats tests.
+ * - recommands state size to be twice the output size,
+ *   which seems a reasonable requirement.
+ * - this implies 128 bit ops for our target parameters,
+ *   otherwise it is a fairly good candidate for simplicity,
+ *   efficiency and quality.
+ *
+ * Xor-Shift-Rotate PRNGs:
+ * - a generic annoying feature of this generator class is that there is one
+ *   special all-zero state which must not be used, inducing a constraint on
+ *   initialization, and when the generator get close to it (i.e. many zeros
+ *   in the state) it takes some iterations to recover, with small lumps
+ *   of close/related/bad numbers generated in the process.
+ * Xoroshiro128+
+ * - fails statistical linearity tests in the low bits.
+ * Xoroshiro128**
+ *   https://prng.di.unimi.it/xoroshiro128starstar.c
+ * - mild and irrelevant issues: close to zero state, stats issues after some transforms
+ * Xoshiro256**
+ * - it looks good, but some issues (no big deal for pg usage IMO)
+ *   https://www.pcg-random.org/posts/a-quick-look-at-xoshiro256.html
+ *   mild problems getting out of closes-to-zero state
+ *
+ * MT19937-64
+ * + default in many places, standard
+ * - huge 2.5 KiB state (but TinyMT, 127 bits state space)
+ * - slow (but SFMT, twice as fast)
+ * - vulnerabilities on tinyMT?
+ *
+ * WELL
+ * - large state, 32-bit oriented
+ *
+ * ACORN Family (https://en.wikipedia.org/wiki/ACORN_(PRNG))
+ * - Fortran / double oriented, no C implementation found?!
+ * - medium large state "In practice we recommend using k > 10, M = 2^60 (for general application)
+ *   or M = 2^120 (for demanding applications requiring high-quality pseudo-random numbers
+ *   that will consistently pass all the tests in standard test packages such as TestU01)
+ *   and choose any odd value less than M for the seed."
+ *   k = 11 => 88 bytes/704-bits state, too large:-(
+ *
+ * Splitmix?
+ * Others?
+ *
+ * The conclusion is that we use xor-shift-rotate generator below.
+ */
+
+#include "c.h"
+#include "common/pg_prng.h"
+#include "port/pg_bitutils.h"
+#include <math.h>
+
+/* global state */
+pg_prng_state	pg_global_prng_state;
+
+#define FIRST_BIT_MASK UINT64CONST(0x8000000000000000)
+#define RIGHT_HALF_MASK UINT64CONST(0x00000000FFFFFFFF)
+#define DMANTISSA_MASK UINT64CONST(0x000FFFFFFFFFFFFF)
+
+/* 64-bits rotate left */
+static inline uint64
+rotl(const uint64 x, const int bits)
+{
+	return (x << bits) | (x >> (64 - bits));
+}
+
+/* another 64-bits generator is used for state initialization or iterations */
+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);
+}
+
+/* seed prng state from a 64 bits integer, ensuring non zero */
+void
+pg_prng_seed(pg_prng_state *state, uint64 seed)
+{
+	state->s0 = splitmix64(&seed);
+	state->s1 = splitmix64(&seed);
+}
+
+/* seed with 53 bits (mantissa & sign) from a float */
+void
+pg_prng_fseed(pg_prng_state *state, double fseed)
+{
+	uint64 seed = (int64) (((double) DMANTISSA_MASK) * fseed);
+	pg_prng_seed(state, seed);
+}
+
+/* strong random seeding */
+bool
+pg_prng_strong_seed(pg_prng_state *state)
+{
+	bool ok = pg_strong_random((void *) state, sizeof(pg_prng_state));
+
+	/* avoid zero with Donald Knuth's LCG parameters */
+	if (unlikely(state->s0 == 0 && state->s1 == 0))
+	{
+		/* should it warn that something is amiss if we get there? */
+		pg_prng_state def = PG_PRNG_DEFAULT_STATE;
+		*state = def;
+	}
+
+	return ok;
+}
+
+/* generator & state update */
+static uint64
+xoroshiro128ss(pg_prng_state *state)
+{
+	const 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;
+}
+
+/* u64 generator */
+uint64
+pg_prng_u64(pg_prng_state *state)
+{
+	return xoroshiro128ss(state);
+}
+
+/*
+ * select in a range with bitmask rejection.
+ *
+ * if range is empty, rmin is returned.
+ *
+ * the prng may advance by several states or none,
+ * depending on the range value.
+ */
+uint64
+pg_prng_u64_range(pg_prng_state *state, uint64 rmin, uint64 rmax)
+{
+	uint64 val;
+
+	if (likely(rmax > rmin))
+	{
+		uint64 range = rmax - rmin;
+
+		/* bit position is between 0 and 63, so rshift >= 0 */
+		uint32 rshift = 63 - pg_leftmost_one_pos64(range);
+
+		do
+		{
+			val = xoroshiro128ss(state) >> rshift;
+		}
+		while (unlikely(val > range));
+	}
+	else
+		val = 0;
+
+	return rmin + val;
+}
+
+/* i64 generator */
+int64
+pg_prng_i64(pg_prng_state *state)
+{
+	return (int64) xoroshiro128ss(state);
+}
+
+/* u32 generator */
+uint32
+pg_prng_u32(pg_prng_state *state)
+{
+	const uint64 v = xoroshiro128ss(state);
+	return (uint32) (((v >> 32) ^ v) & RIGHT_HALF_MASK);
+}
+
+/* i32 generator */
+int32
+pg_prng_i32(pg_prng_state *state)
+{
+	const uint64 v = xoroshiro128ss(state);
+	return (int32) (((v >> 32) ^ v) & RIGHT_HALF_MASK);
+}
+
+/* double generator */
+double
+pg_prng_f64(pg_prng_state *state)
+{
+	uint64 v = xoroshiro128ss(state);
+	return ldexp((double) (v & DMANTISSA_MASK), -52);
+}
+
+/* bool generator */
+bool
+pg_prng_bool(pg_prng_state *state)
+{
+	uint64 v = xoroshiro128ss(state);
+	return (v & FIRST_BIT_MASK) == FIRST_BIT_MASK;
+}
diff --git a/src/include/common/pg_prng.h b/src/include/common/pg_prng.h
new file mode 100644
index 0000000000..55f0e3d6cb
--- /dev/null
+++ b/src/include/common/pg_prng.h
@@ -0,0 +1,37 @@
+/*-------------------------------------------------------------------------
+ *
+ * PRNG: internal Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * src/include/common/pg_prng.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_PRNG_H
+#define PG_PRNG_H
+
+/* 128 bits state */
+typedef struct pg_prng_state {
+	uint64	s0, s1;
+} pg_prng_state;
+
+/* global pseudo-random state */
+extern pg_prng_state	pg_global_prng_state;
+
+/* use Donald Knuth's LCG constants for default state */
+#define PG_PRNG_DEFAULT_STATE \
+	{ UINT64CONST(0x5851F42D4C957F2D), UINT64CONST(0x14057B7EF767814F) }
+
+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_strong_seed(pg_prng_state *state);
+extern uint64 pg_prng_u64(pg_prng_state *state);
+extern uint64 pg_prng_u64_range(pg_prng_state *state, uint64 rmin, uint64 rmax);
+extern int64 pg_prng_i64(pg_prng_state *state);
+extern uint32 pg_prng_u32(pg_prng_state *state);
+extern int32 pg_prng_i32(pg_prng_state *state);
+extern double pg_prng_f64(pg_prng_state *state);
+extern bool pg_prng_bool(pg_prng_state *state);
+
+#endif							/* PG_PRNG_H */
diff --git a/src/include/optimizer/geqo.h b/src/include/optimizer/geqo.h
index 24dcdfb6cc..b28e4f084a 100644
--- a/src/include/optimizer/geqo.h
+++ b/src/include/optimizer/geqo.h
@@ -22,6 +22,7 @@
 #ifndef GEQO_H
 #define GEQO_H
 
+#include "common/pg_prng.h"
 #include "nodes/pathnodes.h"
 #include "optimizer/geqo_gene.h"
 
@@ -72,8 +73,8 @@ extern double Geqo_seed;		/* 0 .. 1 */
  */
 typedef struct
 {
-	List	   *initial_rels;	/* the base relations we are joining */
-	unsigned short random_state[3]; /* state for pg_erand48() */
+	List		   *initial_rels;	/* the base relations we are joining */
+	pg_prng_state	random_state;   /* PRNG state */
 } GeqoPrivateData;
 
 
diff --git a/src/include/port.h b/src/include/port.h
index 82f63de325..ef84d00ea0 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -356,11 +356,6 @@ extern int	gettimeofday(struct timeval *tp, struct timezone *tzp);
 #define pgoff_t off_t
 #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
 extern int	fls(int mask);
 #endif
@@ -446,10 +441,6 @@ extern size_t strlcpy(char *dst, const char *src, size_t siz);
 extern size_t strnlen(const char *str, size_t maxlen);
 #endif
 
-#if !defined(HAVE_RANDOM)
-extern long random(void);
-#endif
-
 #ifndef HAVE_SETENV
 extern int	setenv(const char *name, const char *value, int overwrite);
 #endif
@@ -458,10 +449,6 @@ extern int	setenv(const char *name, const char *value, int overwrite);
 extern int	unsetenv(const char *name);
 #endif
 
-#ifndef HAVE_SRANDOM
-extern void srandom(unsigned int seed);
-#endif
-
 #ifndef HAVE_DLOPEN
 extern void *dlopen(const char *file, int mode);
 extern void *dlsym(void *handle, const char *symbol);
diff --git a/src/include/utils/sampling.h b/src/include/utils/sampling.h
index a58d14281b..c0b1c6a645 100644
--- a/src/include/utils/sampling.h
+++ b/src/include/utils/sampling.h
@@ -13,15 +13,14 @@
 #ifndef SAMPLING_H
 #define SAMPLING_H
 
+#include "common/pg_prng.h"
 #include "storage/block.h"		/* for typedef BlockNumber */
 
 
 /* Random generator for sampling code */
-typedef unsigned short SamplerRandomState[3];
-
 extern void sampler_random_init_state(long seed,
-									  SamplerRandomState randstate);
-extern double sampler_random_fract(SamplerRandomState randstate);
+									  pg_prng_state *randstate);
+extern double sampler_random_fract(pg_prng_state *randstate);
 
 /* Block sampling methods */
 
@@ -32,7 +31,7 @@ typedef struct
 	int			n;				/* desired sample size */
 	BlockNumber t;				/* current block number */
 	int			m;				/* blocks selected so far */
-	SamplerRandomState randstate;	/* random generator state */
+	pg_prng_state randstate;	/* random generator state */
 } BlockSamplerData;
 
 typedef BlockSamplerData *BlockSampler;
@@ -46,8 +45,9 @@ extern BlockNumber BlockSampler_Next(BlockSampler bs);
 
 typedef struct
 {
-	double		W;
-	SamplerRandomState randstate;	/* random generator state */
+	double			W;
+	pg_prng_state	randstate;	/* random generator state */
+	bool			randstate_initialized;
 } ReservoirStateData;
 
 typedef ReservoirStateData *ReservoirState;
diff --git a/src/port/Makefile b/src/port/Makefile
index 52dbf5783f..b3754d8940 100644
--- a/src/port/Makefile
+++ b/src/port/Makefile
@@ -42,7 +42,6 @@ OBJS = \
 	$(PG_CRC32C_OBJS) \
 	bsearch_arg.o \
 	chklocale.o \
-	erand48.o \
 	inet_net_ntop.o \
 	noblock.o \
 	path.o \
diff --git a/src/port/erand48.c b/src/port/erand48.c
deleted file mode 100644
index a82ea94220..0000000000
--- a/src/port/erand48.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/port/random.c b/src/port/random.c
deleted file mode 100644
index 2dd59a0829..0000000000
--- a/src/port/random.c
+++ /dev/null
@@ -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();
-}
diff --git a/src/port/srandom.c b/src/port/srandom.c
deleted file mode 100644
index cf1007b2ef..0000000000
--- a/src/port/srandom.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/test/modules/test_bloomfilter/test_bloomfilter.c b/src/test/modules/test_bloomfilter/test_bloomfilter.c
index 96c5011428..69425bd6fc 100644
--- a/src/test/modules/test_bloomfilter/test_bloomfilter.c
+++ b/src/test/modules/test_bloomfilter/test_bloomfilter.c
@@ -13,6 +13,7 @@
 #include "postgres.h"
 
 #include "fmgr.h"
+#include "common/pg_prng.h"
 #include "lib/bloomfilter.h"
 #include "miscadmin.h"
 
@@ -85,7 +86,7 @@ create_and_test_bloom(int power, int64 nelements, int callerseed)
 	 * 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_i32(&pg_global_prng_state) % PG_INT32_MAX : callerseed;
 
 	/* Create Bloom filter, populate it, and report on false positive rate */
 	filter = bloom_create(nelements, bloom_work_mem, seed);
diff --git a/src/test/modules/test_integerset/test_integerset.c b/src/test/modules/test_integerset/test_integerset.c
index 21c6f49b37..3e8cb7a005 100644
--- a/src/test/modules/test_integerset/test_integerset.c
+++ b/src/test/modules/test_integerset/test_integerset.c
@@ -12,6 +12,7 @@
  */
 #include "postgres.h"
 
+#include "common/pg_prng.h"
 #include "fmgr.h"
 #include "lib/integerset.h"
 #include "miscadmin.h"
@@ -99,12 +100,16 @@ static void check_with_filler(IntegerSet *intset, uint64 x, uint64 value, uint64
 static void test_single_value_and_filler(uint64 value, uint64 filler_min, uint64 filler_max);
 static void test_huge_distances(void);
 
+static pg_prng_state	pr_state;
+
 /*
  * SQL-callable entry point to perform all tests.
  */
 Datum
 test_integerset(PG_FUNCTION_ARGS)
 {
+	pg_prng_strong_seed(&pr_state);
+
 	/* Tests for various corner cases */
 	test_empty();
 	test_huge_distances();
@@ -248,8 +253,7 @@ test_pattern(const test_spec *spec)
 		 * only a small part of the integer space is used.  We would very
 		 * rarely hit values that are actually in the set.
 		 */
-		x = (pg_lrand48() << 31) | pg_lrand48();
-		x = x % (last_int + 1000);
+		x = pg_prng_u64(&pr_state) % (last_int + 1000);
 
 		/* Do we expect this value to be present in the set? */
 		if (x >= last_int)
@@ -571,7 +575,7 @@ test_huge_distances(void)
 	 */
 	while (num_values < 1000)
 	{
-		val += pg_lrand48();
+		val += pg_prng_u32(&pr_state);
 		values[num_values++] = val;
 	}
 
diff --git a/src/test/modules/test_rbtree/test_rbtree.c b/src/test/modules/test_rbtree/test_rbtree.c
index 713ebd1b26..59a571185e 100644
--- a/src/test/modules/test_rbtree/test_rbtree.c
+++ b/src/test/modules/test_rbtree/test_rbtree.c
@@ -14,6 +14,7 @@
 #include "postgres.h"
 
 #include "fmgr.h"
+#include "common/pg_prng.h"
 #include "lib/rbtree.h"
 #include "utils/memutils.h"
 
@@ -108,7 +109,7 @@ GetPermutation(int size)
 	 */
 	for (i = 1; i < size; i++)
 	{
-		int			j = random() % (i + 1);
+		int			j = pg_prng_u32(&pg_global_prng_state) % (i + 1);
 
 		if (j < i)				/* avoid fetching undefined data if j=i */
 			permutation[i] = permutation[j];
@@ -320,7 +321,7 @@ testdelete(int size, int delsize)
 
 	for (i = 0; i < delsize; i++)
 	{
-		int			k = random() % size;
+		int			k = pg_prng_u32(&pg_global_prng_state) % size;
 
 		while (chosen[k])
 			k = (k + 1) % size;
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 4362bd44fd..aa048d949f 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -99,9 +99,9 @@ sub mkvcbuild
 	$solution = CreateSolution($vsVersion, $config);
 
 	our @pgportfiles = qw(
-	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c random.c
-	  srandom.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
+	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c
+	  getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
+	  snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
 	  dirent.c dlopen.c getopt.c getopt_long.c link.c
 	  pread.c preadv.c pwrite.c pwritev.c pg_bitutils.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
 	  f2s.c file_perm.c file_utils.c hashfn.c ip.c jsonapi.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
-	  saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c username.c
-	  wait_error.c wchar.c);
+	  pg_get_line.c pg_lzcompress.c pg_prng.c pgfnames.c psprintf.c relpath.c
+	  rmtree.c saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c
+	  username.c wait_error.c wchar.c);
 
 	if ($solution->{options}->{openssl})
 	{
diff --git a/src/tools/testint128.c b/src/tools/testint128.c
index 71c345969a..43d569aeb5 100644
--- a/src/tools/testint128.c
+++ b/src/tools/testint128.c
@@ -27,6 +27,7 @@
 #endif
 
 #include "common/int128.h"
+#include "common/pg_prng.h"
 
 /*
  * We assume the parts of this union are laid out compatibly.
@@ -61,27 +62,11 @@ my_int128_compare(int128 x, int128 y)
 	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.
  *
  * 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 fix the prng state (hmmm...).
  *
  * 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;
 
+	pg_prng_seed(&pg_global_prng_state, 0);
+
 	if (argc >= 2)
 		count = strtol(argv[1], NULL, 0);
 	else
@@ -97,9 +84,9 @@ main(int argc, char **argv)
 
 	while (count-- > 0)
 	{
-		int64		x = get_random_uint64();
-		int64		y = get_random_uint64();
-		int64		z = get_random_uint64();
+		int64		x = pg_prng_u64(&pg_global_prng_state);
+		int64		y = pg_prng_u64(&pg_global_prng_state);
+		int64		z = pg_prng_u64(&pg_global_prng_state);
 		test128		t1;
 		test128		t2;
 
#44Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Fabien COELHO (#43)
1 attachment(s)
Re: rand48 replacement

<resent because of list filter>

[1]: http://cfbot.cputube.org/

Indeed. I wish that these results would be available from the cf interface.

Attached a v11 which might improve things.

Not enough. Here is a v12 which might improve things further.

Not enough. Here is a v13 which might improve things further more.

--
Fabien.

Attachments:

prng-13.patchtext/x-diff; NAME=prng-13.patchDownload
diff --git a/configure b/configure
index 7542fe30a1..7dc95e3960 100755
--- a/configure
+++ b/configure
@@ -800,6 +800,7 @@ infodir
 docdir
 oldincludedir
 includedir
+runstatedir
 localstatedir
 sharedstatedir
 sysconfdir
@@ -941,6 +942,7 @@ datadir='${datarootdir}'
 sysconfdir='${prefix}/etc'
 sharedstatedir='${prefix}/com'
 localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
 includedir='${prefix}/include'
 oldincludedir='/usr/include'
 docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@@ -1193,6 +1195,15 @@ do
   | -silent | --silent | --silen | --sile | --sil)
     silent=yes ;;
 
+  -runstatedir | --runstatedir | --runstatedi | --runstated \
+  | --runstate | --runstat | --runsta | --runst | --runs \
+  | --run | --ru | --r)
+    ac_prev=runstatedir ;;
+  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+  | --run=* | --ru=* | --r=*)
+    runstatedir=$ac_optarg ;;
+
   -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
     ac_prev=sbindir ;;
   -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1330,7 +1341,7 @@ fi
 for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
 		datadir sysconfdir sharedstatedir localstatedir includedir \
 		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
-		libdir localedir mandir
+		libdir localedir mandir runstatedir
 do
   eval ac_val=\$$ac_var
   # Remove trailing slashes.
@@ -1483,6 +1494,7 @@ Fine tuning of the installation directories:
   --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
   --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
   --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
   --libdir=DIR            object code libraries [EPREFIX/lib]
   --includedir=DIR        C header files [PREFIX/include]
   --oldincludedir=DIR     C header files for non-gcc [/usr/include]
@@ -15026,7 +15038,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -15072,7 +15084,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -15096,7 +15108,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -15141,7 +15153,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -15165,7 +15177,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -16301,32 +16313,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
diff --git a/configure.ac b/configure.ac
index ed3cdb9a8e..b36b32ed94 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1822,8 +1822,6 @@ AC_REPLACE_FUNCS(m4_normalize([
 	mkdtemp
 	pread
 	pwrite
-	random
-	srandom
 	strlcat
 	strlcpy
 	strnlen
diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c
index d19f73127c..b250ae912b 100644
--- a/contrib/amcheck/verify_nbtree.c
+++ b/contrib/amcheck/verify_nbtree.c
@@ -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"
@@ -465,8 +466,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();
+		/* Random seed relies on backend prng initialization to avoid repetition */
+		seed = pg_prng_u64(&pg_global_prng_state);
 		/* Create Bloom filter to fingerprint index */
 		state->filter = bloom_create(total_elems, maintenance_work_mem, seed);
 		state->heaptuplespresent = 0;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 2c2f149fb0..146b524076 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -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]);
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 76d4fea21c..c139382170 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -5157,7 +5157,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]);
 		}
diff --git a/contrib/tablefunc/tablefunc.c b/contrib/tablefunc/tablefunc.c
index 779bd4415e..fdd117f81e 100644
--- a/contrib/tablefunc/tablefunc.c
+++ b/contrib/tablefunc/tablefunc.c
@@ -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"
@@ -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_f64(&pg_global_prng_state);
+		u2 = pg_prng_f64(&pg_global_prng_state);
 
 		v1 = (2.0 * u1) - 1.0;
 		v2 = (2.0 * u2) - 1.0;
diff --git a/contrib/tsm_system_rows/tsm_system_rows.c b/contrib/tsm_system_rows/tsm_system_rows.c
index 4996612902..1a46d4b143 100644
--- a/contrib/tsm_system_rows/tsm_system_rows.c
+++ b/contrib/tsm_system_rows/tsm_system_rows.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/contrib/tsm_system_time/tsm_system_time.c b/contrib/tsm_system_time/tsm_system_time.c
index 788d8f9a68..36acc6c106 100644
--- a/contrib/tsm_system_time/tsm_system_time.c
+++ b/contrib/tsm_system_time/tsm_system_time.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index 03191e016c..3c2f24b653 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -16,6 +16,7 @@
 
 #include "access/gin_private.h"
 #include "access/relscan.h"
+#include "common/pg_prng.h"
 #include "miscadmin.h"
 #include "storage/predicate.h"
 #include "utils/datum.h"
@@ -787,7 +788,7 @@ entryLoadMoreItems(GinState *ginstate, GinScanEntry entry,
 	}
 }
 
-#define gin_rand() (((double) random()) / ((double) MAX_RANDOM_VALUE))
+#define gin_rand() pg_prng_f64(&pg_global_prng_state)
 #define dropItem(e) ( gin_rand() > ((double)GinFuzzySearchLimit)/((double)((e)->predictNumberResult)) )
 
 /*
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 43ba03b6eb..0eff4ade84 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -19,6 +19,7 @@
 #include "access/htup_details.h"
 #include "access/reloptions.h"
 #include "catalog/pg_opclass.h"
+#include "common/pg_prng.h"
 #include "storage/indexfsm.h"
 #include "storage/lmgr.h"
 #include "utils/float.h"
@@ -507,7 +508,7 @@ gistchoose(Relation r, Page p, IndexTuple it,	/* it has compressed entry */
 			if (keep_current_best == -1)
 			{
 				/* we didn't make the random choice yet for this old best */
-				keep_current_best = (random() <= (MAX_RANDOM_VALUE / 2)) ? 1 : 0;
+				keep_current_best = pg_prng_bool(&pg_global_prng_state) ? 1 : 0;
 			}
 			if (keep_current_best == 0)
 			{
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index 7355e1dba1..37d8840ce0 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -19,6 +19,7 @@
 #include "access/nbtxlog.h"
 #include "access/transam.h"
 #include "access/xloginsert.h"
+#include "common/pg_prng.h"
 #include "lib/qunique.h"
 #include "miscadmin.h"
 #include "storage/lmgr.h"
@@ -968,7 +969,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_f64(&pg_global_prng_state) <= 0.01)
 				break;
 
 			_bt_stepright(rel, insertstate, stack);
diff --git a/src/backend/access/spgist/spgdoinsert.c b/src/backend/access/spgist/spgdoinsert.c
index 70557bcf3d..5389c93366 100644
--- a/src/backend/access/spgist/spgdoinsert.c
+++ b/src/backend/access/spgist/spgdoinsert.c
@@ -19,6 +19,7 @@
 #include "access/spgist_private.h"
 #include "access/spgxlog.h"
 #include "access/xloginsert.h"
+#include "common/pg_prng.h"
 #include "miscadmin.h"
 #include "storage/bufmgr.h"
 #include "utils/rel.h"
@@ -2210,7 +2211,7 @@ 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_u32(&pg_global_prng_state) % innerTuple->nNodes;
 			}
 
 			switch (out.resultType)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 6597ec45a9..d0557be7c9 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -37,6 +37,7 @@
 #include "commands/async.h"
 #include "commands/tablecmds.h"
 #include "commands/trigger.h"
+#include "common/pg_prng.h"
 #include "executor/spi.h"
 #include "libpq/be-fsstubs.h"
 #include "libpq/pqsignal.h"
@@ -1939,7 +1940,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_f64(&pg_global_prng_state) <= log_xact_sample_rate);
 
 	/*
 	 * initialize current transaction state fields
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 8bfb2ad958..fe623393d7 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -38,6 +38,7 @@
 #include "commands/progress.h"
 #include "commands/tablecmds.h"
 #include "commands/vacuum.h"
+#include "common/pg_prng.h"
 #include "executor/executor.h"
 #include "foreign/fdwapi.h"
 #include "miscadmin.h"
@@ -1173,7 +1174,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 	OldestXmin = GetOldestNonRemovableTransactionId(onerel);
 
 	/* Prepare for sampling block numbers */
-	randseed = random();
+	randseed = pg_prng_i32(&pg_global_prng_state);
 	nblocks = BlockSampler_Init(&bs, totalblocks, targrows, randseed);
 
 #ifdef USE_PREFETCH
@@ -1290,7 +1291,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 					 * Found a suitable tuple, so save it, replacing one old
 					 * tuple at random
 					 */
-					int			k = (int) (targrows * sampler_random_fract(rstate.randstate));
+					int			k = (int) (targrows * sampler_random_fract(&rstate.randstate));
 
 					Assert(k >= 0 && k < targrows);
 					heap_freetuple(rows[k]);
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 44232d50d0..09c34e96fa 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -17,6 +17,7 @@
 #include "access/relscan.h"
 #include "access/tableam.h"
 #include "access/tsmapi.h"
+#include "common/pg_prng.h"
 #include "executor/executor.h"
 #include "executor/nodeSamplescan.h"
 #include "miscadmin.h"
@@ -154,7 +155,7 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	 * do this just once, since the seed shouldn't change over rescans.
 	 */
 	if (tsc->repeatable == NULL)
-		scanstate->seed = random();
+		scanstate->seed = pg_prng_i32(&pg_global_prng_state);
 
 	/*
 	 * Finally, initialize the TABLESAMPLE method handler.
diff --git a/src/backend/optimizer/geqo/geqo_random.c b/src/backend/optimizer/geqo/geqo_random.c
index f21bc047e6..8b42a9ffba 100644
--- a/src/backend/optimizer/geqo/geqo_random.c
+++ b/src/backend/optimizer/geqo/geqo_random.c
@@ -21,14 +21,7 @@ geqo_set_seed(PlannerInfo *root, double seed)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	/*
-	 * XXX. This seeding algorithm could certainly be improved - but it is not
-	 * critical to do so.
-	 */
-	memset(private->random_state, 0, sizeof(private->random_state));
-	memcpy(private->random_state,
-		   &seed,
-		   Min(sizeof(private->random_state), sizeof(seed)));
+	pg_prng_fseed(&private->random_state, seed);
 }
 
 double
@@ -36,5 +29,5 @@ geqo_rand(PlannerInfo *root)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	return pg_erand48(private->random_state);
+	return pg_prng_f64(&private->random_state);
 }
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index e2a76ba055..13df3cc2e4 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -98,6 +98,7 @@
 #include "catalog/pg_control.h"
 #include "common/file_perm.h"
 #include "common/ip.h"
+#include "common/pg_prng.h"
 #include "common/string.h"
 #include "lib/ilist.h"
 #include "libpq/auth.h"
@@ -2675,19 +2676,22 @@ ClosePostmasterPorts(bool am_syslogger)
 void
 InitProcessGlobals(void)
 {
-	unsigned int rseed;
-
 	MyProcPid = getpid();
 	MyStartTimestamp = GetCurrentTimestamp();
 	MyStartTime = timestamptz_to_time_t(MyStartTimestamp);
 
 	/*
-	 * Set a different seed for random() in every process.  We want something
+	 * Set a different global seed in every process.  We want something
 	 * unpredictable, so if possible, use high-quality random bits for the
 	 * seed.  Otherwise, fall back to a seed based on timestamp and PID.
 	 */
-	if (!pg_strong_random(&rseed, sizeof(rseed)))
+	if (!pg_prng_strong_seed(&pg_global_prng_state))
 	{
+		uint64	rseed;
+
+		ereport(WARNING,
+				(errmsg_internal("pg_prng_strong_seed() failed, falling back to weaker seeding")));
+
 		/*
 		 * Since PIDs and timestamps tend to change more frequently in their
 		 * least significant bits, shift the timestamp left to allow a larger
@@ -2698,8 +2702,9 @@ InitProcessGlobals(void)
 		rseed = ((uint64) MyProcPid) ^
 			((uint64) MyStartTimestamp << 12) ^
 			((uint64) MyStartTimestamp >> 20);
+
+		pg_prng_seed(&pg_global_prng_state, rseed);
 	}
-	srandom(rseed);
 }
 
 
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index f9cda6906d..431931bcfe 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -92,6 +92,7 @@
 #include "catalog/pg_tablespace.h"
 #include "common/file_perm.h"
 #include "common/file_utils.h"
+#include "common/pg_prng.h"
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "port/pg_iovec.h"
@@ -2938,7 +2939,7 @@ SetTempTablespaces(Oid *tableSpaces, int numSpaces)
 	 * available tablespaces.
 	 */
 	if (numSpaces > 1)
-		nextTempTableSpace = random() % numSpaces;
+		nextTempTableSpace = pg_prng_u32(&pg_global_prng_state) % numSpaces;
 	else
 		nextTempTableSpace = 0;
 }
diff --git a/src/backend/storage/ipc/dsm.c b/src/backend/storage/ipc/dsm.c
index b461a5f7e9..e97844614e 100644
--- a/src/backend/storage/ipc/dsm.c
+++ b/src/backend/storage/ipc/dsm.c
@@ -33,6 +33,7 @@
 #endif
 #include <sys/stat.h>
 
+#include "common/pg_prng.h"
 #include "lib/ilist.h"
 #include "miscadmin.h"
 #include "port/pg_bitutils.h"
@@ -180,7 +181,7 @@ dsm_postmaster_startup(PGShmemHeader *shim)
 	{
 		Assert(dsm_control_address == NULL);
 		Assert(dsm_control_mapped_size == 0);
-		dsm_control_handle = random() << 1; /* Even numbers only */
+		dsm_control_handle = pg_prng_u32(&pg_global_prng_state) << 1; /* Even numbers only */
 		if (dsm_control_handle == DSM_HANDLE_INVALID)
 			continue;
 		if (dsm_impl_op(DSM_OP_CREATE, dsm_control_handle, segsize,
@@ -536,7 +537,7 @@ dsm_create(Size size, int flags)
 		for (;;)
 		{
 			Assert(seg->mapped_address == NULL && seg->mapped_size == 0);
-			seg->handle = random() << 1;	/* Even numbers only */
+			seg->handle = pg_prng_u32(&pg_global_prng_state) << 1;	/* Even numbers only */
 			if (seg->handle == DSM_HANDLE_INVALID)	/* Reserve sentinel */
 				continue;
 			if (dsm_impl_op(DSM_OP_CREATE, seg->handle, size, &seg->impl_private,
@@ -1237,7 +1238,7 @@ make_main_region_dsm_handle(int slot)
 	 */
 	handle = 1;
 	handle |= slot << 1;
-	handle |= random() << (pg_leftmost_one_pos32(dsm_control->maxitems) + 1);
+	handle |= pg_prng_i32(&pg_global_prng_state) << (pg_leftmost_one_pos32(dsm_control->maxitems) + 1);
 	return handle;
 }
 
diff --git a/src/backend/storage/lmgr/s_lock.c b/src/backend/storage/lmgr/s_lock.c
index 91322a40c1..74c6f251ce 100644
--- a/src/backend/storage/lmgr/s_lock.c
+++ b/src/backend/storage/lmgr/s_lock.c
@@ -50,6 +50,7 @@
 #include <time.h>
 #include <unistd.h>
 
+#include "common/pg_prng.h"
 #include "port/atomics.h"
 #include "storage/s_lock.h"
 
@@ -63,6 +64,7 @@
 slock_t		dummy_spinlock;
 
 static int	spins_per_delay = DEFAULT_SPINS_PER_DELAY;
+static pg_prng_state	prng_state;
 
 
 /*
@@ -143,8 +145,7 @@ perform_spin_delay(SpinDelayStatus *status)
 #endif
 
 		/* increase delay by a random fraction between 1X and 2X */
-		status->cur_delay += (int) (status->cur_delay *
-									((double) random() / (double) MAX_RANDOM_VALUE) + 0.5);
+		status->cur_delay += (int) (status->cur_delay * (pg_prng_f64(&prng_state) + 0.5));
 		/* wrap back to minimum delay when max is exceeded */
 		if (status->cur_delay > MAX_DELAY_USEC)
 			status->cur_delay = MIN_DELAY_USEC;
@@ -303,7 +304,7 @@ volatile struct test_lock_struct test_lock;
 int
 main()
 {
-	srandom((unsigned int) time(NULL));
+	pg_prng_seed(&prng_state, (unsigned int) time(NULL));
 
 	test_lock.pad1 = test_lock.pad2 = 0x44;
 
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 0775abe35d..ef6c27dade 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -42,6 +42,7 @@
 #include "catalog/pg_type.h"
 #include "commands/async.h"
 #include "commands/prepare.h"
+#include "common/pg_prng.h"
 #include "executor/spi.h"
 #include "jit/jit.h"
 #include "libpq/libpq.h"
@@ -2355,13 +2356,13 @@ check_log_duration(char *msec_str, bool was_logged)
 
 		/*
 		 * Do not log if log_statement_sample_rate = 0. Log a sample if
-		 * log_statement_sample_rate <= 1 and avoid unnecessary random() call
+		 * log_statement_sample_rate <= 1 and avoid unnecessary random call
 		 * if log_statement_sample_rate = 1.
 		 */
 		if (exceeded_sample_duration)
 			in_sample = log_statement_sample_rate != 0 &&
 				(log_statement_sample_rate == 1 ||
-				 random() <= log_statement_sample_rate * MAX_RANDOM_VALUE);
+				 pg_prng_f64(&pg_global_prng_state) <= log_statement_sample_rate);
 
 		if (exceeded_duration || in_sample || log_duration || xact_is_sampled)
 		{
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 098bbb372b..9fc0b108dd 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -21,6 +21,7 @@
 
 #include "catalog/pg_type.h"
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/shortest_dec.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
@@ -65,7 +66,7 @@ float8		degree_c_one = 1.0;
 
 /* State for drandom() and setseed() */
 static bool drandom_seed_set = false;
-static unsigned short drandom_seed[3] = {0, 0, 0};
+static pg_prng_state drandom_seed = PG_PRNG_DEFAULT_STATE;
 
 /* Local function prototypes */
 static double sind_q1(double x);
@@ -2762,22 +2763,20 @@ drandom(PG_FUNCTION_ARGS)
 		 * Should that fail for some reason, we fall back on a lower-quality
 		 * seed based on current time and PID.
 		 */
-		if (!pg_strong_random(drandom_seed, sizeof(drandom_seed)))
+		if (!pg_strong_random(&drandom_seed, sizeof(drandom_seed)))
 		{
 			TimestampTz now = GetCurrentTimestamp();
 			uint64		iseed;
 
 			/* Mix the PID with the most predictable bits of the timestamp */
 			iseed = (uint64) now ^ ((uint64) MyProcPid << 32);
-			drandom_seed[0] = (unsigned short) iseed;
-			drandom_seed[1] = (unsigned short) (iseed >> 16);
-			drandom_seed[2] = (unsigned short) (iseed >> 32);
+			pg_prng_seed(&drandom_seed, iseed);
 		}
 		drandom_seed_set = true;
 	}
 
-	/* pg_erand48 produces desired result range [0.0 - 1.0) */
-	result = pg_erand48(drandom_seed);
+	/* produces desired result range [0.0 - 1.0) */
+	result = pg_prng_f64(&drandom_seed);
 
 	PG_RETURN_FLOAT8(result);
 }
@@ -2790,7 +2789,6 @@ Datum
 setseed(PG_FUNCTION_ARGS)
 {
 	float8		seed = PG_GETARG_FLOAT8(0);
-	uint64		iseed;
 
 	if (seed < -1 || seed > 1 || isnan(seed))
 		ereport(ERROR,
@@ -2798,11 +2796,7 @@ setseed(PG_FUNCTION_ARGS)
 				 errmsg("setseed parameter %g is out of allowed range [-1,1]",
 						seed)));
 
-	/* Use sign bit + 47 fractional bits to fill drandom_seed[] */
-	iseed = (int64) (seed * (float8) UINT64CONST(0x7FFFFFFFFFFF));
-	drandom_seed[0] = (unsigned short) iseed;
-	drandom_seed[1] = (unsigned short) (iseed >> 16);
-	drandom_seed[2] = (unsigned short) (iseed >> 32);
+	pg_prng_fseed(&drandom_seed, seed);
 	drandom_seed_set = true;
 
 	PG_RETURN_VOID();
diff --git a/src/backend/utils/misc/sampling.c b/src/backend/utils/misc/sampling.c
index 0c327e823f..f1c86150d8 100644
--- a/src/backend/utils/misc/sampling.c
+++ b/src/backend/utils/misc/sampling.c
@@ -49,7 +49,7 @@ BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize,
 	bs->t = 0;					/* blocks scanned so far */
 	bs->m = 0;					/* blocks selected so far */
 
-	sampler_random_init_state(randseed, bs->randstate);
+	sampler_random_init_state(randseed, &bs->randstate);
 
 	return Min(bs->n, bs->N);
 }
@@ -98,7 +98,7 @@ BlockSampler_Next(BlockSampler bs)
 	 * less than k, which means that we cannot fail to select enough blocks.
 	 *----------
 	 */
-	V = sampler_random_fract(bs->randstate);
+	V = sampler_random_fract(&bs->randstate);
 	p = 1.0 - (double) k / (double) K;
 	while (V < p)
 	{
@@ -136,10 +136,10 @@ reservoir_init_selection_state(ReservoirState rs, int n)
 	 * Reservoir sampling is not used anywhere where it would need to return
 	 * repeatable results so we can initialize it randomly.
 	 */
-	sampler_random_init_state(random(), rs->randstate);
+	sampler_random_init_state(pg_prng_u32(&pg_global_prng_state), &rs->randstate);
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	rs->W = exp(-log(sampler_random_fract(rs->randstate)) / n);
+	rs->W = exp(-log(sampler_random_fract(&rs->randstate)) / n);
 }
 
 double
@@ -154,7 +154,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 		double		V,
 					quot;
 
-		V = sampler_random_fract(rs->randstate);	/* Generate V */
+		V = sampler_random_fract(&rs->randstate);	/* Generate V */
 		S = 0;
 		t += 1;
 		/* Note: "num" in Vitter's code is always equal to t - n */
@@ -186,7 +186,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 						tmp;
 
 			/* Generate U and X */
-			U = sampler_random_fract(rs->randstate);
+			U = sampler_random_fract(&rs->randstate);
 			X = t * (W - 1.0);
 			S = floor(X);		/* S is tentatively set to floor(X) */
 			/* Test if U <= h(S)/cg(X) in the manner of (6.3) */
@@ -215,7 +215,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 				y *= numer / denom;
 				denom -= 1;
 			}
-			W = exp(-log(sampler_random_fract(rs->randstate)) / n); /* Generate W in advance */
+			W = exp(-log(sampler_random_fract(&rs->randstate)) / n); /* Generate W in advance */
 			if (exp(log(y) / n) <= (t + X) / t)
 				break;
 		}
@@ -230,24 +230,22 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
  *----------
  */
 void
-sampler_random_init_state(long seed, SamplerRandomState randstate)
+sampler_random_init_state(long seed, pg_prng_state *randstate)
 {
-	randstate[0] = 0x330e;		/* same as pg_erand48, but could be anything */
-	randstate[1] = (unsigned short) seed;
-	randstate[2] = (unsigned short) (seed >> 16);
+	pg_prng_seed(randstate, (uint64_t) seed);
 }
 
 /* Select a random value R uniformly distributed in (0 - 1) */
 double
-sampler_random_fract(SamplerRandomState randstate)
+sampler_random_fract(pg_prng_state *randstate)
 {
 	double		res;
 
-	/* pg_erand48 returns a value in [0.0 - 1.0), so we must reject 0 */
+	/* pg_prng_f64 returned value in [0.0 - 1.0), so we must reject 0.0 */
 	do
 	{
-		res = pg_erand48(randstate);
-	} while (res == 0.0);
+		res = pg_prng_f64(randstate);
+	} while (unlikely(res == 0.0));
 	return res;
 }
 
@@ -266,22 +264,28 @@ double
 anl_random_fract(void)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(pg_prng_u32(&pg_global_prng_state), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* and compute a random fraction */
-	return sampler_random_fract(oldrs.randstate);
+	return sampler_random_fract(&oldrs.randstate);
 }
 
 double
 anl_init_selection_state(int n)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(pg_prng_u32(&pg_global_prng_state), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	return exp(-log(sampler_random_fract(oldrs.randstate)) / n);
+	return exp(-log(sampler_random_fract(&oldrs.randstate)) / n);
 }
 
 double
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 1ed4808d53..af9f5169e9 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -66,6 +66,7 @@
 #include "common/file_perm.h"
 #include "common/file_utils.h"
 #include "common/logging.h"
+#include "common/pg_prng.h"
 #include "common/restricted_token.h"
 #include "common/string.h"
 #include "common/username.h"
@@ -880,9 +881,10 @@ choose_dsm_implementation(void)
 {
 #ifdef HAVE_SHM_OPEN
 	int			ntries = 10;
+	pg_prng_state	prng_state;
 
-	/* Initialize random(); this function is its only user in this program. */
-	srandom((unsigned int) (getpid() ^ time(NULL)));
+	/* Initialize pgng state this function is its only user in this program. */
+	pg_prng_seed(&prng_state, ((unsigned int) (getpid() ^ time(NULL))));
 
 	while (ntries > 0)
 	{
@@ -890,7 +892,7 @@ choose_dsm_implementation(void)
 		char		name[64];
 		int			fd;
 
-		handle = random();
+		handle = pg_prng_u32(&prng_state);
 		snprintf(name, 64, "/PostgreSQL.%u", handle);
 		if ((fd = shm_open(name, O_CREAT | O_RDWR | O_EXCL, 0600)) != -1)
 		{
diff --git a/src/bin/pg_test_fsync/pg_test_fsync.c b/src/bin/pg_test_fsync/pg_test_fsync.c
index fef31844fa..5b3c197aa6 100644
--- a/src/bin/pg_test_fsync/pg_test_fsync.c
+++ b/src/bin/pg_test_fsync/pg_test_fsync.c
@@ -15,6 +15,7 @@
 
 #include "access/xlogdefs.h"
 #include "common/logging.h"
+#include "common/pg_prng.h"
 #include "getopt_long.h"
 
 /*
@@ -71,6 +72,7 @@ static char full_buf[DEFAULT_XLOG_SEG_SIZE],
 static struct timeval start_t,
 			stop_t;
 static bool alarm_triggered = false;
+static pg_prng_state	prng_state;
 
 
 static void handle_args(int argc, char *argv[]);
@@ -117,6 +119,7 @@ main(int argc, char *argv[])
 	pqsignal(SIGHUP, signal_cleanup);
 #endif
 
+	pg_prng_strong_seed(&prng_state);
 	prepare_buf();
 
 	test_open();
@@ -233,7 +236,7 @@ prepare_buf(void)
 
 	/* write random data into buffer */
 	for (ops = 0; ops < DEFAULT_XLOG_SEG_SIZE; ops++)
-		full_buf[ops] = random();
+		full_buf[ops] = pg_prng_u32(&prng_state);
 
 	buf = (char *) TYPEALIGN(XLOG_BLCKSZ, full_buf);
 }
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 433abd954b..64c7370ff5 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -58,6 +58,7 @@
 #endif
 
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/logging.h"
 #include "common/string.h"
 #include "common/username.h"
@@ -350,16 +351,8 @@ typedef struct StatsData
  */
 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. */
-static RandomState base_random_sequence;
+static pg_prng_state	base_random_sequence;
 
 /* Synchronization barrier for start and connection */
 static THREAD_BARRIER_T barrier;
@@ -461,7 +454,7 @@ typedef struct
 	 * Separate randomness for each client. This is used for random functions
 	 * 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			command;		/* command number in script */
@@ -498,9 +491,9 @@ typedef struct
 	 * random state to make all of them independent of each other and
 	 * therefore deterministic at the thread level.
 	 */
-	RandomState ts_choose_rs;	/* random state for selecting a script */
-	RandomState ts_throttle_rs; /* random state for transaction throttling */
-	RandomState ts_sample_rs;	/* random state for log sampling */
+	pg_prng_state	ts_choose_rs;	/* random state for selecting a script */
+	pg_prng_state	ts_throttle_rs; /* random state for transaction throttling */
+	pg_prng_state	ts_sample_rs;	/* random state for log sampling */
 
 	int64		throttle_trigger;	/* previous/next throttling (us) */
 	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.
  */
 static void
-initRandomState(RandomState *random_state)
+initRandomState(pg_prng_state *state)
 {
-	random_state->xseed[0] = (unsigned short)
-		(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);
+	pg_prng_seed(state, pg_prng_u64(&base_random_sequence));
 }
 
+
 /*
- * Random number generator: uniform distribution from min to max inclusive.
+ * PRNG: uniform distribution from min to max inclusive.
  *
  * Although the limits are expressed as int64, you can't generate the full
  * int64 range in one call, because the difference of the limits mustn't
- * overflow int64.  In practice it's unwise to ask for more than an int32
- * range, because of the limited precision of pg_erand48().
+ * overflow int64. This is not checked.
  */
 static int64
-getrand(RandomState *random_state, int64 min, int64 max)
+getrand(pg_prng_state *state, int64 min, int64 max)
 {
-	/*
-	 * 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));
+	return min + (int64) pg_prng_u64_range(state, 0, max - min);
 }
 
 /*
@@ -942,8 +921,7 @@ getrand(RandomState *random_state, int64 min, int64 max)
  * value is exp(-parameter).
  */
 static int64
-getExponentialRand(RandomState *random_state, int64 min, int64 max,
-				   double parameter)
+getExponentialRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		cut,
 				uniform,
@@ -953,7 +931,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 	Assert(parameter > 0.0);
 	cut = exp(-parameter);
 	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	uniform = 1.0 - pg_prng_f64(state);
 
 	/*
 	 * inner expression in (cut, 1] (if parameter > 0), rand in [0, 1)
@@ -966,8 +944,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 
 /* random number generator: gaussian distribution from min to max inclusive */
 static int64
-getGaussianRand(RandomState *random_state, int64 min, int64 max,
-				double parameter)
+getGaussianRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		stdev;
 	double		rand;
@@ -990,13 +967,13 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
 	do
 	{
 		/*
-		 * pg_erand48 generates [0,1), but for the basic version of the
+		 * pg_prng_f64 generates [0,1), but for the basic version of the
 		 * Box-Muller transform the two uniformly distributed random numbers
 		 * are expected in (0, 1] (see
 		 * https://en.wikipedia.org/wiki/Box-Muller_transform)
 		 */
-		double		rand1 = 1.0 - pg_erand48(random_state->xseed);
-		double		rand2 = 1.0 - pg_erand48(random_state->xseed);
+		double		rand1 = 1.0 - pg_prng_f64(state);
+		double		rand2 = 1.0 - pg_prng_f64(state);
 
 		/* Box-Muller basic form transform */
 		double		var_sqrt = sqrt(-2.0 * log(rand1));
@@ -1026,7 +1003,7 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
  * not be one.
  */
 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
@@ -1034,8 +1011,8 @@ getPoissonRand(RandomState *random_state, double center)
 	 */
 	double		uniform;
 
-	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	/* pseudo-random value in [0, 1), uniform in (0, 1] */
+	uniform = 1.0 - pg_prng_f64(state);
 
 	return (int64) (-log(uniform) * center + 0.5);
 }
@@ -1048,7 +1025,7 @@ getPoissonRand(RandomState *random_state, double center)
  * This works for s > 1.0, but may perform badly for s very close to 1.0.
  */
 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		x,
@@ -1063,8 +1040,8 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 	while (true)
 	{
 		/* random variates */
-		u = pg_erand48(random_state->xseed);
-		v = pg_erand48(random_state->xseed);
+		u = pg_prng_f64(state);
+		v = pg_prng_f64(state);
 
 		x = floor(pow(u, -1.0 / (s - 1.0)));
 
@@ -1078,14 +1055,14 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 
 /* random number generator: zipfian distribution from min to max inclusive */
 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;
 
 	/* abort if parameter is invalid */
 	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 +1119,7 @@ getHashMurmur2(int64 val, uint64 seed)
  * For small sizes, this generates each of the (size!) possible permutations
  * of integers in the range [0, size) with roughly equal probability.  Once
  * the size is larger than 20, the number of possible permutations exceeds the
- * number of distinct states of the internal pseudorandom number generators,
+ * number of distinct states of the internal pseudorandom number generator,
  * and so not all possible permutations can be generated, but the permutations
  * chosen should continue to give the appearance of being random.
  *
@@ -1152,25 +1129,19 @@ getHashMurmur2(int64 val, uint64 seed)
 static int64
 permute(const int64 val, const int64 isize, const int64 seed)
 {
-	RandomState random_state1;
-	RandomState random_state2;
-	uint64		size;
-	uint64		v;
-	int			masklen;
-	uint64		mask;
-	int			i;
+	/* using a high-end PRNG is probably overkill */
+	pg_prng_state	state;
+	uint64			size;
+	uint64			v;
+	int				masklen;
+	uint64			mask;
+	int				i;
 
 	if (isize < 2)
 		return 0;				/* nothing to permute */
 
-	/* Initialize a pair of random states using the seed */
-	random_state1.xseed[0] = seed & 0xFFFF;
-	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;
+	/* Initialize prng state using the seed */
+	pg_prng_seed(&state, (uint64) seed);
 
 	/* Computations are performed on unsigned values */
 	size = (uint64) isize;
@@ -1216,8 +1187,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 					t;
 
 		/* Random multiply (by an odd number), XOR and rotate of lower half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		if (v <= mask)
 		{
 			v = ((v * m) ^ r) & mask;
@@ -1225,8 +1196,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random multiply (by an odd number), XOR and rotate of upper half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		t = size - 1 - v;
 		if (t <= mask)
 		{
@@ -1236,7 +1207,7 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random offset */
-		r = (uint64) getrand(&random_state2, 0, size - 1);
+		r = pg_prng_u64(&state) % (size - 1);
 		v = (v + r) % size;
 	}
 
@@ -3827,7 +3798,7 @@ doLog(TState *thread, CState *st,
 	 * to the random sample.
 	 */
 	if (sample_rate != 0.0 &&
-		pg_erand48(thread->ts_sample_rs.xseed) > sample_rate)
+		pg_prng_f64(&thread->ts_sample_rs) > sample_rate)
 		return;
 
 	/* should we aggregate the results or not? */
@@ -5763,12 +5734,11 @@ set_random_seed(const char *seed)
 
 	if (seed != NULL)
 		pg_log_info("setting random seed to %llu", (unsigned long long) iseed);
+
 	random_seed = iseed;
 
 	/* Fill base_random_sequence with low-order bits of seed */
-	base_random_sequence.xseed[0] = iseed & 0xFFFF;
-	base_random_sequence.xseed[1] = (iseed >> 16) & 0xFFFF;
-	base_random_sequence.xseed[2] = (iseed >> 32) & 0xFFFF;
+	pg_prng_seed(&base_random_sequence, (uint64_t) iseed);
 
 	return true;
 }
@@ -6439,9 +6409,7 @@ main(int argc, char **argv)
 	/* set default seed for hash functions */
 	if (lookupVariable(&state[0], "default_seed") == NULL)
 	{
-		uint64		seed =
-		((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) |
-		(((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) << 32);
+		uint64		seed = pg_prng_u64(&base_random_sequence);
 
 		for (i = 0; i < nclients; i++)
 			if (!putVariableInt(&state[i], "startup", "default_seed", (int64) seed))
diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl
index ef53f6b2d9..13c9204a3d 100644
--- a/src/bin/pgbench/t/001_pgbench_with_server.pl
+++ b/src/bin/pgbench/t/001_pgbench_with_server.pl
@@ -427,9 +427,9 @@ pgbench(
 
 		# After explicit seeding, the four random checks (1-3,20) are
 		# deterministic
-		qr{command=1.: int 13\b},      # uniform random
-		qr{command=2.: int 116\b},     # exponential random
-		qr{command=3.: int 1498\b},    # gaussian random
+		qr{command=1.: int 17\b},      # uniform random
+		qr{command=2.: int 108\b},     # exponential random
+		qr{command=3.: int 1452\b},    # gaussian random
 		qr{command=4.: int 4\b},
 		qr{command=5.: int 5\b},
 		qr{command=6.: int 6\b},
@@ -496,6 +496,7 @@ pgbench(
 		qr{command=109.: boolean true\b},
 		qr{command=110.: boolean true\b},
 		qr{command=111.: boolean true\b},
+		qr{command=113.: boolean true\b},
 	],
 	'pgbench expressions',
 	{
@@ -639,8 +640,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))
 -- actual values
 \set t debug(permute(:v, 1) = 0)
-\set t debug(permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1 and \
-             permute(0, 2, 5435) = 1 and permute(1, 2, 5435) = 0)
+\set t debug(permute(0, 2, 5431) = 1 and permute(1, 2, 5431) = 0 and \
+             permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1)
+-- 63 bits tests for checking permute portability across architectures
+\set size debug(:max - 10)
+\set t debug(permute(:size-1, :size, 5432) = 4352241160009873020 and \
+             permute(:size-2, :size, 5432) = 4990004119691638710 and \
+             permute(:size-3, :size, 5432) = 5868029867256127549 and \
+             permute(:size-4, :size, 5432) = 1238830324886341378 and \
+             permute(:size-5, :size, 5432) = 7415914367102906897 and \
+             permute(:size-6, :size, 5432) = 3214501037984818995 and \
+             permute(:size-7, :size, 5432) =  600660351261124695)
 }
 	});
 
diff --git a/src/common/Makefile b/src/common/Makefile
index 880722fcf5..31c0dd366d 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -66,6 +66,7 @@ OBJS_COMMON = \
 	md5_common.o \
 	pg_get_line.o \
 	pg_lzcompress.o \
+	pg_prng.o \
 	pgfnames.o \
 	psprintf.o \
 	relpath.o \
diff --git a/src/common/pg_prng.c b/src/common/pg_prng.c
new file mode 100644
index 0000000000..df2054af79
--- /dev/null
+++ b/src/common/pg_prng.c
@@ -0,0 +1,244 @@
+/*
+ * Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * Previous version was the 1988 (?) rand48 standard with 48 bits state LCG,
+ * designed for running on 32 bits or even 16 bits architectures, and to produce
+ * 32 bit ints and floats (i.e. *not* doubles).
+ *
+ * The implied 16-bits unpacking/packing may have a detrimental performance impact
+ * on modern 64 bits architectures.
+ *
+ * We (probably) want:
+ * - one reasonable default PRNG for all pg internal uses.
+ * - NOT to invent a new design!
+ * - something fast, close to rand48 (which basically does 2 arithmetic ops)
+ *   no need for something cryptographic though, which would imply slow
+ * - to produce 64 bits integers & doubles with a 52 bits mantissa
+ *   so state size > 64 bits.
+ * - a small state though, because we might generate quite a few of them
+ *   for different purposes so state size <= 256 or even <= 128 bits
+ * - the state to be aligned to whatever, so state size = 128 bits
+ * - 64 bits operations for efficiency on modern architectures,
+ *   but NOT 128 bits operations.
+ * - not to depend on special hardware for speed (eg MMX/SSE/AES).
+ * - not something with obvious known and relevant defects.
+ * - not something with "rights" attached.
+ *
+ * These constraints reduce a lot the available options from
+ * https://en.wikipedia.org/wiki/List_of_random_number_generators
+ *
+ * LCG
+ * - POSIX rand48: m = 2^48, a = 25214903917, c = 11, extract 47...16
+ * - MMIX by Donald Knuth: m = 2^64, a = 6364136223846793005, c = 1442695040888963407
+ * - very bad on low bits, which MUST NOT BE USED
+ *   a 128 bits would do, but no clear standards, and it would imply 128-bit ops,
+ *   see PCG below.
+ *
+ * PCG Family (https://www.pcg-random.org/)
+ * - this is basically an LCG with an improved outpout function
+ *   so as to avoid typical LCG issues and pass stats tests.
+ * - recommands state size to be twice the output size,
+ *   which seems a reasonable requirement.
+ * - this implies 128 bit ops for our target parameters,
+ *   otherwise it is a fairly good candidate for simplicity,
+ *   efficiency and quality.
+ *
+ * Xor-Shift-Rotate PRNGs:
+ * - a generic annoying feature of this generator class is that there is one
+ *   special all-zero state which must not be used, inducing a constraint on
+ *   initialization, and when the generator get close to it (i.e. many zeros
+ *   in the state) it takes some iterations to recover, with small lumps
+ *   of close/related/bad numbers generated in the process.
+ * Xoroshiro128+
+ * - fails statistical linearity tests in the low bits.
+ * Xoroshiro128**
+ *   https://prng.di.unimi.it/xoroshiro128starstar.c
+ * - mild and irrelevant issues: close to zero state, stats issues after some transforms
+ * Xoshiro256**
+ * - it looks good, but some issues (no big deal for pg usage IMO)
+ *   https://www.pcg-random.org/posts/a-quick-look-at-xoshiro256.html
+ *   mild problems getting out of closes-to-zero state
+ *
+ * MT19937-64
+ * + default in many places, standard
+ * - huge 2.5 KiB state (but TinyMT, 127 bits state space)
+ * - slow (but SFMT, twice as fast)
+ * - vulnerabilities on tinyMT?
+ *
+ * WELL
+ * - large state, 32-bit oriented
+ *
+ * ACORN Family (https://en.wikipedia.org/wiki/ACORN_(PRNG))
+ * - Fortran / double oriented, no C implementation found?!
+ * - medium large state "In practice we recommend using k > 10, M = 2^60 (for general application)
+ *   or M = 2^120 (for demanding applications requiring high-quality pseudo-random numbers
+ *   that will consistently pass all the tests in standard test packages such as TestU01)
+ *   and choose any odd value less than M for the seed."
+ *   k = 11 => 88 bytes/704-bits state, too large:-(
+ *
+ * Splitmix?
+ * Others?
+ *
+ * The conclusion is that we use xor-shift-rotate generator below.
+ */
+
+#include "c.h"
+#include "common/pg_prng.h"
+#include "port/pg_bitutils.h"
+#include <math.h>
+
+/* global state */
+pg_prng_state	pg_global_prng_state;
+
+#define FIRST_BIT_MASK UINT64CONST(0x8000000000000000)
+#define RIGHT_HALF_MASK UINT64CONST(0x00000000FFFFFFFF)
+#define DMANTISSA_MASK UINT64CONST(0x000FFFFFFFFFFFFF)
+
+/* 64-bits rotate left */
+static inline uint64
+rotl(const uint64 x, const int bits)
+{
+	return (x << bits) | (x >> (64 - bits));
+}
+
+/* another 64-bits generator is used for state initialization or iterations */
+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);
+}
+
+/* seed prng state from a 64 bits integer, ensuring non zero */
+void
+pg_prng_seed(pg_prng_state *state, uint64 seed)
+{
+	state->s0 = splitmix64(&seed);
+	state->s1 = splitmix64(&seed);
+}
+
+/* seed with 53 bits (mantissa & sign) from a float */
+void
+pg_prng_fseed(pg_prng_state *state, double fseed)
+{
+	uint64 seed = (int64) (((double) DMANTISSA_MASK) * fseed);
+	pg_prng_seed(state, seed);
+}
+
+/* strong random seeding */
+bool
+pg_prng_strong_seed(pg_prng_state *state)
+{
+	bool ok = pg_strong_random((void *) state, sizeof(pg_prng_state));
+
+	/* avoid zero with Donald Knuth's LCG parameters */
+	if (unlikely(state->s0 == 0 && state->s1 == 0))
+	{
+		/* should it warn that something is amiss if we get there? */
+		pg_prng_state def = PG_PRNG_DEFAULT_STATE;
+		*state = def;
+	}
+
+	return ok;
+}
+
+/* generator & state update */
+static uint64
+xoroshiro128ss(pg_prng_state *state)
+{
+	const 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;
+}
+
+/* u64 generator */
+uint64
+pg_prng_u64(pg_prng_state *state)
+{
+	return xoroshiro128ss(state);
+}
+
+/*
+ * select in a range with bitmask rejection.
+ *
+ * if range is empty, rmin is returned.
+ *
+ * the prng may advance by several states or none,
+ * depending on the range value.
+ */
+uint64
+pg_prng_u64_range(pg_prng_state *state, uint64 rmin, uint64 rmax)
+{
+	uint64 val;
+
+	if (likely(rmax > rmin))
+	{
+		uint64 range = rmax - rmin;
+
+		/* bit position is between 0 and 63, so rshift >= 0 */
+		uint32 rshift = 63 - pg_leftmost_one_pos64(range);
+
+		do
+		{
+			val = xoroshiro128ss(state) >> rshift;
+		}
+		while (unlikely(val > range));
+	}
+	else
+		val = 0;
+
+	return rmin + val;
+}
+
+/* i64 generator */
+int64
+pg_prng_i64(pg_prng_state *state)
+{
+	return (int64) xoroshiro128ss(state);
+}
+
+/* u32 generator */
+uint32
+pg_prng_u32(pg_prng_state *state)
+{
+	const uint64 v = xoroshiro128ss(state);
+	return (uint32) (((v >> 32) ^ v) & RIGHT_HALF_MASK);
+}
+
+/* i32 generator */
+int32
+pg_prng_i32(pg_prng_state *state)
+{
+	const uint64 v = xoroshiro128ss(state);
+	return (int32) (((v >> 32) ^ v) & RIGHT_HALF_MASK);
+}
+
+/* double generator */
+double
+pg_prng_f64(pg_prng_state *state)
+{
+	uint64 v = xoroshiro128ss(state);
+	return ldexp((double) (v & DMANTISSA_MASK), -52);
+}
+
+/* bool generator */
+bool
+pg_prng_bool(pg_prng_state *state)
+{
+	uint64 v = xoroshiro128ss(state);
+	return (v & FIRST_BIT_MASK) == FIRST_BIT_MASK;
+}
diff --git a/src/include/common/pg_prng.h b/src/include/common/pg_prng.h
new file mode 100644
index 0000000000..55f0e3d6cb
--- /dev/null
+++ b/src/include/common/pg_prng.h
@@ -0,0 +1,37 @@
+/*-------------------------------------------------------------------------
+ *
+ * PRNG: internal Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * src/include/common/pg_prng.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_PRNG_H
+#define PG_PRNG_H
+
+/* 128 bits state */
+typedef struct pg_prng_state {
+	uint64	s0, s1;
+} pg_prng_state;
+
+/* global pseudo-random state */
+extern pg_prng_state	pg_global_prng_state;
+
+/* use Donald Knuth's LCG constants for default state */
+#define PG_PRNG_DEFAULT_STATE \
+	{ UINT64CONST(0x5851F42D4C957F2D), UINT64CONST(0x14057B7EF767814F) }
+
+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_strong_seed(pg_prng_state *state);
+extern uint64 pg_prng_u64(pg_prng_state *state);
+extern uint64 pg_prng_u64_range(pg_prng_state *state, uint64 rmin, uint64 rmax);
+extern int64 pg_prng_i64(pg_prng_state *state);
+extern uint32 pg_prng_u32(pg_prng_state *state);
+extern int32 pg_prng_i32(pg_prng_state *state);
+extern double pg_prng_f64(pg_prng_state *state);
+extern bool pg_prng_bool(pg_prng_state *state);
+
+#endif							/* PG_PRNG_H */
diff --git a/src/include/optimizer/geqo.h b/src/include/optimizer/geqo.h
index 24dcdfb6cc..b28e4f084a 100644
--- a/src/include/optimizer/geqo.h
+++ b/src/include/optimizer/geqo.h
@@ -22,6 +22,7 @@
 #ifndef GEQO_H
 #define GEQO_H
 
+#include "common/pg_prng.h"
 #include "nodes/pathnodes.h"
 #include "optimizer/geqo_gene.h"
 
@@ -72,8 +73,8 @@ extern double Geqo_seed;		/* 0 .. 1 */
  */
 typedef struct
 {
-	List	   *initial_rels;	/* the base relations we are joining */
-	unsigned short random_state[3]; /* state for pg_erand48() */
+	List		   *initial_rels;	/* the base relations we are joining */
+	pg_prng_state	random_state;   /* PRNG state */
 } GeqoPrivateData;
 
 
diff --git a/src/include/port.h b/src/include/port.h
index 82f63de325..ef84d00ea0 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -356,11 +356,6 @@ extern int	gettimeofday(struct timeval *tp, struct timezone *tzp);
 #define pgoff_t off_t
 #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
 extern int	fls(int mask);
 #endif
@@ -446,10 +441,6 @@ extern size_t strlcpy(char *dst, const char *src, size_t siz);
 extern size_t strnlen(const char *str, size_t maxlen);
 #endif
 
-#if !defined(HAVE_RANDOM)
-extern long random(void);
-#endif
-
 #ifndef HAVE_SETENV
 extern int	setenv(const char *name, const char *value, int overwrite);
 #endif
@@ -458,10 +449,6 @@ extern int	setenv(const char *name, const char *value, int overwrite);
 extern int	unsetenv(const char *name);
 #endif
 
-#ifndef HAVE_SRANDOM
-extern void srandom(unsigned int seed);
-#endif
-
 #ifndef HAVE_DLOPEN
 extern void *dlopen(const char *file, int mode);
 extern void *dlsym(void *handle, const char *symbol);
diff --git a/src/include/utils/sampling.h b/src/include/utils/sampling.h
index a58d14281b..c0b1c6a645 100644
--- a/src/include/utils/sampling.h
+++ b/src/include/utils/sampling.h
@@ -13,15 +13,14 @@
 #ifndef SAMPLING_H
 #define SAMPLING_H
 
+#include "common/pg_prng.h"
 #include "storage/block.h"		/* for typedef BlockNumber */
 
 
 /* Random generator for sampling code */
-typedef unsigned short SamplerRandomState[3];
-
 extern void sampler_random_init_state(long seed,
-									  SamplerRandomState randstate);
-extern double sampler_random_fract(SamplerRandomState randstate);
+									  pg_prng_state *randstate);
+extern double sampler_random_fract(pg_prng_state *randstate);
 
 /* Block sampling methods */
 
@@ -32,7 +31,7 @@ typedef struct
 	int			n;				/* desired sample size */
 	BlockNumber t;				/* current block number */
 	int			m;				/* blocks selected so far */
-	SamplerRandomState randstate;	/* random generator state */
+	pg_prng_state randstate;	/* random generator state */
 } BlockSamplerData;
 
 typedef BlockSamplerData *BlockSampler;
@@ -46,8 +45,9 @@ extern BlockNumber BlockSampler_Next(BlockSampler bs);
 
 typedef struct
 {
-	double		W;
-	SamplerRandomState randstate;	/* random generator state */
+	double			W;
+	pg_prng_state	randstate;	/* random generator state */
+	bool			randstate_initialized;
 } ReservoirStateData;
 
 typedef ReservoirStateData *ReservoirState;
diff --git a/src/port/Makefile b/src/port/Makefile
index 52dbf5783f..b3754d8940 100644
--- a/src/port/Makefile
+++ b/src/port/Makefile
@@ -42,7 +42,6 @@ OBJS = \
 	$(PG_CRC32C_OBJS) \
 	bsearch_arg.o \
 	chklocale.o \
-	erand48.o \
 	inet_net_ntop.o \
 	noblock.o \
 	path.o \
diff --git a/src/port/erand48.c b/src/port/erand48.c
deleted file mode 100644
index a82ea94220..0000000000
--- a/src/port/erand48.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/port/random.c b/src/port/random.c
deleted file mode 100644
index 2dd59a0829..0000000000
--- a/src/port/random.c
+++ /dev/null
@@ -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();
-}
diff --git a/src/port/srandom.c b/src/port/srandom.c
deleted file mode 100644
index cf1007b2ef..0000000000
--- a/src/port/srandom.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/test/modules/test_bloomfilter/test_bloomfilter.c b/src/test/modules/test_bloomfilter/test_bloomfilter.c
index 96c5011428..69425bd6fc 100644
--- a/src/test/modules/test_bloomfilter/test_bloomfilter.c
+++ b/src/test/modules/test_bloomfilter/test_bloomfilter.c
@@ -13,6 +13,7 @@
 #include "postgres.h"
 
 #include "fmgr.h"
+#include "common/pg_prng.h"
 #include "lib/bloomfilter.h"
 #include "miscadmin.h"
 
@@ -85,7 +86,7 @@ create_and_test_bloom(int power, int64 nelements, int callerseed)
 	 * 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_i32(&pg_global_prng_state) % PG_INT32_MAX : callerseed;
 
 	/* Create Bloom filter, populate it, and report on false positive rate */
 	filter = bloom_create(nelements, bloom_work_mem, seed);
diff --git a/src/test/modules/test_integerset/test_integerset.c b/src/test/modules/test_integerset/test_integerset.c
index 21c6f49b37..3e8cb7a005 100644
--- a/src/test/modules/test_integerset/test_integerset.c
+++ b/src/test/modules/test_integerset/test_integerset.c
@@ -12,6 +12,7 @@
  */
 #include "postgres.h"
 
+#include "common/pg_prng.h"
 #include "fmgr.h"
 #include "lib/integerset.h"
 #include "miscadmin.h"
@@ -99,12 +100,16 @@ static void check_with_filler(IntegerSet *intset, uint64 x, uint64 value, uint64
 static void test_single_value_and_filler(uint64 value, uint64 filler_min, uint64 filler_max);
 static void test_huge_distances(void);
 
+static pg_prng_state	pr_state;
+
 /*
  * SQL-callable entry point to perform all tests.
  */
 Datum
 test_integerset(PG_FUNCTION_ARGS)
 {
+	pg_prng_strong_seed(&pr_state);
+
 	/* Tests for various corner cases */
 	test_empty();
 	test_huge_distances();
@@ -248,8 +253,7 @@ test_pattern(const test_spec *spec)
 		 * only a small part of the integer space is used.  We would very
 		 * rarely hit values that are actually in the set.
 		 */
-		x = (pg_lrand48() << 31) | pg_lrand48();
-		x = x % (last_int + 1000);
+		x = pg_prng_u64(&pr_state) % (last_int + 1000);
 
 		/* Do we expect this value to be present in the set? */
 		if (x >= last_int)
@@ -571,7 +575,7 @@ test_huge_distances(void)
 	 */
 	while (num_values < 1000)
 	{
-		val += pg_lrand48();
+		val += pg_prng_u32(&pr_state);
 		values[num_values++] = val;
 	}
 
diff --git a/src/test/modules/test_rbtree/test_rbtree.c b/src/test/modules/test_rbtree/test_rbtree.c
index 713ebd1b26..59a571185e 100644
--- a/src/test/modules/test_rbtree/test_rbtree.c
+++ b/src/test/modules/test_rbtree/test_rbtree.c
@@ -14,6 +14,7 @@
 #include "postgres.h"
 
 #include "fmgr.h"
+#include "common/pg_prng.h"
 #include "lib/rbtree.h"
 #include "utils/memutils.h"
 
@@ -108,7 +109,7 @@ GetPermutation(int size)
 	 */
 	for (i = 1; i < size; i++)
 	{
-		int			j = random() % (i + 1);
+		int			j = pg_prng_u32(&pg_global_prng_state) % (i + 1);
 
 		if (j < i)				/* avoid fetching undefined data if j=i */
 			permutation[i] = permutation[j];
@@ -320,7 +321,7 @@ testdelete(int size, int delsize)
 
 	for (i = 0; i < delsize; i++)
 	{
-		int			k = random() % size;
+		int			k = pg_prng_u32(&pg_global_prng_state) % size;
 
 		while (chosen[k])
 			k = (k + 1) % size;
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 4362bd44fd..aa048d949f 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -99,9 +99,9 @@ sub mkvcbuild
 	$solution = CreateSolution($vsVersion, $config);
 
 	our @pgportfiles = qw(
-	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c random.c
-	  srandom.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
+	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c
+	  getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
+	  snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
 	  dirent.c dlopen.c getopt.c getopt_long.c link.c
 	  pread.c preadv.c pwrite.c pwritev.c pg_bitutils.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
 	  f2s.c file_perm.c file_utils.c hashfn.c ip.c jsonapi.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
-	  saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c username.c
-	  wait_error.c wchar.c);
+	  pg_get_line.c pg_lzcompress.c pg_prng.c pgfnames.c psprintf.c relpath.c
+	  rmtree.c saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c
+	  username.c wait_error.c wchar.c);
 
 	if ($solution->{options}->{openssl})
 	{
diff --git a/src/tools/testint128.c b/src/tools/testint128.c
index 71c345969a..43d569aeb5 100644
--- a/src/tools/testint128.c
+++ b/src/tools/testint128.c
@@ -27,6 +27,7 @@
 #endif
 
 #include "common/int128.h"
+#include "common/pg_prng.h"
 
 /*
  * We assume the parts of this union are laid out compatibly.
@@ -61,27 +62,11 @@ my_int128_compare(int128 x, int128 y)
 	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.
  *
  * 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 fix the prng state (hmmm...).
  *
  * 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;
 
+	pg_prng_seed(&pg_global_prng_state, 0);
+
 	if (argc >= 2)
 		count = strtol(argv[1], NULL, 0);
 	else
@@ -97,9 +84,9 @@ main(int argc, char **argv)
 
 	while (count-- > 0)
 	{
-		int64		x = get_random_uint64();
-		int64		y = get_random_uint64();
-		int64		z = get_random_uint64();
+		int64		x = pg_prng_u64(&pg_global_prng_state);
+		int64		y = pg_prng_u64(&pg_global_prng_state);
+		int64		z = pg_prng_u64(&pg_global_prng_state);
 		test128		t1;
 		test128		t2;
 
#45Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Fabien COELHO (#44)
1 attachment(s)
Re: rand48 replacement

[1]: http://cfbot.cputube.org/

Indeed. I wish that these results would be available from the cf
interface.

Attached a v11 which might improve things.

Not enough. Here is a v12 which might improve things further.

Not enough. Here is a v13 which might improve things further more.

Not enough. Here is a v14 which might improve things further more again.
Sorry for this noise due to blind windows tests.

--
Fabien.

Attachments:

prng-14.patchtext/x-diff; name=prng-14.patchDownload
diff --git a/configure b/configure
index 7542fe30a1..7dc95e3960 100755
--- a/configure
+++ b/configure
@@ -800,6 +800,7 @@ infodir
 docdir
 oldincludedir
 includedir
+runstatedir
 localstatedir
 sharedstatedir
 sysconfdir
@@ -941,6 +942,7 @@ datadir='${datarootdir}'
 sysconfdir='${prefix}/etc'
 sharedstatedir='${prefix}/com'
 localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
 includedir='${prefix}/include'
 oldincludedir='/usr/include'
 docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@@ -1193,6 +1195,15 @@ do
   | -silent | --silent | --silen | --sile | --sil)
     silent=yes ;;
 
+  -runstatedir | --runstatedir | --runstatedi | --runstated \
+  | --runstate | --runstat | --runsta | --runst | --runs \
+  | --run | --ru | --r)
+    ac_prev=runstatedir ;;
+  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+  | --run=* | --ru=* | --r=*)
+    runstatedir=$ac_optarg ;;
+
   -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
     ac_prev=sbindir ;;
   -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1330,7 +1341,7 @@ fi
 for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
 		datadir sysconfdir sharedstatedir localstatedir includedir \
 		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
-		libdir localedir mandir
+		libdir localedir mandir runstatedir
 do
   eval ac_val=\$$ac_var
   # Remove trailing slashes.
@@ -1483,6 +1494,7 @@ Fine tuning of the installation directories:
   --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
   --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
   --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
   --libdir=DIR            object code libraries [EPREFIX/lib]
   --includedir=DIR        C header files [PREFIX/include]
   --oldincludedir=DIR     C header files for non-gcc [/usr/include]
@@ -15026,7 +15038,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -15072,7 +15084,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -15096,7 +15108,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -15141,7 +15153,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -15165,7 +15177,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -16301,32 +16313,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
diff --git a/configure.ac b/configure.ac
index ed3cdb9a8e..b36b32ed94 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1822,8 +1822,6 @@ AC_REPLACE_FUNCS(m4_normalize([
 	mkdtemp
 	pread
 	pwrite
-	random
-	srandom
 	strlcat
 	strlcpy
 	strnlen
diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c
index d19f73127c..b250ae912b 100644
--- a/contrib/amcheck/verify_nbtree.c
+++ b/contrib/amcheck/verify_nbtree.c
@@ -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"
@@ -465,8 +466,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();
+		/* Random seed relies on backend prng initialization to avoid repetition */
+		seed = pg_prng_u64(&pg_global_prng_state);
 		/* Create Bloom filter to fingerprint index */
 		state->filter = bloom_create(total_elems, maintenance_work_mem, seed);
 		state->heaptuplespresent = 0;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 2c2f149fb0..146b524076 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -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]);
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 76d4fea21c..c139382170 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -5157,7 +5157,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]);
 		}
diff --git a/contrib/tablefunc/tablefunc.c b/contrib/tablefunc/tablefunc.c
index 779bd4415e..fdd117f81e 100644
--- a/contrib/tablefunc/tablefunc.c
+++ b/contrib/tablefunc/tablefunc.c
@@ -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"
@@ -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_f64(&pg_global_prng_state);
+		u2 = pg_prng_f64(&pg_global_prng_state);
 
 		v1 = (2.0 * u1) - 1.0;
 		v2 = (2.0 * u2) - 1.0;
diff --git a/contrib/tsm_system_rows/tsm_system_rows.c b/contrib/tsm_system_rows/tsm_system_rows.c
index 4996612902..1a46d4b143 100644
--- a/contrib/tsm_system_rows/tsm_system_rows.c
+++ b/contrib/tsm_system_rows/tsm_system_rows.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/contrib/tsm_system_time/tsm_system_time.c b/contrib/tsm_system_time/tsm_system_time.c
index 788d8f9a68..36acc6c106 100644
--- a/contrib/tsm_system_time/tsm_system_time.c
+++ b/contrib/tsm_system_time/tsm_system_time.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index 03191e016c..3c2f24b653 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -16,6 +16,7 @@
 
 #include "access/gin_private.h"
 #include "access/relscan.h"
+#include "common/pg_prng.h"
 #include "miscadmin.h"
 #include "storage/predicate.h"
 #include "utils/datum.h"
@@ -787,7 +788,7 @@ entryLoadMoreItems(GinState *ginstate, GinScanEntry entry,
 	}
 }
 
-#define gin_rand() (((double) random()) / ((double) MAX_RANDOM_VALUE))
+#define gin_rand() pg_prng_f64(&pg_global_prng_state)
 #define dropItem(e) ( gin_rand() > ((double)GinFuzzySearchLimit)/((double)((e)->predictNumberResult)) )
 
 /*
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 43ba03b6eb..94dbabc198 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -19,6 +19,7 @@
 #include "access/htup_details.h"
 #include "access/reloptions.h"
 #include "catalog/pg_opclass.h"
+#include "common/pg_prng.h"
 #include "storage/indexfsm.h"
 #include "storage/lmgr.h"
 #include "utils/float.h"
@@ -507,7 +508,7 @@ gistchoose(Relation r, Page p, IndexTuple it,	/* it has compressed entry */
 			if (keep_current_best == -1)
 			{
 				/* we didn't make the random choice yet for this old best */
-				keep_current_best = (random() <= (MAX_RANDOM_VALUE / 2)) ? 1 : 0;
+				keep_current_best = pg_prng_bool(&pg_global_prng_state) ? 1 : 0;
 			}
 			if (keep_current_best == 0)
 			{
@@ -529,7 +530,7 @@ gistchoose(Relation r, Page p, IndexTuple it,	/* it has compressed entry */
 			if (keep_current_best == -1)
 			{
 				/* we didn't make the random choice yet for this old best */
-				keep_current_best = (random() <= (MAX_RANDOM_VALUE / 2)) ? 1 : 0;
+				keep_current_best = pg_prng_bool(&pg_global_prng_state) ? 1 : 0;
 			}
 			if (keep_current_best == 1)
 				break;
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index 7355e1dba1..37d8840ce0 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -19,6 +19,7 @@
 #include "access/nbtxlog.h"
 #include "access/transam.h"
 #include "access/xloginsert.h"
+#include "common/pg_prng.h"
 #include "lib/qunique.h"
 #include "miscadmin.h"
 #include "storage/lmgr.h"
@@ -968,7 +969,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_f64(&pg_global_prng_state) <= 0.01)
 				break;
 
 			_bt_stepright(rel, insertstate, stack);
diff --git a/src/backend/access/spgist/spgdoinsert.c b/src/backend/access/spgist/spgdoinsert.c
index 70557bcf3d..5389c93366 100644
--- a/src/backend/access/spgist/spgdoinsert.c
+++ b/src/backend/access/spgist/spgdoinsert.c
@@ -19,6 +19,7 @@
 #include "access/spgist_private.h"
 #include "access/spgxlog.h"
 #include "access/xloginsert.h"
+#include "common/pg_prng.h"
 #include "miscadmin.h"
 #include "storage/bufmgr.h"
 #include "utils/rel.h"
@@ -2210,7 +2211,7 @@ 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_u32(&pg_global_prng_state) % innerTuple->nNodes;
 			}
 
 			switch (out.resultType)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 6597ec45a9..d0557be7c9 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -37,6 +37,7 @@
 #include "commands/async.h"
 #include "commands/tablecmds.h"
 #include "commands/trigger.h"
+#include "common/pg_prng.h"
 #include "executor/spi.h"
 #include "libpq/be-fsstubs.h"
 #include "libpq/pqsignal.h"
@@ -1939,7 +1940,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_f64(&pg_global_prng_state) <= log_xact_sample_rate);
 
 	/*
 	 * initialize current transaction state fields
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 8bfb2ad958..fe623393d7 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -38,6 +38,7 @@
 #include "commands/progress.h"
 #include "commands/tablecmds.h"
 #include "commands/vacuum.h"
+#include "common/pg_prng.h"
 #include "executor/executor.h"
 #include "foreign/fdwapi.h"
 #include "miscadmin.h"
@@ -1173,7 +1174,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 	OldestXmin = GetOldestNonRemovableTransactionId(onerel);
 
 	/* Prepare for sampling block numbers */
-	randseed = random();
+	randseed = pg_prng_i32(&pg_global_prng_state);
 	nblocks = BlockSampler_Init(&bs, totalblocks, targrows, randseed);
 
 #ifdef USE_PREFETCH
@@ -1290,7 +1291,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 					 * Found a suitable tuple, so save it, replacing one old
 					 * tuple at random
 					 */
-					int			k = (int) (targrows * sampler_random_fract(rstate.randstate));
+					int			k = (int) (targrows * sampler_random_fract(&rstate.randstate));
 
 					Assert(k >= 0 && k < targrows);
 					heap_freetuple(rows[k]);
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 44232d50d0..09c34e96fa 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -17,6 +17,7 @@
 #include "access/relscan.h"
 #include "access/tableam.h"
 #include "access/tsmapi.h"
+#include "common/pg_prng.h"
 #include "executor/executor.h"
 #include "executor/nodeSamplescan.h"
 #include "miscadmin.h"
@@ -154,7 +155,7 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	 * do this just once, since the seed shouldn't change over rescans.
 	 */
 	if (tsc->repeatable == NULL)
-		scanstate->seed = random();
+		scanstate->seed = pg_prng_i32(&pg_global_prng_state);
 
 	/*
 	 * Finally, initialize the TABLESAMPLE method handler.
diff --git a/src/backend/optimizer/geqo/geqo_random.c b/src/backend/optimizer/geqo/geqo_random.c
index f21bc047e6..8b42a9ffba 100644
--- a/src/backend/optimizer/geqo/geqo_random.c
+++ b/src/backend/optimizer/geqo/geqo_random.c
@@ -21,14 +21,7 @@ geqo_set_seed(PlannerInfo *root, double seed)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	/*
-	 * XXX. This seeding algorithm could certainly be improved - but it is not
-	 * critical to do so.
-	 */
-	memset(private->random_state, 0, sizeof(private->random_state));
-	memcpy(private->random_state,
-		   &seed,
-		   Min(sizeof(private->random_state), sizeof(seed)));
+	pg_prng_fseed(&private->random_state, seed);
 }
 
 double
@@ -36,5 +29,5 @@ geqo_rand(PlannerInfo *root)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	return pg_erand48(private->random_state);
+	return pg_prng_f64(&private->random_state);
 }
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index e2a76ba055..13df3cc2e4 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -98,6 +98,7 @@
 #include "catalog/pg_control.h"
 #include "common/file_perm.h"
 #include "common/ip.h"
+#include "common/pg_prng.h"
 #include "common/string.h"
 #include "lib/ilist.h"
 #include "libpq/auth.h"
@@ -2675,19 +2676,22 @@ ClosePostmasterPorts(bool am_syslogger)
 void
 InitProcessGlobals(void)
 {
-	unsigned int rseed;
-
 	MyProcPid = getpid();
 	MyStartTimestamp = GetCurrentTimestamp();
 	MyStartTime = timestamptz_to_time_t(MyStartTimestamp);
 
 	/*
-	 * Set a different seed for random() in every process.  We want something
+	 * Set a different global seed in every process.  We want something
 	 * unpredictable, so if possible, use high-quality random bits for the
 	 * seed.  Otherwise, fall back to a seed based on timestamp and PID.
 	 */
-	if (!pg_strong_random(&rseed, sizeof(rseed)))
+	if (!pg_prng_strong_seed(&pg_global_prng_state))
 	{
+		uint64	rseed;
+
+		ereport(WARNING,
+				(errmsg_internal("pg_prng_strong_seed() failed, falling back to weaker seeding")));
+
 		/*
 		 * Since PIDs and timestamps tend to change more frequently in their
 		 * least significant bits, shift the timestamp left to allow a larger
@@ -2698,8 +2702,9 @@ InitProcessGlobals(void)
 		rseed = ((uint64) MyProcPid) ^
 			((uint64) MyStartTimestamp << 12) ^
 			((uint64) MyStartTimestamp >> 20);
+
+		pg_prng_seed(&pg_global_prng_state, rseed);
 	}
-	srandom(rseed);
 }
 
 
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index f9cda6906d..431931bcfe 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -92,6 +92,7 @@
 #include "catalog/pg_tablespace.h"
 #include "common/file_perm.h"
 #include "common/file_utils.h"
+#include "common/pg_prng.h"
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "port/pg_iovec.h"
@@ -2938,7 +2939,7 @@ SetTempTablespaces(Oid *tableSpaces, int numSpaces)
 	 * available tablespaces.
 	 */
 	if (numSpaces > 1)
-		nextTempTableSpace = random() % numSpaces;
+		nextTempTableSpace = pg_prng_u32(&pg_global_prng_state) % numSpaces;
 	else
 		nextTempTableSpace = 0;
 }
diff --git a/src/backend/storage/ipc/dsm.c b/src/backend/storage/ipc/dsm.c
index b461a5f7e9..e97844614e 100644
--- a/src/backend/storage/ipc/dsm.c
+++ b/src/backend/storage/ipc/dsm.c
@@ -33,6 +33,7 @@
 #endif
 #include <sys/stat.h>
 
+#include "common/pg_prng.h"
 #include "lib/ilist.h"
 #include "miscadmin.h"
 #include "port/pg_bitutils.h"
@@ -180,7 +181,7 @@ dsm_postmaster_startup(PGShmemHeader *shim)
 	{
 		Assert(dsm_control_address == NULL);
 		Assert(dsm_control_mapped_size == 0);
-		dsm_control_handle = random() << 1; /* Even numbers only */
+		dsm_control_handle = pg_prng_u32(&pg_global_prng_state) << 1; /* Even numbers only */
 		if (dsm_control_handle == DSM_HANDLE_INVALID)
 			continue;
 		if (dsm_impl_op(DSM_OP_CREATE, dsm_control_handle, segsize,
@@ -536,7 +537,7 @@ dsm_create(Size size, int flags)
 		for (;;)
 		{
 			Assert(seg->mapped_address == NULL && seg->mapped_size == 0);
-			seg->handle = random() << 1;	/* Even numbers only */
+			seg->handle = pg_prng_u32(&pg_global_prng_state) << 1;	/* Even numbers only */
 			if (seg->handle == DSM_HANDLE_INVALID)	/* Reserve sentinel */
 				continue;
 			if (dsm_impl_op(DSM_OP_CREATE, seg->handle, size, &seg->impl_private,
@@ -1237,7 +1238,7 @@ make_main_region_dsm_handle(int slot)
 	 */
 	handle = 1;
 	handle |= slot << 1;
-	handle |= random() << (pg_leftmost_one_pos32(dsm_control->maxitems) + 1);
+	handle |= pg_prng_i32(&pg_global_prng_state) << (pg_leftmost_one_pos32(dsm_control->maxitems) + 1);
 	return handle;
 }
 
diff --git a/src/backend/storage/lmgr/s_lock.c b/src/backend/storage/lmgr/s_lock.c
index 91322a40c1..74c6f251ce 100644
--- a/src/backend/storage/lmgr/s_lock.c
+++ b/src/backend/storage/lmgr/s_lock.c
@@ -50,6 +50,7 @@
 #include <time.h>
 #include <unistd.h>
 
+#include "common/pg_prng.h"
 #include "port/atomics.h"
 #include "storage/s_lock.h"
 
@@ -63,6 +64,7 @@
 slock_t		dummy_spinlock;
 
 static int	spins_per_delay = DEFAULT_SPINS_PER_DELAY;
+static pg_prng_state	prng_state;
 
 
 /*
@@ -143,8 +145,7 @@ perform_spin_delay(SpinDelayStatus *status)
 #endif
 
 		/* increase delay by a random fraction between 1X and 2X */
-		status->cur_delay += (int) (status->cur_delay *
-									((double) random() / (double) MAX_RANDOM_VALUE) + 0.5);
+		status->cur_delay += (int) (status->cur_delay * (pg_prng_f64(&prng_state) + 0.5));
 		/* wrap back to minimum delay when max is exceeded */
 		if (status->cur_delay > MAX_DELAY_USEC)
 			status->cur_delay = MIN_DELAY_USEC;
@@ -303,7 +304,7 @@ volatile struct test_lock_struct test_lock;
 int
 main()
 {
-	srandom((unsigned int) time(NULL));
+	pg_prng_seed(&prng_state, (unsigned int) time(NULL));
 
 	test_lock.pad1 = test_lock.pad2 = 0x44;
 
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 0775abe35d..ef6c27dade 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -42,6 +42,7 @@
 #include "catalog/pg_type.h"
 #include "commands/async.h"
 #include "commands/prepare.h"
+#include "common/pg_prng.h"
 #include "executor/spi.h"
 #include "jit/jit.h"
 #include "libpq/libpq.h"
@@ -2355,13 +2356,13 @@ check_log_duration(char *msec_str, bool was_logged)
 
 		/*
 		 * Do not log if log_statement_sample_rate = 0. Log a sample if
-		 * log_statement_sample_rate <= 1 and avoid unnecessary random() call
+		 * log_statement_sample_rate <= 1 and avoid unnecessary random call
 		 * if log_statement_sample_rate = 1.
 		 */
 		if (exceeded_sample_duration)
 			in_sample = log_statement_sample_rate != 0 &&
 				(log_statement_sample_rate == 1 ||
-				 random() <= log_statement_sample_rate * MAX_RANDOM_VALUE);
+				 pg_prng_f64(&pg_global_prng_state) <= log_statement_sample_rate);
 
 		if (exceeded_duration || in_sample || log_duration || xact_is_sampled)
 		{
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 098bbb372b..9fc0b108dd 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -21,6 +21,7 @@
 
 #include "catalog/pg_type.h"
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/shortest_dec.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
@@ -65,7 +66,7 @@ float8		degree_c_one = 1.0;
 
 /* State for drandom() and setseed() */
 static bool drandom_seed_set = false;
-static unsigned short drandom_seed[3] = {0, 0, 0};
+static pg_prng_state drandom_seed = PG_PRNG_DEFAULT_STATE;
 
 /* Local function prototypes */
 static double sind_q1(double x);
@@ -2762,22 +2763,20 @@ drandom(PG_FUNCTION_ARGS)
 		 * Should that fail for some reason, we fall back on a lower-quality
 		 * seed based on current time and PID.
 		 */
-		if (!pg_strong_random(drandom_seed, sizeof(drandom_seed)))
+		if (!pg_strong_random(&drandom_seed, sizeof(drandom_seed)))
 		{
 			TimestampTz now = GetCurrentTimestamp();
 			uint64		iseed;
 
 			/* Mix the PID with the most predictable bits of the timestamp */
 			iseed = (uint64) now ^ ((uint64) MyProcPid << 32);
-			drandom_seed[0] = (unsigned short) iseed;
-			drandom_seed[1] = (unsigned short) (iseed >> 16);
-			drandom_seed[2] = (unsigned short) (iseed >> 32);
+			pg_prng_seed(&drandom_seed, iseed);
 		}
 		drandom_seed_set = true;
 	}
 
-	/* pg_erand48 produces desired result range [0.0 - 1.0) */
-	result = pg_erand48(drandom_seed);
+	/* produces desired result range [0.0 - 1.0) */
+	result = pg_prng_f64(&drandom_seed);
 
 	PG_RETURN_FLOAT8(result);
 }
@@ -2790,7 +2789,6 @@ Datum
 setseed(PG_FUNCTION_ARGS)
 {
 	float8		seed = PG_GETARG_FLOAT8(0);
-	uint64		iseed;
 
 	if (seed < -1 || seed > 1 || isnan(seed))
 		ereport(ERROR,
@@ -2798,11 +2796,7 @@ setseed(PG_FUNCTION_ARGS)
 				 errmsg("setseed parameter %g is out of allowed range [-1,1]",
 						seed)));
 
-	/* Use sign bit + 47 fractional bits to fill drandom_seed[] */
-	iseed = (int64) (seed * (float8) UINT64CONST(0x7FFFFFFFFFFF));
-	drandom_seed[0] = (unsigned short) iseed;
-	drandom_seed[1] = (unsigned short) (iseed >> 16);
-	drandom_seed[2] = (unsigned short) (iseed >> 32);
+	pg_prng_fseed(&drandom_seed, seed);
 	drandom_seed_set = true;
 
 	PG_RETURN_VOID();
diff --git a/src/backend/utils/misc/sampling.c b/src/backend/utils/misc/sampling.c
index 0c327e823f..f1c86150d8 100644
--- a/src/backend/utils/misc/sampling.c
+++ b/src/backend/utils/misc/sampling.c
@@ -49,7 +49,7 @@ BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize,
 	bs->t = 0;					/* blocks scanned so far */
 	bs->m = 0;					/* blocks selected so far */
 
-	sampler_random_init_state(randseed, bs->randstate);
+	sampler_random_init_state(randseed, &bs->randstate);
 
 	return Min(bs->n, bs->N);
 }
@@ -98,7 +98,7 @@ BlockSampler_Next(BlockSampler bs)
 	 * less than k, which means that we cannot fail to select enough blocks.
 	 *----------
 	 */
-	V = sampler_random_fract(bs->randstate);
+	V = sampler_random_fract(&bs->randstate);
 	p = 1.0 - (double) k / (double) K;
 	while (V < p)
 	{
@@ -136,10 +136,10 @@ reservoir_init_selection_state(ReservoirState rs, int n)
 	 * Reservoir sampling is not used anywhere where it would need to return
 	 * repeatable results so we can initialize it randomly.
 	 */
-	sampler_random_init_state(random(), rs->randstate);
+	sampler_random_init_state(pg_prng_u32(&pg_global_prng_state), &rs->randstate);
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	rs->W = exp(-log(sampler_random_fract(rs->randstate)) / n);
+	rs->W = exp(-log(sampler_random_fract(&rs->randstate)) / n);
 }
 
 double
@@ -154,7 +154,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 		double		V,
 					quot;
 
-		V = sampler_random_fract(rs->randstate);	/* Generate V */
+		V = sampler_random_fract(&rs->randstate);	/* Generate V */
 		S = 0;
 		t += 1;
 		/* Note: "num" in Vitter's code is always equal to t - n */
@@ -186,7 +186,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 						tmp;
 
 			/* Generate U and X */
-			U = sampler_random_fract(rs->randstate);
+			U = sampler_random_fract(&rs->randstate);
 			X = t * (W - 1.0);
 			S = floor(X);		/* S is tentatively set to floor(X) */
 			/* Test if U <= h(S)/cg(X) in the manner of (6.3) */
@@ -215,7 +215,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 				y *= numer / denom;
 				denom -= 1;
 			}
-			W = exp(-log(sampler_random_fract(rs->randstate)) / n); /* Generate W in advance */
+			W = exp(-log(sampler_random_fract(&rs->randstate)) / n); /* Generate W in advance */
 			if (exp(log(y) / n) <= (t + X) / t)
 				break;
 		}
@@ -230,24 +230,22 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
  *----------
  */
 void
-sampler_random_init_state(long seed, SamplerRandomState randstate)
+sampler_random_init_state(long seed, pg_prng_state *randstate)
 {
-	randstate[0] = 0x330e;		/* same as pg_erand48, but could be anything */
-	randstate[1] = (unsigned short) seed;
-	randstate[2] = (unsigned short) (seed >> 16);
+	pg_prng_seed(randstate, (uint64_t) seed);
 }
 
 /* Select a random value R uniformly distributed in (0 - 1) */
 double
-sampler_random_fract(SamplerRandomState randstate)
+sampler_random_fract(pg_prng_state *randstate)
 {
 	double		res;
 
-	/* pg_erand48 returns a value in [0.0 - 1.0), so we must reject 0 */
+	/* pg_prng_f64 returned value in [0.0 - 1.0), so we must reject 0.0 */
 	do
 	{
-		res = pg_erand48(randstate);
-	} while (res == 0.0);
+		res = pg_prng_f64(randstate);
+	} while (unlikely(res == 0.0));
 	return res;
 }
 
@@ -266,22 +264,28 @@ double
 anl_random_fract(void)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(pg_prng_u32(&pg_global_prng_state), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* and compute a random fraction */
-	return sampler_random_fract(oldrs.randstate);
+	return sampler_random_fract(&oldrs.randstate);
 }
 
 double
 anl_init_selection_state(int n)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(pg_prng_u32(&pg_global_prng_state), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	return exp(-log(sampler_random_fract(oldrs.randstate)) / n);
+	return exp(-log(sampler_random_fract(&oldrs.randstate)) / n);
 }
 
 double
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 1ed4808d53..af9f5169e9 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -66,6 +66,7 @@
 #include "common/file_perm.h"
 #include "common/file_utils.h"
 #include "common/logging.h"
+#include "common/pg_prng.h"
 #include "common/restricted_token.h"
 #include "common/string.h"
 #include "common/username.h"
@@ -880,9 +881,10 @@ choose_dsm_implementation(void)
 {
 #ifdef HAVE_SHM_OPEN
 	int			ntries = 10;
+	pg_prng_state	prng_state;
 
-	/* Initialize random(); this function is its only user in this program. */
-	srandom((unsigned int) (getpid() ^ time(NULL)));
+	/* Initialize pgng state this function is its only user in this program. */
+	pg_prng_seed(&prng_state, ((unsigned int) (getpid() ^ time(NULL))));
 
 	while (ntries > 0)
 	{
@@ -890,7 +892,7 @@ choose_dsm_implementation(void)
 		char		name[64];
 		int			fd;
 
-		handle = random();
+		handle = pg_prng_u32(&prng_state);
 		snprintf(name, 64, "/PostgreSQL.%u", handle);
 		if ((fd = shm_open(name, O_CREAT | O_RDWR | O_EXCL, 0600)) != -1)
 		{
diff --git a/src/bin/pg_test_fsync/pg_test_fsync.c b/src/bin/pg_test_fsync/pg_test_fsync.c
index fef31844fa..5b3c197aa6 100644
--- a/src/bin/pg_test_fsync/pg_test_fsync.c
+++ b/src/bin/pg_test_fsync/pg_test_fsync.c
@@ -15,6 +15,7 @@
 
 #include "access/xlogdefs.h"
 #include "common/logging.h"
+#include "common/pg_prng.h"
 #include "getopt_long.h"
 
 /*
@@ -71,6 +72,7 @@ static char full_buf[DEFAULT_XLOG_SEG_SIZE],
 static struct timeval start_t,
 			stop_t;
 static bool alarm_triggered = false;
+static pg_prng_state	prng_state;
 
 
 static void handle_args(int argc, char *argv[]);
@@ -117,6 +119,7 @@ main(int argc, char *argv[])
 	pqsignal(SIGHUP, signal_cleanup);
 #endif
 
+	pg_prng_strong_seed(&prng_state);
 	prepare_buf();
 
 	test_open();
@@ -233,7 +236,7 @@ prepare_buf(void)
 
 	/* write random data into buffer */
 	for (ops = 0; ops < DEFAULT_XLOG_SEG_SIZE; ops++)
-		full_buf[ops] = random();
+		full_buf[ops] = pg_prng_u32(&prng_state);
 
 	buf = (char *) TYPEALIGN(XLOG_BLCKSZ, full_buf);
 }
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 433abd954b..64c7370ff5 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -58,6 +58,7 @@
 #endif
 
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/logging.h"
 #include "common/string.h"
 #include "common/username.h"
@@ -350,16 +351,8 @@ typedef struct StatsData
  */
 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. */
-static RandomState base_random_sequence;
+static pg_prng_state	base_random_sequence;
 
 /* Synchronization barrier for start and connection */
 static THREAD_BARRIER_T barrier;
@@ -461,7 +454,7 @@ typedef struct
 	 * Separate randomness for each client. This is used for random functions
 	 * 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			command;		/* command number in script */
@@ -498,9 +491,9 @@ typedef struct
 	 * random state to make all of them independent of each other and
 	 * therefore deterministic at the thread level.
 	 */
-	RandomState ts_choose_rs;	/* random state for selecting a script */
-	RandomState ts_throttle_rs; /* random state for transaction throttling */
-	RandomState ts_sample_rs;	/* random state for log sampling */
+	pg_prng_state	ts_choose_rs;	/* random state for selecting a script */
+	pg_prng_state	ts_throttle_rs; /* random state for transaction throttling */
+	pg_prng_state	ts_sample_rs;	/* random state for log sampling */
 
 	int64		throttle_trigger;	/* previous/next throttling (us) */
 	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.
  */
 static void
-initRandomState(RandomState *random_state)
+initRandomState(pg_prng_state *state)
 {
-	random_state->xseed[0] = (unsigned short)
-		(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);
+	pg_prng_seed(state, pg_prng_u64(&base_random_sequence));
 }
 
+
 /*
- * Random number generator: uniform distribution from min to max inclusive.
+ * PRNG: uniform distribution from min to max inclusive.
  *
  * Although the limits are expressed as int64, you can't generate the full
  * int64 range in one call, because the difference of the limits mustn't
- * overflow int64.  In practice it's unwise to ask for more than an int32
- * range, because of the limited precision of pg_erand48().
+ * overflow int64. This is not checked.
  */
 static int64
-getrand(RandomState *random_state, int64 min, int64 max)
+getrand(pg_prng_state *state, int64 min, int64 max)
 {
-	/*
-	 * 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));
+	return min + (int64) pg_prng_u64_range(state, 0, max - min);
 }
 
 /*
@@ -942,8 +921,7 @@ getrand(RandomState *random_state, int64 min, int64 max)
  * value is exp(-parameter).
  */
 static int64
-getExponentialRand(RandomState *random_state, int64 min, int64 max,
-				   double parameter)
+getExponentialRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		cut,
 				uniform,
@@ -953,7 +931,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 	Assert(parameter > 0.0);
 	cut = exp(-parameter);
 	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	uniform = 1.0 - pg_prng_f64(state);
 
 	/*
 	 * inner expression in (cut, 1] (if parameter > 0), rand in [0, 1)
@@ -966,8 +944,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 
 /* random number generator: gaussian distribution from min to max inclusive */
 static int64
-getGaussianRand(RandomState *random_state, int64 min, int64 max,
-				double parameter)
+getGaussianRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		stdev;
 	double		rand;
@@ -990,13 +967,13 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
 	do
 	{
 		/*
-		 * pg_erand48 generates [0,1), but for the basic version of the
+		 * pg_prng_f64 generates [0,1), but for the basic version of the
 		 * Box-Muller transform the two uniformly distributed random numbers
 		 * are expected in (0, 1] (see
 		 * https://en.wikipedia.org/wiki/Box-Muller_transform)
 		 */
-		double		rand1 = 1.0 - pg_erand48(random_state->xseed);
-		double		rand2 = 1.0 - pg_erand48(random_state->xseed);
+		double		rand1 = 1.0 - pg_prng_f64(state);
+		double		rand2 = 1.0 - pg_prng_f64(state);
 
 		/* Box-Muller basic form transform */
 		double		var_sqrt = sqrt(-2.0 * log(rand1));
@@ -1026,7 +1003,7 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
  * not be one.
  */
 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
@@ -1034,8 +1011,8 @@ getPoissonRand(RandomState *random_state, double center)
 	 */
 	double		uniform;
 
-	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	/* pseudo-random value in [0, 1), uniform in (0, 1] */
+	uniform = 1.0 - pg_prng_f64(state);
 
 	return (int64) (-log(uniform) * center + 0.5);
 }
@@ -1048,7 +1025,7 @@ getPoissonRand(RandomState *random_state, double center)
  * This works for s > 1.0, but may perform badly for s very close to 1.0.
  */
 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		x,
@@ -1063,8 +1040,8 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 	while (true)
 	{
 		/* random variates */
-		u = pg_erand48(random_state->xseed);
-		v = pg_erand48(random_state->xseed);
+		u = pg_prng_f64(state);
+		v = pg_prng_f64(state);
 
 		x = floor(pow(u, -1.0 / (s - 1.0)));
 
@@ -1078,14 +1055,14 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 
 /* random number generator: zipfian distribution from min to max inclusive */
 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;
 
 	/* abort if parameter is invalid */
 	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 +1119,7 @@ getHashMurmur2(int64 val, uint64 seed)
  * For small sizes, this generates each of the (size!) possible permutations
  * of integers in the range [0, size) with roughly equal probability.  Once
  * the size is larger than 20, the number of possible permutations exceeds the
- * number of distinct states of the internal pseudorandom number generators,
+ * number of distinct states of the internal pseudorandom number generator,
  * and so not all possible permutations can be generated, but the permutations
  * chosen should continue to give the appearance of being random.
  *
@@ -1152,25 +1129,19 @@ getHashMurmur2(int64 val, uint64 seed)
 static int64
 permute(const int64 val, const int64 isize, const int64 seed)
 {
-	RandomState random_state1;
-	RandomState random_state2;
-	uint64		size;
-	uint64		v;
-	int			masklen;
-	uint64		mask;
-	int			i;
+	/* using a high-end PRNG is probably overkill */
+	pg_prng_state	state;
+	uint64			size;
+	uint64			v;
+	int				masklen;
+	uint64			mask;
+	int				i;
 
 	if (isize < 2)
 		return 0;				/* nothing to permute */
 
-	/* Initialize a pair of random states using the seed */
-	random_state1.xseed[0] = seed & 0xFFFF;
-	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;
+	/* Initialize prng state using the seed */
+	pg_prng_seed(&state, (uint64) seed);
 
 	/* Computations are performed on unsigned values */
 	size = (uint64) isize;
@@ -1216,8 +1187,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 					t;
 
 		/* Random multiply (by an odd number), XOR and rotate of lower half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		if (v <= mask)
 		{
 			v = ((v * m) ^ r) & mask;
@@ -1225,8 +1196,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random multiply (by an odd number), XOR and rotate of upper half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		t = size - 1 - v;
 		if (t <= mask)
 		{
@@ -1236,7 +1207,7 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random offset */
-		r = (uint64) getrand(&random_state2, 0, size - 1);
+		r = pg_prng_u64(&state) % (size - 1);
 		v = (v + r) % size;
 	}
 
@@ -3827,7 +3798,7 @@ doLog(TState *thread, CState *st,
 	 * to the random sample.
 	 */
 	if (sample_rate != 0.0 &&
-		pg_erand48(thread->ts_sample_rs.xseed) > sample_rate)
+		pg_prng_f64(&thread->ts_sample_rs) > sample_rate)
 		return;
 
 	/* should we aggregate the results or not? */
@@ -5763,12 +5734,11 @@ set_random_seed(const char *seed)
 
 	if (seed != NULL)
 		pg_log_info("setting random seed to %llu", (unsigned long long) iseed);
+
 	random_seed = iseed;
 
 	/* Fill base_random_sequence with low-order bits of seed */
-	base_random_sequence.xseed[0] = iseed & 0xFFFF;
-	base_random_sequence.xseed[1] = (iseed >> 16) & 0xFFFF;
-	base_random_sequence.xseed[2] = (iseed >> 32) & 0xFFFF;
+	pg_prng_seed(&base_random_sequence, (uint64_t) iseed);
 
 	return true;
 }
@@ -6439,9 +6409,7 @@ main(int argc, char **argv)
 	/* set default seed for hash functions */
 	if (lookupVariable(&state[0], "default_seed") == NULL)
 	{
-		uint64		seed =
-		((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) |
-		(((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) << 32);
+		uint64		seed = pg_prng_u64(&base_random_sequence);
 
 		for (i = 0; i < nclients; i++)
 			if (!putVariableInt(&state[i], "startup", "default_seed", (int64) seed))
diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl
index ef53f6b2d9..13c9204a3d 100644
--- a/src/bin/pgbench/t/001_pgbench_with_server.pl
+++ b/src/bin/pgbench/t/001_pgbench_with_server.pl
@@ -427,9 +427,9 @@ pgbench(
 
 		# After explicit seeding, the four random checks (1-3,20) are
 		# deterministic
-		qr{command=1.: int 13\b},      # uniform random
-		qr{command=2.: int 116\b},     # exponential random
-		qr{command=3.: int 1498\b},    # gaussian random
+		qr{command=1.: int 17\b},      # uniform random
+		qr{command=2.: int 108\b},     # exponential random
+		qr{command=3.: int 1452\b},    # gaussian random
 		qr{command=4.: int 4\b},
 		qr{command=5.: int 5\b},
 		qr{command=6.: int 6\b},
@@ -496,6 +496,7 @@ pgbench(
 		qr{command=109.: boolean true\b},
 		qr{command=110.: boolean true\b},
 		qr{command=111.: boolean true\b},
+		qr{command=113.: boolean true\b},
 	],
 	'pgbench expressions',
 	{
@@ -639,8 +640,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))
 -- actual values
 \set t debug(permute(:v, 1) = 0)
-\set t debug(permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1 and \
-             permute(0, 2, 5435) = 1 and permute(1, 2, 5435) = 0)
+\set t debug(permute(0, 2, 5431) = 1 and permute(1, 2, 5431) = 0 and \
+             permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1)
+-- 63 bits tests for checking permute portability across architectures
+\set size debug(:max - 10)
+\set t debug(permute(:size-1, :size, 5432) = 4352241160009873020 and \
+             permute(:size-2, :size, 5432) = 4990004119691638710 and \
+             permute(:size-3, :size, 5432) = 5868029867256127549 and \
+             permute(:size-4, :size, 5432) = 1238830324886341378 and \
+             permute(:size-5, :size, 5432) = 7415914367102906897 and \
+             permute(:size-6, :size, 5432) = 3214501037984818995 and \
+             permute(:size-7, :size, 5432) =  600660351261124695)
 }
 	});
 
diff --git a/src/common/Makefile b/src/common/Makefile
index 880722fcf5..31c0dd366d 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -66,6 +66,7 @@ OBJS_COMMON = \
 	md5_common.o \
 	pg_get_line.o \
 	pg_lzcompress.o \
+	pg_prng.o \
 	pgfnames.o \
 	psprintf.o \
 	relpath.o \
diff --git a/src/common/pg_prng.c b/src/common/pg_prng.c
new file mode 100644
index 0000000000..df2054af79
--- /dev/null
+++ b/src/common/pg_prng.c
@@ -0,0 +1,244 @@
+/*
+ * Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * Previous version was the 1988 (?) rand48 standard with 48 bits state LCG,
+ * designed for running on 32 bits or even 16 bits architectures, and to produce
+ * 32 bit ints and floats (i.e. *not* doubles).
+ *
+ * The implied 16-bits unpacking/packing may have a detrimental performance impact
+ * on modern 64 bits architectures.
+ *
+ * We (probably) want:
+ * - one reasonable default PRNG for all pg internal uses.
+ * - NOT to invent a new design!
+ * - something fast, close to rand48 (which basically does 2 arithmetic ops)
+ *   no need for something cryptographic though, which would imply slow
+ * - to produce 64 bits integers & doubles with a 52 bits mantissa
+ *   so state size > 64 bits.
+ * - a small state though, because we might generate quite a few of them
+ *   for different purposes so state size <= 256 or even <= 128 bits
+ * - the state to be aligned to whatever, so state size = 128 bits
+ * - 64 bits operations for efficiency on modern architectures,
+ *   but NOT 128 bits operations.
+ * - not to depend on special hardware for speed (eg MMX/SSE/AES).
+ * - not something with obvious known and relevant defects.
+ * - not something with "rights" attached.
+ *
+ * These constraints reduce a lot the available options from
+ * https://en.wikipedia.org/wiki/List_of_random_number_generators
+ *
+ * LCG
+ * - POSIX rand48: m = 2^48, a = 25214903917, c = 11, extract 47...16
+ * - MMIX by Donald Knuth: m = 2^64, a = 6364136223846793005, c = 1442695040888963407
+ * - very bad on low bits, which MUST NOT BE USED
+ *   a 128 bits would do, but no clear standards, and it would imply 128-bit ops,
+ *   see PCG below.
+ *
+ * PCG Family (https://www.pcg-random.org/)
+ * - this is basically an LCG with an improved outpout function
+ *   so as to avoid typical LCG issues and pass stats tests.
+ * - recommands state size to be twice the output size,
+ *   which seems a reasonable requirement.
+ * - this implies 128 bit ops for our target parameters,
+ *   otherwise it is a fairly good candidate for simplicity,
+ *   efficiency and quality.
+ *
+ * Xor-Shift-Rotate PRNGs:
+ * - a generic annoying feature of this generator class is that there is one
+ *   special all-zero state which must not be used, inducing a constraint on
+ *   initialization, and when the generator get close to it (i.e. many zeros
+ *   in the state) it takes some iterations to recover, with small lumps
+ *   of close/related/bad numbers generated in the process.
+ * Xoroshiro128+
+ * - fails statistical linearity tests in the low bits.
+ * Xoroshiro128**
+ *   https://prng.di.unimi.it/xoroshiro128starstar.c
+ * - mild and irrelevant issues: close to zero state, stats issues after some transforms
+ * Xoshiro256**
+ * - it looks good, but some issues (no big deal for pg usage IMO)
+ *   https://www.pcg-random.org/posts/a-quick-look-at-xoshiro256.html
+ *   mild problems getting out of closes-to-zero state
+ *
+ * MT19937-64
+ * + default in many places, standard
+ * - huge 2.5 KiB state (but TinyMT, 127 bits state space)
+ * - slow (but SFMT, twice as fast)
+ * - vulnerabilities on tinyMT?
+ *
+ * WELL
+ * - large state, 32-bit oriented
+ *
+ * ACORN Family (https://en.wikipedia.org/wiki/ACORN_(PRNG))
+ * - Fortran / double oriented, no C implementation found?!
+ * - medium large state "In practice we recommend using k > 10, M = 2^60 (for general application)
+ *   or M = 2^120 (for demanding applications requiring high-quality pseudo-random numbers
+ *   that will consistently pass all the tests in standard test packages such as TestU01)
+ *   and choose any odd value less than M for the seed."
+ *   k = 11 => 88 bytes/704-bits state, too large:-(
+ *
+ * Splitmix?
+ * Others?
+ *
+ * The conclusion is that we use xor-shift-rotate generator below.
+ */
+
+#include "c.h"
+#include "common/pg_prng.h"
+#include "port/pg_bitutils.h"
+#include <math.h>
+
+/* global state */
+pg_prng_state	pg_global_prng_state;
+
+#define FIRST_BIT_MASK UINT64CONST(0x8000000000000000)
+#define RIGHT_HALF_MASK UINT64CONST(0x00000000FFFFFFFF)
+#define DMANTISSA_MASK UINT64CONST(0x000FFFFFFFFFFFFF)
+
+/* 64-bits rotate left */
+static inline uint64
+rotl(const uint64 x, const int bits)
+{
+	return (x << bits) | (x >> (64 - bits));
+}
+
+/* another 64-bits generator is used for state initialization or iterations */
+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);
+}
+
+/* seed prng state from a 64 bits integer, ensuring non zero */
+void
+pg_prng_seed(pg_prng_state *state, uint64 seed)
+{
+	state->s0 = splitmix64(&seed);
+	state->s1 = splitmix64(&seed);
+}
+
+/* seed with 53 bits (mantissa & sign) from a float */
+void
+pg_prng_fseed(pg_prng_state *state, double fseed)
+{
+	uint64 seed = (int64) (((double) DMANTISSA_MASK) * fseed);
+	pg_prng_seed(state, seed);
+}
+
+/* strong random seeding */
+bool
+pg_prng_strong_seed(pg_prng_state *state)
+{
+	bool ok = pg_strong_random((void *) state, sizeof(pg_prng_state));
+
+	/* avoid zero with Donald Knuth's LCG parameters */
+	if (unlikely(state->s0 == 0 && state->s1 == 0))
+	{
+		/* should it warn that something is amiss if we get there? */
+		pg_prng_state def = PG_PRNG_DEFAULT_STATE;
+		*state = def;
+	}
+
+	return ok;
+}
+
+/* generator & state update */
+static uint64
+xoroshiro128ss(pg_prng_state *state)
+{
+	const 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;
+}
+
+/* u64 generator */
+uint64
+pg_prng_u64(pg_prng_state *state)
+{
+	return xoroshiro128ss(state);
+}
+
+/*
+ * select in a range with bitmask rejection.
+ *
+ * if range is empty, rmin is returned.
+ *
+ * the prng may advance by several states or none,
+ * depending on the range value.
+ */
+uint64
+pg_prng_u64_range(pg_prng_state *state, uint64 rmin, uint64 rmax)
+{
+	uint64 val;
+
+	if (likely(rmax > rmin))
+	{
+		uint64 range = rmax - rmin;
+
+		/* bit position is between 0 and 63, so rshift >= 0 */
+		uint32 rshift = 63 - pg_leftmost_one_pos64(range);
+
+		do
+		{
+			val = xoroshiro128ss(state) >> rshift;
+		}
+		while (unlikely(val > range));
+	}
+	else
+		val = 0;
+
+	return rmin + val;
+}
+
+/* i64 generator */
+int64
+pg_prng_i64(pg_prng_state *state)
+{
+	return (int64) xoroshiro128ss(state);
+}
+
+/* u32 generator */
+uint32
+pg_prng_u32(pg_prng_state *state)
+{
+	const uint64 v = xoroshiro128ss(state);
+	return (uint32) (((v >> 32) ^ v) & RIGHT_HALF_MASK);
+}
+
+/* i32 generator */
+int32
+pg_prng_i32(pg_prng_state *state)
+{
+	const uint64 v = xoroshiro128ss(state);
+	return (int32) (((v >> 32) ^ v) & RIGHT_HALF_MASK);
+}
+
+/* double generator */
+double
+pg_prng_f64(pg_prng_state *state)
+{
+	uint64 v = xoroshiro128ss(state);
+	return ldexp((double) (v & DMANTISSA_MASK), -52);
+}
+
+/* bool generator */
+bool
+pg_prng_bool(pg_prng_state *state)
+{
+	uint64 v = xoroshiro128ss(state);
+	return (v & FIRST_BIT_MASK) == FIRST_BIT_MASK;
+}
diff --git a/src/include/common/pg_prng.h b/src/include/common/pg_prng.h
new file mode 100644
index 0000000000..55f0e3d6cb
--- /dev/null
+++ b/src/include/common/pg_prng.h
@@ -0,0 +1,37 @@
+/*-------------------------------------------------------------------------
+ *
+ * PRNG: internal Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * src/include/common/pg_prng.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_PRNG_H
+#define PG_PRNG_H
+
+/* 128 bits state */
+typedef struct pg_prng_state {
+	uint64	s0, s1;
+} pg_prng_state;
+
+/* global pseudo-random state */
+extern pg_prng_state	pg_global_prng_state;
+
+/* use Donald Knuth's LCG constants for default state */
+#define PG_PRNG_DEFAULT_STATE \
+	{ UINT64CONST(0x5851F42D4C957F2D), UINT64CONST(0x14057B7EF767814F) }
+
+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_strong_seed(pg_prng_state *state);
+extern uint64 pg_prng_u64(pg_prng_state *state);
+extern uint64 pg_prng_u64_range(pg_prng_state *state, uint64 rmin, uint64 rmax);
+extern int64 pg_prng_i64(pg_prng_state *state);
+extern uint32 pg_prng_u32(pg_prng_state *state);
+extern int32 pg_prng_i32(pg_prng_state *state);
+extern double pg_prng_f64(pg_prng_state *state);
+extern bool pg_prng_bool(pg_prng_state *state);
+
+#endif							/* PG_PRNG_H */
diff --git a/src/include/optimizer/geqo.h b/src/include/optimizer/geqo.h
index 24dcdfb6cc..b28e4f084a 100644
--- a/src/include/optimizer/geqo.h
+++ b/src/include/optimizer/geqo.h
@@ -22,6 +22,7 @@
 #ifndef GEQO_H
 #define GEQO_H
 
+#include "common/pg_prng.h"
 #include "nodes/pathnodes.h"
 #include "optimizer/geqo_gene.h"
 
@@ -72,8 +73,8 @@ extern double Geqo_seed;		/* 0 .. 1 */
  */
 typedef struct
 {
-	List	   *initial_rels;	/* the base relations we are joining */
-	unsigned short random_state[3]; /* state for pg_erand48() */
+	List		   *initial_rels;	/* the base relations we are joining */
+	pg_prng_state	random_state;   /* PRNG state */
 } GeqoPrivateData;
 
 
diff --git a/src/include/port.h b/src/include/port.h
index 82f63de325..ef84d00ea0 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -356,11 +356,6 @@ extern int	gettimeofday(struct timeval *tp, struct timezone *tzp);
 #define pgoff_t off_t
 #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
 extern int	fls(int mask);
 #endif
@@ -446,10 +441,6 @@ extern size_t strlcpy(char *dst, const char *src, size_t siz);
 extern size_t strnlen(const char *str, size_t maxlen);
 #endif
 
-#if !defined(HAVE_RANDOM)
-extern long random(void);
-#endif
-
 #ifndef HAVE_SETENV
 extern int	setenv(const char *name, const char *value, int overwrite);
 #endif
@@ -458,10 +449,6 @@ extern int	setenv(const char *name, const char *value, int overwrite);
 extern int	unsetenv(const char *name);
 #endif
 
-#ifndef HAVE_SRANDOM
-extern void srandom(unsigned int seed);
-#endif
-
 #ifndef HAVE_DLOPEN
 extern void *dlopen(const char *file, int mode);
 extern void *dlsym(void *handle, const char *symbol);
diff --git a/src/include/utils/sampling.h b/src/include/utils/sampling.h
index a58d14281b..c0b1c6a645 100644
--- a/src/include/utils/sampling.h
+++ b/src/include/utils/sampling.h
@@ -13,15 +13,14 @@
 #ifndef SAMPLING_H
 #define SAMPLING_H
 
+#include "common/pg_prng.h"
 #include "storage/block.h"		/* for typedef BlockNumber */
 
 
 /* Random generator for sampling code */
-typedef unsigned short SamplerRandomState[3];
-
 extern void sampler_random_init_state(long seed,
-									  SamplerRandomState randstate);
-extern double sampler_random_fract(SamplerRandomState randstate);
+									  pg_prng_state *randstate);
+extern double sampler_random_fract(pg_prng_state *randstate);
 
 /* Block sampling methods */
 
@@ -32,7 +31,7 @@ typedef struct
 	int			n;				/* desired sample size */
 	BlockNumber t;				/* current block number */
 	int			m;				/* blocks selected so far */
-	SamplerRandomState randstate;	/* random generator state */
+	pg_prng_state randstate;	/* random generator state */
 } BlockSamplerData;
 
 typedef BlockSamplerData *BlockSampler;
@@ -46,8 +45,9 @@ extern BlockNumber BlockSampler_Next(BlockSampler bs);
 
 typedef struct
 {
-	double		W;
-	SamplerRandomState randstate;	/* random generator state */
+	double			W;
+	pg_prng_state	randstate;	/* random generator state */
+	bool			randstate_initialized;
 } ReservoirStateData;
 
 typedef ReservoirStateData *ReservoirState;
diff --git a/src/port/Makefile b/src/port/Makefile
index 52dbf5783f..b3754d8940 100644
--- a/src/port/Makefile
+++ b/src/port/Makefile
@@ -42,7 +42,6 @@ OBJS = \
 	$(PG_CRC32C_OBJS) \
 	bsearch_arg.o \
 	chklocale.o \
-	erand48.o \
 	inet_net_ntop.o \
 	noblock.o \
 	path.o \
diff --git a/src/port/erand48.c b/src/port/erand48.c
deleted file mode 100644
index a82ea94220..0000000000
--- a/src/port/erand48.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/port/random.c b/src/port/random.c
deleted file mode 100644
index 2dd59a0829..0000000000
--- a/src/port/random.c
+++ /dev/null
@@ -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();
-}
diff --git a/src/port/srandom.c b/src/port/srandom.c
deleted file mode 100644
index cf1007b2ef..0000000000
--- a/src/port/srandom.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/test/modules/test_bloomfilter/test_bloomfilter.c b/src/test/modules/test_bloomfilter/test_bloomfilter.c
index 96c5011428..69425bd6fc 100644
--- a/src/test/modules/test_bloomfilter/test_bloomfilter.c
+++ b/src/test/modules/test_bloomfilter/test_bloomfilter.c
@@ -13,6 +13,7 @@
 #include "postgres.h"
 
 #include "fmgr.h"
+#include "common/pg_prng.h"
 #include "lib/bloomfilter.h"
 #include "miscadmin.h"
 
@@ -85,7 +86,7 @@ create_and_test_bloom(int power, int64 nelements, int callerseed)
 	 * 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_i32(&pg_global_prng_state) % PG_INT32_MAX : callerseed;
 
 	/* Create Bloom filter, populate it, and report on false positive rate */
 	filter = bloom_create(nelements, bloom_work_mem, seed);
diff --git a/src/test/modules/test_integerset/test_integerset.c b/src/test/modules/test_integerset/test_integerset.c
index 21c6f49b37..3e8cb7a005 100644
--- a/src/test/modules/test_integerset/test_integerset.c
+++ b/src/test/modules/test_integerset/test_integerset.c
@@ -12,6 +12,7 @@
  */
 #include "postgres.h"
 
+#include "common/pg_prng.h"
 #include "fmgr.h"
 #include "lib/integerset.h"
 #include "miscadmin.h"
@@ -99,12 +100,16 @@ static void check_with_filler(IntegerSet *intset, uint64 x, uint64 value, uint64
 static void test_single_value_and_filler(uint64 value, uint64 filler_min, uint64 filler_max);
 static void test_huge_distances(void);
 
+static pg_prng_state	pr_state;
+
 /*
  * SQL-callable entry point to perform all tests.
  */
 Datum
 test_integerset(PG_FUNCTION_ARGS)
 {
+	pg_prng_strong_seed(&pr_state);
+
 	/* Tests for various corner cases */
 	test_empty();
 	test_huge_distances();
@@ -248,8 +253,7 @@ test_pattern(const test_spec *spec)
 		 * only a small part of the integer space is used.  We would very
 		 * rarely hit values that are actually in the set.
 		 */
-		x = (pg_lrand48() << 31) | pg_lrand48();
-		x = x % (last_int + 1000);
+		x = pg_prng_u64(&pr_state) % (last_int + 1000);
 
 		/* Do we expect this value to be present in the set? */
 		if (x >= last_int)
@@ -571,7 +575,7 @@ test_huge_distances(void)
 	 */
 	while (num_values < 1000)
 	{
-		val += pg_lrand48();
+		val += pg_prng_u32(&pr_state);
 		values[num_values++] = val;
 	}
 
diff --git a/src/test/modules/test_rbtree/test_rbtree.c b/src/test/modules/test_rbtree/test_rbtree.c
index 713ebd1b26..59a571185e 100644
--- a/src/test/modules/test_rbtree/test_rbtree.c
+++ b/src/test/modules/test_rbtree/test_rbtree.c
@@ -14,6 +14,7 @@
 #include "postgres.h"
 
 #include "fmgr.h"
+#include "common/pg_prng.h"
 #include "lib/rbtree.h"
 #include "utils/memutils.h"
 
@@ -108,7 +109,7 @@ GetPermutation(int size)
 	 */
 	for (i = 1; i < size; i++)
 	{
-		int			j = random() % (i + 1);
+		int			j = pg_prng_u32(&pg_global_prng_state) % (i + 1);
 
 		if (j < i)				/* avoid fetching undefined data if j=i */
 			permutation[i] = permutation[j];
@@ -320,7 +321,7 @@ testdelete(int size, int delsize)
 
 	for (i = 0; i < delsize; i++)
 	{
-		int			k = random() % size;
+		int			k = pg_prng_u32(&pg_global_prng_state) % size;
 
 		while (chosen[k])
 			k = (k + 1) % size;
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 4362bd44fd..aa048d949f 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -99,9 +99,9 @@ sub mkvcbuild
 	$solution = CreateSolution($vsVersion, $config);
 
 	our @pgportfiles = qw(
-	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c random.c
-	  srandom.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
+	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c
+	  getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
+	  snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
 	  dirent.c dlopen.c getopt.c getopt_long.c link.c
 	  pread.c preadv.c pwrite.c pwritev.c pg_bitutils.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
 	  f2s.c file_perm.c file_utils.c hashfn.c ip.c jsonapi.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
-	  saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c username.c
-	  wait_error.c wchar.c);
+	  pg_get_line.c pg_lzcompress.c pg_prng.c pgfnames.c psprintf.c relpath.c
+	  rmtree.c saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c
+	  username.c wait_error.c wchar.c);
 
 	if ($solution->{options}->{openssl})
 	{
diff --git a/src/tools/testint128.c b/src/tools/testint128.c
index 71c345969a..43d569aeb5 100644
--- a/src/tools/testint128.c
+++ b/src/tools/testint128.c
@@ -27,6 +27,7 @@
 #endif
 
 #include "common/int128.h"
+#include "common/pg_prng.h"
 
 /*
  * We assume the parts of this union are laid out compatibly.
@@ -61,27 +62,11 @@ my_int128_compare(int128 x, int128 y)
 	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.
  *
  * 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 fix the prng state (hmmm...).
  *
  * 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;
 
+	pg_prng_seed(&pg_global_prng_state, 0);
+
 	if (argc >= 2)
 		count = strtol(argv[1], NULL, 0);
 	else
@@ -97,9 +84,9 @@ main(int argc, char **argv)
 
 	while (count-- > 0)
 	{
-		int64		x = get_random_uint64();
-		int64		y = get_random_uint64();
-		int64		z = get_random_uint64();
+		int64		x = pg_prng_u64(&pg_global_prng_state);
+		int64		y = pg_prng_u64(&pg_global_prng_state);
+		int64		z = pg_prng_u64(&pg_global_prng_state);
 		test128		t1;
 		test128		t2;
 
#46Tom Lane
tgl@sss.pgh.pa.us
In reply to: Fabien COELHO (#45)
Re: rand48 replacement

Fabien COELHO <coelho@cri.ensmp.fr> writes:

Not enough. Here is a v14 which might improve things further more again.
Sorry for this noise due to blind windows tests.

Just FTR, I strongly object to your removal of process-startup srandom()
calls. Those are not only setting the seed for our own use, but also
ensuring that things like random() calls within PL functions or other
libraries aren't 100% predictable.

regards, tom lane

#47Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Tom Lane (#46)
1 attachment(s)
Re: rand48 replacement

Hello Tom,

Just FTR, I strongly object to your removal of process-startup srandom()
calls.

Ok. The point of the patch is to replace and unify the postgres underlying
PRNG, so there was some logic behind this removal.

Those are not only setting the seed for our own use, but also ensuring
that things like random() calls within PL functions or other libraries
aren't 100% predictable.

Sure, they shouldn't be predictable.

Attached v15 also does call srandom if it is there, and fixes yet another
remaining random call.

--
Fabien.

Attachments:

prng-15.patchtext/x-diff; name=prng-15.patchDownload
diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c
index d19f73127c..b250ae912b 100644
--- a/contrib/amcheck/verify_nbtree.c
+++ b/contrib/amcheck/verify_nbtree.c
@@ -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"
@@ -465,8 +466,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();
+		/* Random seed relies on backend prng initialization to avoid repetition */
+		seed = pg_prng_u64(&pg_global_prng_state);
 		/* Create Bloom filter to fingerprint index */
 		state->filter = bloom_create(total_elems, maintenance_work_mem, seed);
 		state->heaptuplespresent = 0;
diff --git a/contrib/auto_explain/auto_explain.c b/contrib/auto_explain/auto_explain.c
index e9092ba359..0c4279447c 100644
--- a/contrib/auto_explain/auto_explain.c
+++ b/contrib/auto_explain/auto_explain.c
@@ -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"
@@ -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_f64(&pg_global_prng_state) < auto_explain_sample_rate;
 		else
 			current_query_sampled = false;
 	}
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 2c2f149fb0..146b524076 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -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]);
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 76d4fea21c..c139382170 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -5157,7 +5157,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]);
 		}
diff --git a/contrib/tablefunc/tablefunc.c b/contrib/tablefunc/tablefunc.c
index 779bd4415e..fdd117f81e 100644
--- a/contrib/tablefunc/tablefunc.c
+++ b/contrib/tablefunc/tablefunc.c
@@ -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"
@@ -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_f64(&pg_global_prng_state);
+		u2 = pg_prng_f64(&pg_global_prng_state);
 
 		v1 = (2.0 * u1) - 1.0;
 		v2 = (2.0 * u2) - 1.0;
diff --git a/contrib/tsm_system_rows/tsm_system_rows.c b/contrib/tsm_system_rows/tsm_system_rows.c
index 4996612902..1a46d4b143 100644
--- a/contrib/tsm_system_rows/tsm_system_rows.c
+++ b/contrib/tsm_system_rows/tsm_system_rows.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/contrib/tsm_system_time/tsm_system_time.c b/contrib/tsm_system_time/tsm_system_time.c
index 788d8f9a68..36acc6c106 100644
--- a/contrib/tsm_system_time/tsm_system_time.c
+++ b/contrib/tsm_system_time/tsm_system_time.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index 03191e016c..3c2f24b653 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -16,6 +16,7 @@
 
 #include "access/gin_private.h"
 #include "access/relscan.h"
+#include "common/pg_prng.h"
 #include "miscadmin.h"
 #include "storage/predicate.h"
 #include "utils/datum.h"
@@ -787,7 +788,7 @@ entryLoadMoreItems(GinState *ginstate, GinScanEntry entry,
 	}
 }
 
-#define gin_rand() (((double) random()) / ((double) MAX_RANDOM_VALUE))
+#define gin_rand() pg_prng_f64(&pg_global_prng_state)
 #define dropItem(e) ( gin_rand() > ((double)GinFuzzySearchLimit)/((double)((e)->predictNumberResult)) )
 
 /*
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 43ba03b6eb..94dbabc198 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -19,6 +19,7 @@
 #include "access/htup_details.h"
 #include "access/reloptions.h"
 #include "catalog/pg_opclass.h"
+#include "common/pg_prng.h"
 #include "storage/indexfsm.h"
 #include "storage/lmgr.h"
 #include "utils/float.h"
@@ -507,7 +508,7 @@ gistchoose(Relation r, Page p, IndexTuple it,	/* it has compressed entry */
 			if (keep_current_best == -1)
 			{
 				/* we didn't make the random choice yet for this old best */
-				keep_current_best = (random() <= (MAX_RANDOM_VALUE / 2)) ? 1 : 0;
+				keep_current_best = pg_prng_bool(&pg_global_prng_state) ? 1 : 0;
 			}
 			if (keep_current_best == 0)
 			{
@@ -529,7 +530,7 @@ gistchoose(Relation r, Page p, IndexTuple it,	/* it has compressed entry */
 			if (keep_current_best == -1)
 			{
 				/* we didn't make the random choice yet for this old best */
-				keep_current_best = (random() <= (MAX_RANDOM_VALUE / 2)) ? 1 : 0;
+				keep_current_best = pg_prng_bool(&pg_global_prng_state) ? 1 : 0;
 			}
 			if (keep_current_best == 1)
 				break;
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index 7355e1dba1..37d8840ce0 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -19,6 +19,7 @@
 #include "access/nbtxlog.h"
 #include "access/transam.h"
 #include "access/xloginsert.h"
+#include "common/pg_prng.h"
 #include "lib/qunique.h"
 #include "miscadmin.h"
 #include "storage/lmgr.h"
@@ -968,7 +969,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_f64(&pg_global_prng_state) <= 0.01)
 				break;
 
 			_bt_stepright(rel, insertstate, stack);
diff --git a/src/backend/access/spgist/spgdoinsert.c b/src/backend/access/spgist/spgdoinsert.c
index 70557bcf3d..5389c93366 100644
--- a/src/backend/access/spgist/spgdoinsert.c
+++ b/src/backend/access/spgist/spgdoinsert.c
@@ -19,6 +19,7 @@
 #include "access/spgist_private.h"
 #include "access/spgxlog.h"
 #include "access/xloginsert.h"
+#include "common/pg_prng.h"
 #include "miscadmin.h"
 #include "storage/bufmgr.h"
 #include "utils/rel.h"
@@ -2210,7 +2211,7 @@ 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_u32(&pg_global_prng_state) % innerTuple->nNodes;
 			}
 
 			switch (out.resultType)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 6597ec45a9..d0557be7c9 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -37,6 +37,7 @@
 #include "commands/async.h"
 #include "commands/tablecmds.h"
 #include "commands/trigger.h"
+#include "common/pg_prng.h"
 #include "executor/spi.h"
 #include "libpq/be-fsstubs.h"
 #include "libpq/pqsignal.h"
@@ -1939,7 +1940,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_f64(&pg_global_prng_state) <= log_xact_sample_rate);
 
 	/*
 	 * initialize current transaction state fields
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 8bfb2ad958..fe623393d7 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -38,6 +38,7 @@
 #include "commands/progress.h"
 #include "commands/tablecmds.h"
 #include "commands/vacuum.h"
+#include "common/pg_prng.h"
 #include "executor/executor.h"
 #include "foreign/fdwapi.h"
 #include "miscadmin.h"
@@ -1173,7 +1174,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 	OldestXmin = GetOldestNonRemovableTransactionId(onerel);
 
 	/* Prepare for sampling block numbers */
-	randseed = random();
+	randseed = pg_prng_i32(&pg_global_prng_state);
 	nblocks = BlockSampler_Init(&bs, totalblocks, targrows, randseed);
 
 #ifdef USE_PREFETCH
@@ -1290,7 +1291,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 					 * Found a suitable tuple, so save it, replacing one old
 					 * tuple at random
 					 */
-					int			k = (int) (targrows * sampler_random_fract(rstate.randstate));
+					int			k = (int) (targrows * sampler_random_fract(&rstate.randstate));
 
 					Assert(k >= 0 && k < targrows);
 					heap_freetuple(rows[k]);
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 44232d50d0..09c34e96fa 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -17,6 +17,7 @@
 #include "access/relscan.h"
 #include "access/tableam.h"
 #include "access/tsmapi.h"
+#include "common/pg_prng.h"
 #include "executor/executor.h"
 #include "executor/nodeSamplescan.h"
 #include "miscadmin.h"
@@ -154,7 +155,7 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	 * do this just once, since the seed shouldn't change over rescans.
 	 */
 	if (tsc->repeatable == NULL)
-		scanstate->seed = random();
+		scanstate->seed = pg_prng_i32(&pg_global_prng_state);
 
 	/*
 	 * Finally, initialize the TABLESAMPLE method handler.
diff --git a/src/backend/optimizer/geqo/geqo_random.c b/src/backend/optimizer/geqo/geqo_random.c
index f21bc047e6..8b42a9ffba 100644
--- a/src/backend/optimizer/geqo/geqo_random.c
+++ b/src/backend/optimizer/geqo/geqo_random.c
@@ -21,14 +21,7 @@ geqo_set_seed(PlannerInfo *root, double seed)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	/*
-	 * XXX. This seeding algorithm could certainly be improved - but it is not
-	 * critical to do so.
-	 */
-	memset(private->random_state, 0, sizeof(private->random_state));
-	memcpy(private->random_state,
-		   &seed,
-		   Min(sizeof(private->random_state), sizeof(seed)));
+	pg_prng_fseed(&private->random_state, seed);
 }
 
 double
@@ -36,5 +29,5 @@ geqo_rand(PlannerInfo *root)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	return pg_erand48(private->random_state);
+	return pg_prng_f64(&private->random_state);
 }
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index e2a76ba055..09b4ec9b72 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -98,6 +98,7 @@
 #include "catalog/pg_control.h"
 #include "common/file_perm.h"
 #include "common/ip.h"
+#include "common/pg_prng.h"
 #include "common/string.h"
 #include "lib/ilist.h"
 #include "libpq/auth.h"
@@ -2675,19 +2676,22 @@ ClosePostmasterPorts(bool am_syslogger)
 void
 InitProcessGlobals(void)
 {
-	unsigned int rseed;
-
 	MyProcPid = getpid();
 	MyStartTimestamp = GetCurrentTimestamp();
 	MyStartTime = timestamptz_to_time_t(MyStartTimestamp);
 
 	/*
-	 * Set a different seed for random() in every process.  We want something
+	 * Set a different global seed in every process.  We want something
 	 * unpredictable, so if possible, use high-quality random bits for the
 	 * seed.  Otherwise, fall back to a seed based on timestamp and PID.
 	 */
-	if (!pg_strong_random(&rseed, sizeof(rseed)))
+	if (!pg_prng_strong_seed(&pg_global_prng_state))
 	{
+		uint64	rseed;
+
+		ereport(WARNING,
+				(errmsg_internal("pg_prng_strong_seed() failed, falling back to weaker seeding")));
+
 		/*
 		 * Since PIDs and timestamps tend to change more frequently in their
 		 * least significant bits, shift the timestamp left to allow a larger
@@ -2698,8 +2702,13 @@ InitProcessGlobals(void)
 		rseed = ((uint64) MyProcPid) ^
 			((uint64) MyStartTimestamp << 12) ^
 			((uint64) MyStartTimestamp >> 20);
+
+		pg_prng_seed(&pg_global_prng_state, rseed);
 	}
-	srandom(rseed);
+
+#ifdef HAVE_SRANDOM
+	srandom(pg_prng_i32(&pg_global_prng_state));
+#endif
 }
 
 
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index f9cda6906d..431931bcfe 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -92,6 +92,7 @@
 #include "catalog/pg_tablespace.h"
 #include "common/file_perm.h"
 #include "common/file_utils.h"
+#include "common/pg_prng.h"
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "port/pg_iovec.h"
@@ -2938,7 +2939,7 @@ SetTempTablespaces(Oid *tableSpaces, int numSpaces)
 	 * available tablespaces.
 	 */
 	if (numSpaces > 1)
-		nextTempTableSpace = random() % numSpaces;
+		nextTempTableSpace = pg_prng_u32(&pg_global_prng_state) % numSpaces;
 	else
 		nextTempTableSpace = 0;
 }
diff --git a/src/backend/storage/ipc/dsm.c b/src/backend/storage/ipc/dsm.c
index b461a5f7e9..e97844614e 100644
--- a/src/backend/storage/ipc/dsm.c
+++ b/src/backend/storage/ipc/dsm.c
@@ -33,6 +33,7 @@
 #endif
 #include <sys/stat.h>
 
+#include "common/pg_prng.h"
 #include "lib/ilist.h"
 #include "miscadmin.h"
 #include "port/pg_bitutils.h"
@@ -180,7 +181,7 @@ dsm_postmaster_startup(PGShmemHeader *shim)
 	{
 		Assert(dsm_control_address == NULL);
 		Assert(dsm_control_mapped_size == 0);
-		dsm_control_handle = random() << 1; /* Even numbers only */
+		dsm_control_handle = pg_prng_u32(&pg_global_prng_state) << 1; /* Even numbers only */
 		if (dsm_control_handle == DSM_HANDLE_INVALID)
 			continue;
 		if (dsm_impl_op(DSM_OP_CREATE, dsm_control_handle, segsize,
@@ -536,7 +537,7 @@ dsm_create(Size size, int flags)
 		for (;;)
 		{
 			Assert(seg->mapped_address == NULL && seg->mapped_size == 0);
-			seg->handle = random() << 1;	/* Even numbers only */
+			seg->handle = pg_prng_u32(&pg_global_prng_state) << 1;	/* Even numbers only */
 			if (seg->handle == DSM_HANDLE_INVALID)	/* Reserve sentinel */
 				continue;
 			if (dsm_impl_op(DSM_OP_CREATE, seg->handle, size, &seg->impl_private,
@@ -1237,7 +1238,7 @@ make_main_region_dsm_handle(int slot)
 	 */
 	handle = 1;
 	handle |= slot << 1;
-	handle |= random() << (pg_leftmost_one_pos32(dsm_control->maxitems) + 1);
+	handle |= pg_prng_i32(&pg_global_prng_state) << (pg_leftmost_one_pos32(dsm_control->maxitems) + 1);
 	return handle;
 }
 
diff --git a/src/backend/storage/lmgr/s_lock.c b/src/backend/storage/lmgr/s_lock.c
index 91322a40c1..74c6f251ce 100644
--- a/src/backend/storage/lmgr/s_lock.c
+++ b/src/backend/storage/lmgr/s_lock.c
@@ -50,6 +50,7 @@
 #include <time.h>
 #include <unistd.h>
 
+#include "common/pg_prng.h"
 #include "port/atomics.h"
 #include "storage/s_lock.h"
 
@@ -63,6 +64,7 @@
 slock_t		dummy_spinlock;
 
 static int	spins_per_delay = DEFAULT_SPINS_PER_DELAY;
+static pg_prng_state	prng_state;
 
 
 /*
@@ -143,8 +145,7 @@ perform_spin_delay(SpinDelayStatus *status)
 #endif
 
 		/* increase delay by a random fraction between 1X and 2X */
-		status->cur_delay += (int) (status->cur_delay *
-									((double) random() / (double) MAX_RANDOM_VALUE) + 0.5);
+		status->cur_delay += (int) (status->cur_delay * (pg_prng_f64(&prng_state) + 0.5));
 		/* wrap back to minimum delay when max is exceeded */
 		if (status->cur_delay > MAX_DELAY_USEC)
 			status->cur_delay = MIN_DELAY_USEC;
@@ -303,7 +304,7 @@ volatile struct test_lock_struct test_lock;
 int
 main()
 {
-	srandom((unsigned int) time(NULL));
+	pg_prng_seed(&prng_state, (unsigned int) time(NULL));
 
 	test_lock.pad1 = test_lock.pad2 = 0x44;
 
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 0775abe35d..ef6c27dade 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -42,6 +42,7 @@
 #include "catalog/pg_type.h"
 #include "commands/async.h"
 #include "commands/prepare.h"
+#include "common/pg_prng.h"
 #include "executor/spi.h"
 #include "jit/jit.h"
 #include "libpq/libpq.h"
@@ -2355,13 +2356,13 @@ check_log_duration(char *msec_str, bool was_logged)
 
 		/*
 		 * Do not log if log_statement_sample_rate = 0. Log a sample if
-		 * log_statement_sample_rate <= 1 and avoid unnecessary random() call
+		 * log_statement_sample_rate <= 1 and avoid unnecessary random call
 		 * if log_statement_sample_rate = 1.
 		 */
 		if (exceeded_sample_duration)
 			in_sample = log_statement_sample_rate != 0 &&
 				(log_statement_sample_rate == 1 ||
-				 random() <= log_statement_sample_rate * MAX_RANDOM_VALUE);
+				 pg_prng_f64(&pg_global_prng_state) <= log_statement_sample_rate);
 
 		if (exceeded_duration || in_sample || log_duration || xact_is_sampled)
 		{
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 098bbb372b..9fc0b108dd 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -21,6 +21,7 @@
 
 #include "catalog/pg_type.h"
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/shortest_dec.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
@@ -65,7 +66,7 @@ float8		degree_c_one = 1.0;
 
 /* State for drandom() and setseed() */
 static bool drandom_seed_set = false;
-static unsigned short drandom_seed[3] = {0, 0, 0};
+static pg_prng_state drandom_seed = PG_PRNG_DEFAULT_STATE;
 
 /* Local function prototypes */
 static double sind_q1(double x);
@@ -2762,22 +2763,20 @@ drandom(PG_FUNCTION_ARGS)
 		 * Should that fail for some reason, we fall back on a lower-quality
 		 * seed based on current time and PID.
 		 */
-		if (!pg_strong_random(drandom_seed, sizeof(drandom_seed)))
+		if (!pg_strong_random(&drandom_seed, sizeof(drandom_seed)))
 		{
 			TimestampTz now = GetCurrentTimestamp();
 			uint64		iseed;
 
 			/* Mix the PID with the most predictable bits of the timestamp */
 			iseed = (uint64) now ^ ((uint64) MyProcPid << 32);
-			drandom_seed[0] = (unsigned short) iseed;
-			drandom_seed[1] = (unsigned short) (iseed >> 16);
-			drandom_seed[2] = (unsigned short) (iseed >> 32);
+			pg_prng_seed(&drandom_seed, iseed);
 		}
 		drandom_seed_set = true;
 	}
 
-	/* pg_erand48 produces desired result range [0.0 - 1.0) */
-	result = pg_erand48(drandom_seed);
+	/* produces desired result range [0.0 - 1.0) */
+	result = pg_prng_f64(&drandom_seed);
 
 	PG_RETURN_FLOAT8(result);
 }
@@ -2790,7 +2789,6 @@ Datum
 setseed(PG_FUNCTION_ARGS)
 {
 	float8		seed = PG_GETARG_FLOAT8(0);
-	uint64		iseed;
 
 	if (seed < -1 || seed > 1 || isnan(seed))
 		ereport(ERROR,
@@ -2798,11 +2796,7 @@ setseed(PG_FUNCTION_ARGS)
 				 errmsg("setseed parameter %g is out of allowed range [-1,1]",
 						seed)));
 
-	/* Use sign bit + 47 fractional bits to fill drandom_seed[] */
-	iseed = (int64) (seed * (float8) UINT64CONST(0x7FFFFFFFFFFF));
-	drandom_seed[0] = (unsigned short) iseed;
-	drandom_seed[1] = (unsigned short) (iseed >> 16);
-	drandom_seed[2] = (unsigned short) (iseed >> 32);
+	pg_prng_fseed(&drandom_seed, seed);
 	drandom_seed_set = true;
 
 	PG_RETURN_VOID();
diff --git a/src/backend/utils/misc/sampling.c b/src/backend/utils/misc/sampling.c
index 0c327e823f..f1c86150d8 100644
--- a/src/backend/utils/misc/sampling.c
+++ b/src/backend/utils/misc/sampling.c
@@ -49,7 +49,7 @@ BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize,
 	bs->t = 0;					/* blocks scanned so far */
 	bs->m = 0;					/* blocks selected so far */
 
-	sampler_random_init_state(randseed, bs->randstate);
+	sampler_random_init_state(randseed, &bs->randstate);
 
 	return Min(bs->n, bs->N);
 }
@@ -98,7 +98,7 @@ BlockSampler_Next(BlockSampler bs)
 	 * less than k, which means that we cannot fail to select enough blocks.
 	 *----------
 	 */
-	V = sampler_random_fract(bs->randstate);
+	V = sampler_random_fract(&bs->randstate);
 	p = 1.0 - (double) k / (double) K;
 	while (V < p)
 	{
@@ -136,10 +136,10 @@ reservoir_init_selection_state(ReservoirState rs, int n)
 	 * Reservoir sampling is not used anywhere where it would need to return
 	 * repeatable results so we can initialize it randomly.
 	 */
-	sampler_random_init_state(random(), rs->randstate);
+	sampler_random_init_state(pg_prng_u32(&pg_global_prng_state), &rs->randstate);
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	rs->W = exp(-log(sampler_random_fract(rs->randstate)) / n);
+	rs->W = exp(-log(sampler_random_fract(&rs->randstate)) / n);
 }
 
 double
@@ -154,7 +154,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 		double		V,
 					quot;
 
-		V = sampler_random_fract(rs->randstate);	/* Generate V */
+		V = sampler_random_fract(&rs->randstate);	/* Generate V */
 		S = 0;
 		t += 1;
 		/* Note: "num" in Vitter's code is always equal to t - n */
@@ -186,7 +186,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 						tmp;
 
 			/* Generate U and X */
-			U = sampler_random_fract(rs->randstate);
+			U = sampler_random_fract(&rs->randstate);
 			X = t * (W - 1.0);
 			S = floor(X);		/* S is tentatively set to floor(X) */
 			/* Test if U <= h(S)/cg(X) in the manner of (6.3) */
@@ -215,7 +215,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 				y *= numer / denom;
 				denom -= 1;
 			}
-			W = exp(-log(sampler_random_fract(rs->randstate)) / n); /* Generate W in advance */
+			W = exp(-log(sampler_random_fract(&rs->randstate)) / n); /* Generate W in advance */
 			if (exp(log(y) / n) <= (t + X) / t)
 				break;
 		}
@@ -230,24 +230,22 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
  *----------
  */
 void
-sampler_random_init_state(long seed, SamplerRandomState randstate)
+sampler_random_init_state(long seed, pg_prng_state *randstate)
 {
-	randstate[0] = 0x330e;		/* same as pg_erand48, but could be anything */
-	randstate[1] = (unsigned short) seed;
-	randstate[2] = (unsigned short) (seed >> 16);
+	pg_prng_seed(randstate, (uint64_t) seed);
 }
 
 /* Select a random value R uniformly distributed in (0 - 1) */
 double
-sampler_random_fract(SamplerRandomState randstate)
+sampler_random_fract(pg_prng_state *randstate)
 {
 	double		res;
 
-	/* pg_erand48 returns a value in [0.0 - 1.0), so we must reject 0 */
+	/* pg_prng_f64 returned value in [0.0 - 1.0), so we must reject 0.0 */
 	do
 	{
-		res = pg_erand48(randstate);
-	} while (res == 0.0);
+		res = pg_prng_f64(randstate);
+	} while (unlikely(res == 0.0));
 	return res;
 }
 
@@ -266,22 +264,28 @@ double
 anl_random_fract(void)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(pg_prng_u32(&pg_global_prng_state), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* and compute a random fraction */
-	return sampler_random_fract(oldrs.randstate);
+	return sampler_random_fract(&oldrs.randstate);
 }
 
 double
 anl_init_selection_state(int n)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(pg_prng_u32(&pg_global_prng_state), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	return exp(-log(sampler_random_fract(oldrs.randstate)) / n);
+	return exp(-log(sampler_random_fract(&oldrs.randstate)) / n);
 }
 
 double
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 1ed4808d53..af9f5169e9 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -66,6 +66,7 @@
 #include "common/file_perm.h"
 #include "common/file_utils.h"
 #include "common/logging.h"
+#include "common/pg_prng.h"
 #include "common/restricted_token.h"
 #include "common/string.h"
 #include "common/username.h"
@@ -880,9 +881,10 @@ choose_dsm_implementation(void)
 {
 #ifdef HAVE_SHM_OPEN
 	int			ntries = 10;
+	pg_prng_state	prng_state;
 
-	/* Initialize random(); this function is its only user in this program. */
-	srandom((unsigned int) (getpid() ^ time(NULL)));
+	/* Initialize pgng state this function is its only user in this program. */
+	pg_prng_seed(&prng_state, ((unsigned int) (getpid() ^ time(NULL))));
 
 	while (ntries > 0)
 	{
@@ -890,7 +892,7 @@ choose_dsm_implementation(void)
 		char		name[64];
 		int			fd;
 
-		handle = random();
+		handle = pg_prng_u32(&prng_state);
 		snprintf(name, 64, "/PostgreSQL.%u", handle);
 		if ((fd = shm_open(name, O_CREAT | O_RDWR | O_EXCL, 0600)) != -1)
 		{
diff --git a/src/bin/pg_test_fsync/pg_test_fsync.c b/src/bin/pg_test_fsync/pg_test_fsync.c
index fef31844fa..5b3c197aa6 100644
--- a/src/bin/pg_test_fsync/pg_test_fsync.c
+++ b/src/bin/pg_test_fsync/pg_test_fsync.c
@@ -15,6 +15,7 @@
 
 #include "access/xlogdefs.h"
 #include "common/logging.h"
+#include "common/pg_prng.h"
 #include "getopt_long.h"
 
 /*
@@ -71,6 +72,7 @@ static char full_buf[DEFAULT_XLOG_SEG_SIZE],
 static struct timeval start_t,
 			stop_t;
 static bool alarm_triggered = false;
+static pg_prng_state	prng_state;
 
 
 static void handle_args(int argc, char *argv[]);
@@ -117,6 +119,7 @@ main(int argc, char *argv[])
 	pqsignal(SIGHUP, signal_cleanup);
 #endif
 
+	pg_prng_strong_seed(&prng_state);
 	prepare_buf();
 
 	test_open();
@@ -233,7 +236,7 @@ prepare_buf(void)
 
 	/* write random data into buffer */
 	for (ops = 0; ops < DEFAULT_XLOG_SEG_SIZE; ops++)
-		full_buf[ops] = random();
+		full_buf[ops] = pg_prng_u32(&prng_state);
 
 	buf = (char *) TYPEALIGN(XLOG_BLCKSZ, full_buf);
 }
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 433abd954b..64c7370ff5 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -58,6 +58,7 @@
 #endif
 
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/logging.h"
 #include "common/string.h"
 #include "common/username.h"
@@ -350,16 +351,8 @@ typedef struct StatsData
  */
 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. */
-static RandomState base_random_sequence;
+static pg_prng_state	base_random_sequence;
 
 /* Synchronization barrier for start and connection */
 static THREAD_BARRIER_T barrier;
@@ -461,7 +454,7 @@ typedef struct
 	 * Separate randomness for each client. This is used for random functions
 	 * 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			command;		/* command number in script */
@@ -498,9 +491,9 @@ typedef struct
 	 * random state to make all of them independent of each other and
 	 * therefore deterministic at the thread level.
 	 */
-	RandomState ts_choose_rs;	/* random state for selecting a script */
-	RandomState ts_throttle_rs; /* random state for transaction throttling */
-	RandomState ts_sample_rs;	/* random state for log sampling */
+	pg_prng_state	ts_choose_rs;	/* random state for selecting a script */
+	pg_prng_state	ts_throttle_rs; /* random state for transaction throttling */
+	pg_prng_state	ts_sample_rs;	/* random state for log sampling */
 
 	int64		throttle_trigger;	/* previous/next throttling (us) */
 	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.
  */
 static void
-initRandomState(RandomState *random_state)
+initRandomState(pg_prng_state *state)
 {
-	random_state->xseed[0] = (unsigned short)
-		(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);
+	pg_prng_seed(state, pg_prng_u64(&base_random_sequence));
 }
 
+
 /*
- * Random number generator: uniform distribution from min to max inclusive.
+ * PRNG: uniform distribution from min to max inclusive.
  *
  * Although the limits are expressed as int64, you can't generate the full
  * int64 range in one call, because the difference of the limits mustn't
- * overflow int64.  In practice it's unwise to ask for more than an int32
- * range, because of the limited precision of pg_erand48().
+ * overflow int64. This is not checked.
  */
 static int64
-getrand(RandomState *random_state, int64 min, int64 max)
+getrand(pg_prng_state *state, int64 min, int64 max)
 {
-	/*
-	 * 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));
+	return min + (int64) pg_prng_u64_range(state, 0, max - min);
 }
 
 /*
@@ -942,8 +921,7 @@ getrand(RandomState *random_state, int64 min, int64 max)
  * value is exp(-parameter).
  */
 static int64
-getExponentialRand(RandomState *random_state, int64 min, int64 max,
-				   double parameter)
+getExponentialRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		cut,
 				uniform,
@@ -953,7 +931,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 	Assert(parameter > 0.0);
 	cut = exp(-parameter);
 	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	uniform = 1.0 - pg_prng_f64(state);
 
 	/*
 	 * inner expression in (cut, 1] (if parameter > 0), rand in [0, 1)
@@ -966,8 +944,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 
 /* random number generator: gaussian distribution from min to max inclusive */
 static int64
-getGaussianRand(RandomState *random_state, int64 min, int64 max,
-				double parameter)
+getGaussianRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		stdev;
 	double		rand;
@@ -990,13 +967,13 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
 	do
 	{
 		/*
-		 * pg_erand48 generates [0,1), but for the basic version of the
+		 * pg_prng_f64 generates [0,1), but for the basic version of the
 		 * Box-Muller transform the two uniformly distributed random numbers
 		 * are expected in (0, 1] (see
 		 * https://en.wikipedia.org/wiki/Box-Muller_transform)
 		 */
-		double		rand1 = 1.0 - pg_erand48(random_state->xseed);
-		double		rand2 = 1.0 - pg_erand48(random_state->xseed);
+		double		rand1 = 1.0 - pg_prng_f64(state);
+		double		rand2 = 1.0 - pg_prng_f64(state);
 
 		/* Box-Muller basic form transform */
 		double		var_sqrt = sqrt(-2.0 * log(rand1));
@@ -1026,7 +1003,7 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
  * not be one.
  */
 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
@@ -1034,8 +1011,8 @@ getPoissonRand(RandomState *random_state, double center)
 	 */
 	double		uniform;
 
-	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	/* pseudo-random value in [0, 1), uniform in (0, 1] */
+	uniform = 1.0 - pg_prng_f64(state);
 
 	return (int64) (-log(uniform) * center + 0.5);
 }
@@ -1048,7 +1025,7 @@ getPoissonRand(RandomState *random_state, double center)
  * This works for s > 1.0, but may perform badly for s very close to 1.0.
  */
 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		x,
@@ -1063,8 +1040,8 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 	while (true)
 	{
 		/* random variates */
-		u = pg_erand48(random_state->xseed);
-		v = pg_erand48(random_state->xseed);
+		u = pg_prng_f64(state);
+		v = pg_prng_f64(state);
 
 		x = floor(pow(u, -1.0 / (s - 1.0)));
 
@@ -1078,14 +1055,14 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 
 /* random number generator: zipfian distribution from min to max inclusive */
 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;
 
 	/* abort if parameter is invalid */
 	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 +1119,7 @@ getHashMurmur2(int64 val, uint64 seed)
  * For small sizes, this generates each of the (size!) possible permutations
  * of integers in the range [0, size) with roughly equal probability.  Once
  * the size is larger than 20, the number of possible permutations exceeds the
- * number of distinct states of the internal pseudorandom number generators,
+ * number of distinct states of the internal pseudorandom number generator,
  * and so not all possible permutations can be generated, but the permutations
  * chosen should continue to give the appearance of being random.
  *
@@ -1152,25 +1129,19 @@ getHashMurmur2(int64 val, uint64 seed)
 static int64
 permute(const int64 val, const int64 isize, const int64 seed)
 {
-	RandomState random_state1;
-	RandomState random_state2;
-	uint64		size;
-	uint64		v;
-	int			masklen;
-	uint64		mask;
-	int			i;
+	/* using a high-end PRNG is probably overkill */
+	pg_prng_state	state;
+	uint64			size;
+	uint64			v;
+	int				masklen;
+	uint64			mask;
+	int				i;
 
 	if (isize < 2)
 		return 0;				/* nothing to permute */
 
-	/* Initialize a pair of random states using the seed */
-	random_state1.xseed[0] = seed & 0xFFFF;
-	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;
+	/* Initialize prng state using the seed */
+	pg_prng_seed(&state, (uint64) seed);
 
 	/* Computations are performed on unsigned values */
 	size = (uint64) isize;
@@ -1216,8 +1187,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 					t;
 
 		/* Random multiply (by an odd number), XOR and rotate of lower half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		if (v <= mask)
 		{
 			v = ((v * m) ^ r) & mask;
@@ -1225,8 +1196,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random multiply (by an odd number), XOR and rotate of upper half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		t = size - 1 - v;
 		if (t <= mask)
 		{
@@ -1236,7 +1207,7 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random offset */
-		r = (uint64) getrand(&random_state2, 0, size - 1);
+		r = pg_prng_u64(&state) % (size - 1);
 		v = (v + r) % size;
 	}
 
@@ -3827,7 +3798,7 @@ doLog(TState *thread, CState *st,
 	 * to the random sample.
 	 */
 	if (sample_rate != 0.0 &&
-		pg_erand48(thread->ts_sample_rs.xseed) > sample_rate)
+		pg_prng_f64(&thread->ts_sample_rs) > sample_rate)
 		return;
 
 	/* should we aggregate the results or not? */
@@ -5763,12 +5734,11 @@ set_random_seed(const char *seed)
 
 	if (seed != NULL)
 		pg_log_info("setting random seed to %llu", (unsigned long long) iseed);
+
 	random_seed = iseed;
 
 	/* Fill base_random_sequence with low-order bits of seed */
-	base_random_sequence.xseed[0] = iseed & 0xFFFF;
-	base_random_sequence.xseed[1] = (iseed >> 16) & 0xFFFF;
-	base_random_sequence.xseed[2] = (iseed >> 32) & 0xFFFF;
+	pg_prng_seed(&base_random_sequence, (uint64_t) iseed);
 
 	return true;
 }
@@ -6439,9 +6409,7 @@ main(int argc, char **argv)
 	/* set default seed for hash functions */
 	if (lookupVariable(&state[0], "default_seed") == NULL)
 	{
-		uint64		seed =
-		((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) |
-		(((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) << 32);
+		uint64		seed = pg_prng_u64(&base_random_sequence);
 
 		for (i = 0; i < nclients; i++)
 			if (!putVariableInt(&state[i], "startup", "default_seed", (int64) seed))
diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl
index ef53f6b2d9..13c9204a3d 100644
--- a/src/bin/pgbench/t/001_pgbench_with_server.pl
+++ b/src/bin/pgbench/t/001_pgbench_with_server.pl
@@ -427,9 +427,9 @@ pgbench(
 
 		# After explicit seeding, the four random checks (1-3,20) are
 		# deterministic
-		qr{command=1.: int 13\b},      # uniform random
-		qr{command=2.: int 116\b},     # exponential random
-		qr{command=3.: int 1498\b},    # gaussian random
+		qr{command=1.: int 17\b},      # uniform random
+		qr{command=2.: int 108\b},     # exponential random
+		qr{command=3.: int 1452\b},    # gaussian random
 		qr{command=4.: int 4\b},
 		qr{command=5.: int 5\b},
 		qr{command=6.: int 6\b},
@@ -496,6 +496,7 @@ pgbench(
 		qr{command=109.: boolean true\b},
 		qr{command=110.: boolean true\b},
 		qr{command=111.: boolean true\b},
+		qr{command=113.: boolean true\b},
 	],
 	'pgbench expressions',
 	{
@@ -639,8 +640,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))
 -- actual values
 \set t debug(permute(:v, 1) = 0)
-\set t debug(permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1 and \
-             permute(0, 2, 5435) = 1 and permute(1, 2, 5435) = 0)
+\set t debug(permute(0, 2, 5431) = 1 and permute(1, 2, 5431) = 0 and \
+             permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1)
+-- 63 bits tests for checking permute portability across architectures
+\set size debug(:max - 10)
+\set t debug(permute(:size-1, :size, 5432) = 4352241160009873020 and \
+             permute(:size-2, :size, 5432) = 4990004119691638710 and \
+             permute(:size-3, :size, 5432) = 5868029867256127549 and \
+             permute(:size-4, :size, 5432) = 1238830324886341378 and \
+             permute(:size-5, :size, 5432) = 7415914367102906897 and \
+             permute(:size-6, :size, 5432) = 3214501037984818995 and \
+             permute(:size-7, :size, 5432) =  600660351261124695)
 }
 	});
 
diff --git a/src/common/Makefile b/src/common/Makefile
index 880722fcf5..31c0dd366d 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -66,6 +66,7 @@ OBJS_COMMON = \
 	md5_common.o \
 	pg_get_line.o \
 	pg_lzcompress.o \
+	pg_prng.o \
 	pgfnames.o \
 	psprintf.o \
 	relpath.o \
diff --git a/src/common/pg_prng.c b/src/common/pg_prng.c
new file mode 100644
index 0000000000..df2054af79
--- /dev/null
+++ b/src/common/pg_prng.c
@@ -0,0 +1,244 @@
+/*
+ * Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * Previous version was the 1988 (?) rand48 standard with 48 bits state LCG,
+ * designed for running on 32 bits or even 16 bits architectures, and to produce
+ * 32 bit ints and floats (i.e. *not* doubles).
+ *
+ * The implied 16-bits unpacking/packing may have a detrimental performance impact
+ * on modern 64 bits architectures.
+ *
+ * We (probably) want:
+ * - one reasonable default PRNG for all pg internal uses.
+ * - NOT to invent a new design!
+ * - something fast, close to rand48 (which basically does 2 arithmetic ops)
+ *   no need for something cryptographic though, which would imply slow
+ * - to produce 64 bits integers & doubles with a 52 bits mantissa
+ *   so state size > 64 bits.
+ * - a small state though, because we might generate quite a few of them
+ *   for different purposes so state size <= 256 or even <= 128 bits
+ * - the state to be aligned to whatever, so state size = 128 bits
+ * - 64 bits operations for efficiency on modern architectures,
+ *   but NOT 128 bits operations.
+ * - not to depend on special hardware for speed (eg MMX/SSE/AES).
+ * - not something with obvious known and relevant defects.
+ * - not something with "rights" attached.
+ *
+ * These constraints reduce a lot the available options from
+ * https://en.wikipedia.org/wiki/List_of_random_number_generators
+ *
+ * LCG
+ * - POSIX rand48: m = 2^48, a = 25214903917, c = 11, extract 47...16
+ * - MMIX by Donald Knuth: m = 2^64, a = 6364136223846793005, c = 1442695040888963407
+ * - very bad on low bits, which MUST NOT BE USED
+ *   a 128 bits would do, but no clear standards, and it would imply 128-bit ops,
+ *   see PCG below.
+ *
+ * PCG Family (https://www.pcg-random.org/)
+ * - this is basically an LCG with an improved outpout function
+ *   so as to avoid typical LCG issues and pass stats tests.
+ * - recommands state size to be twice the output size,
+ *   which seems a reasonable requirement.
+ * - this implies 128 bit ops for our target parameters,
+ *   otherwise it is a fairly good candidate for simplicity,
+ *   efficiency and quality.
+ *
+ * Xor-Shift-Rotate PRNGs:
+ * - a generic annoying feature of this generator class is that there is one
+ *   special all-zero state which must not be used, inducing a constraint on
+ *   initialization, and when the generator get close to it (i.e. many zeros
+ *   in the state) it takes some iterations to recover, with small lumps
+ *   of close/related/bad numbers generated in the process.
+ * Xoroshiro128+
+ * - fails statistical linearity tests in the low bits.
+ * Xoroshiro128**
+ *   https://prng.di.unimi.it/xoroshiro128starstar.c
+ * - mild and irrelevant issues: close to zero state, stats issues after some transforms
+ * Xoshiro256**
+ * - it looks good, but some issues (no big deal for pg usage IMO)
+ *   https://www.pcg-random.org/posts/a-quick-look-at-xoshiro256.html
+ *   mild problems getting out of closes-to-zero state
+ *
+ * MT19937-64
+ * + default in many places, standard
+ * - huge 2.5 KiB state (but TinyMT, 127 bits state space)
+ * - slow (but SFMT, twice as fast)
+ * - vulnerabilities on tinyMT?
+ *
+ * WELL
+ * - large state, 32-bit oriented
+ *
+ * ACORN Family (https://en.wikipedia.org/wiki/ACORN_(PRNG))
+ * - Fortran / double oriented, no C implementation found?!
+ * - medium large state "In practice we recommend using k > 10, M = 2^60 (for general application)
+ *   or M = 2^120 (for demanding applications requiring high-quality pseudo-random numbers
+ *   that will consistently pass all the tests in standard test packages such as TestU01)
+ *   and choose any odd value less than M for the seed."
+ *   k = 11 => 88 bytes/704-bits state, too large:-(
+ *
+ * Splitmix?
+ * Others?
+ *
+ * The conclusion is that we use xor-shift-rotate generator below.
+ */
+
+#include "c.h"
+#include "common/pg_prng.h"
+#include "port/pg_bitutils.h"
+#include <math.h>
+
+/* global state */
+pg_prng_state	pg_global_prng_state;
+
+#define FIRST_BIT_MASK UINT64CONST(0x8000000000000000)
+#define RIGHT_HALF_MASK UINT64CONST(0x00000000FFFFFFFF)
+#define DMANTISSA_MASK UINT64CONST(0x000FFFFFFFFFFFFF)
+
+/* 64-bits rotate left */
+static inline uint64
+rotl(const uint64 x, const int bits)
+{
+	return (x << bits) | (x >> (64 - bits));
+}
+
+/* another 64-bits generator is used for state initialization or iterations */
+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);
+}
+
+/* seed prng state from a 64 bits integer, ensuring non zero */
+void
+pg_prng_seed(pg_prng_state *state, uint64 seed)
+{
+	state->s0 = splitmix64(&seed);
+	state->s1 = splitmix64(&seed);
+}
+
+/* seed with 53 bits (mantissa & sign) from a float */
+void
+pg_prng_fseed(pg_prng_state *state, double fseed)
+{
+	uint64 seed = (int64) (((double) DMANTISSA_MASK) * fseed);
+	pg_prng_seed(state, seed);
+}
+
+/* strong random seeding */
+bool
+pg_prng_strong_seed(pg_prng_state *state)
+{
+	bool ok = pg_strong_random((void *) state, sizeof(pg_prng_state));
+
+	/* avoid zero with Donald Knuth's LCG parameters */
+	if (unlikely(state->s0 == 0 && state->s1 == 0))
+	{
+		/* should it warn that something is amiss if we get there? */
+		pg_prng_state def = PG_PRNG_DEFAULT_STATE;
+		*state = def;
+	}
+
+	return ok;
+}
+
+/* generator & state update */
+static uint64
+xoroshiro128ss(pg_prng_state *state)
+{
+	const 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;
+}
+
+/* u64 generator */
+uint64
+pg_prng_u64(pg_prng_state *state)
+{
+	return xoroshiro128ss(state);
+}
+
+/*
+ * select in a range with bitmask rejection.
+ *
+ * if range is empty, rmin is returned.
+ *
+ * the prng may advance by several states or none,
+ * depending on the range value.
+ */
+uint64
+pg_prng_u64_range(pg_prng_state *state, uint64 rmin, uint64 rmax)
+{
+	uint64 val;
+
+	if (likely(rmax > rmin))
+	{
+		uint64 range = rmax - rmin;
+
+		/* bit position is between 0 and 63, so rshift >= 0 */
+		uint32 rshift = 63 - pg_leftmost_one_pos64(range);
+
+		do
+		{
+			val = xoroshiro128ss(state) >> rshift;
+		}
+		while (unlikely(val > range));
+	}
+	else
+		val = 0;
+
+	return rmin + val;
+}
+
+/* i64 generator */
+int64
+pg_prng_i64(pg_prng_state *state)
+{
+	return (int64) xoroshiro128ss(state);
+}
+
+/* u32 generator */
+uint32
+pg_prng_u32(pg_prng_state *state)
+{
+	const uint64 v = xoroshiro128ss(state);
+	return (uint32) (((v >> 32) ^ v) & RIGHT_HALF_MASK);
+}
+
+/* i32 generator */
+int32
+pg_prng_i32(pg_prng_state *state)
+{
+	const uint64 v = xoroshiro128ss(state);
+	return (int32) (((v >> 32) ^ v) & RIGHT_HALF_MASK);
+}
+
+/* double generator */
+double
+pg_prng_f64(pg_prng_state *state)
+{
+	uint64 v = xoroshiro128ss(state);
+	return ldexp((double) (v & DMANTISSA_MASK), -52);
+}
+
+/* bool generator */
+bool
+pg_prng_bool(pg_prng_state *state)
+{
+	uint64 v = xoroshiro128ss(state);
+	return (v & FIRST_BIT_MASK) == FIRST_BIT_MASK;
+}
diff --git a/src/include/common/pg_prng.h b/src/include/common/pg_prng.h
new file mode 100644
index 0000000000..55f0e3d6cb
--- /dev/null
+++ b/src/include/common/pg_prng.h
@@ -0,0 +1,37 @@
+/*-------------------------------------------------------------------------
+ *
+ * PRNG: internal Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * src/include/common/pg_prng.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_PRNG_H
+#define PG_PRNG_H
+
+/* 128 bits state */
+typedef struct pg_prng_state {
+	uint64	s0, s1;
+} pg_prng_state;
+
+/* global pseudo-random state */
+extern pg_prng_state	pg_global_prng_state;
+
+/* use Donald Knuth's LCG constants for default state */
+#define PG_PRNG_DEFAULT_STATE \
+	{ UINT64CONST(0x5851F42D4C957F2D), UINT64CONST(0x14057B7EF767814F) }
+
+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_strong_seed(pg_prng_state *state);
+extern uint64 pg_prng_u64(pg_prng_state *state);
+extern uint64 pg_prng_u64_range(pg_prng_state *state, uint64 rmin, uint64 rmax);
+extern int64 pg_prng_i64(pg_prng_state *state);
+extern uint32 pg_prng_u32(pg_prng_state *state);
+extern int32 pg_prng_i32(pg_prng_state *state);
+extern double pg_prng_f64(pg_prng_state *state);
+extern bool pg_prng_bool(pg_prng_state *state);
+
+#endif							/* PG_PRNG_H */
diff --git a/src/include/optimizer/geqo.h b/src/include/optimizer/geqo.h
index 24dcdfb6cc..b28e4f084a 100644
--- a/src/include/optimizer/geqo.h
+++ b/src/include/optimizer/geqo.h
@@ -22,6 +22,7 @@
 #ifndef GEQO_H
 #define GEQO_H
 
+#include "common/pg_prng.h"
 #include "nodes/pathnodes.h"
 #include "optimizer/geqo_gene.h"
 
@@ -72,8 +73,8 @@ extern double Geqo_seed;		/* 0 .. 1 */
  */
 typedef struct
 {
-	List	   *initial_rels;	/* the base relations we are joining */
-	unsigned short random_state[3]; /* state for pg_erand48() */
+	List		   *initial_rels;	/* the base relations we are joining */
+	pg_prng_state	random_state;   /* PRNG state */
 } GeqoPrivateData;
 
 
diff --git a/src/include/port.h b/src/include/port.h
index 82f63de325..ef84d00ea0 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -356,11 +356,6 @@ extern int	gettimeofday(struct timeval *tp, struct timezone *tzp);
 #define pgoff_t off_t
 #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
 extern int	fls(int mask);
 #endif
@@ -446,10 +441,6 @@ extern size_t strlcpy(char *dst, const char *src, size_t siz);
 extern size_t strnlen(const char *str, size_t maxlen);
 #endif
 
-#if !defined(HAVE_RANDOM)
-extern long random(void);
-#endif
-
 #ifndef HAVE_SETENV
 extern int	setenv(const char *name, const char *value, int overwrite);
 #endif
@@ -458,10 +449,6 @@ extern int	setenv(const char *name, const char *value, int overwrite);
 extern int	unsetenv(const char *name);
 #endif
 
-#ifndef HAVE_SRANDOM
-extern void srandom(unsigned int seed);
-#endif
-
 #ifndef HAVE_DLOPEN
 extern void *dlopen(const char *file, int mode);
 extern void *dlsym(void *handle, const char *symbol);
diff --git a/src/include/utils/sampling.h b/src/include/utils/sampling.h
index a58d14281b..c0b1c6a645 100644
--- a/src/include/utils/sampling.h
+++ b/src/include/utils/sampling.h
@@ -13,15 +13,14 @@
 #ifndef SAMPLING_H
 #define SAMPLING_H
 
+#include "common/pg_prng.h"
 #include "storage/block.h"		/* for typedef BlockNumber */
 
 
 /* Random generator for sampling code */
-typedef unsigned short SamplerRandomState[3];
-
 extern void sampler_random_init_state(long seed,
-									  SamplerRandomState randstate);
-extern double sampler_random_fract(SamplerRandomState randstate);
+									  pg_prng_state *randstate);
+extern double sampler_random_fract(pg_prng_state *randstate);
 
 /* Block sampling methods */
 
@@ -32,7 +31,7 @@ typedef struct
 	int			n;				/* desired sample size */
 	BlockNumber t;				/* current block number */
 	int			m;				/* blocks selected so far */
-	SamplerRandomState randstate;	/* random generator state */
+	pg_prng_state randstate;	/* random generator state */
 } BlockSamplerData;
 
 typedef BlockSamplerData *BlockSampler;
@@ -46,8 +45,9 @@ extern BlockNumber BlockSampler_Next(BlockSampler bs);
 
 typedef struct
 {
-	double		W;
-	SamplerRandomState randstate;	/* random generator state */
+	double			W;
+	pg_prng_state	randstate;	/* random generator state */
+	bool			randstate_initialized;
 } ReservoirStateData;
 
 typedef ReservoirStateData *ReservoirState;
diff --git a/src/port/Makefile b/src/port/Makefile
index 52dbf5783f..b3754d8940 100644
--- a/src/port/Makefile
+++ b/src/port/Makefile
@@ -42,7 +42,6 @@ OBJS = \
 	$(PG_CRC32C_OBJS) \
 	bsearch_arg.o \
 	chklocale.o \
-	erand48.o \
 	inet_net_ntop.o \
 	noblock.o \
 	path.o \
diff --git a/src/port/erand48.c b/src/port/erand48.c
deleted file mode 100644
index a82ea94220..0000000000
--- a/src/port/erand48.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/port/random.c b/src/port/random.c
deleted file mode 100644
index 2dd59a0829..0000000000
--- a/src/port/random.c
+++ /dev/null
@@ -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();
-}
diff --git a/src/port/srandom.c b/src/port/srandom.c
deleted file mode 100644
index cf1007b2ef..0000000000
--- a/src/port/srandom.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/test/modules/test_bloomfilter/test_bloomfilter.c b/src/test/modules/test_bloomfilter/test_bloomfilter.c
index 96c5011428..69425bd6fc 100644
--- a/src/test/modules/test_bloomfilter/test_bloomfilter.c
+++ b/src/test/modules/test_bloomfilter/test_bloomfilter.c
@@ -13,6 +13,7 @@
 #include "postgres.h"
 
 #include "fmgr.h"
+#include "common/pg_prng.h"
 #include "lib/bloomfilter.h"
 #include "miscadmin.h"
 
@@ -85,7 +86,7 @@ create_and_test_bloom(int power, int64 nelements, int callerseed)
 	 * 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_i32(&pg_global_prng_state) % PG_INT32_MAX : callerseed;
 
 	/* Create Bloom filter, populate it, and report on false positive rate */
 	filter = bloom_create(nelements, bloom_work_mem, seed);
diff --git a/src/test/modules/test_integerset/test_integerset.c b/src/test/modules/test_integerset/test_integerset.c
index 21c6f49b37..3e8cb7a005 100644
--- a/src/test/modules/test_integerset/test_integerset.c
+++ b/src/test/modules/test_integerset/test_integerset.c
@@ -12,6 +12,7 @@
  */
 #include "postgres.h"
 
+#include "common/pg_prng.h"
 #include "fmgr.h"
 #include "lib/integerset.h"
 #include "miscadmin.h"
@@ -99,12 +100,16 @@ static void check_with_filler(IntegerSet *intset, uint64 x, uint64 value, uint64
 static void test_single_value_and_filler(uint64 value, uint64 filler_min, uint64 filler_max);
 static void test_huge_distances(void);
 
+static pg_prng_state	pr_state;
+
 /*
  * SQL-callable entry point to perform all tests.
  */
 Datum
 test_integerset(PG_FUNCTION_ARGS)
 {
+	pg_prng_strong_seed(&pr_state);
+
 	/* Tests for various corner cases */
 	test_empty();
 	test_huge_distances();
@@ -248,8 +253,7 @@ test_pattern(const test_spec *spec)
 		 * only a small part of the integer space is used.  We would very
 		 * rarely hit values that are actually in the set.
 		 */
-		x = (pg_lrand48() << 31) | pg_lrand48();
-		x = x % (last_int + 1000);
+		x = pg_prng_u64(&pr_state) % (last_int + 1000);
 
 		/* Do we expect this value to be present in the set? */
 		if (x >= last_int)
@@ -571,7 +575,7 @@ test_huge_distances(void)
 	 */
 	while (num_values < 1000)
 	{
-		val += pg_lrand48();
+		val += pg_prng_u32(&pr_state);
 		values[num_values++] = val;
 	}
 
diff --git a/src/test/modules/test_rbtree/test_rbtree.c b/src/test/modules/test_rbtree/test_rbtree.c
index 713ebd1b26..59a571185e 100644
--- a/src/test/modules/test_rbtree/test_rbtree.c
+++ b/src/test/modules/test_rbtree/test_rbtree.c
@@ -14,6 +14,7 @@
 #include "postgres.h"
 
 #include "fmgr.h"
+#include "common/pg_prng.h"
 #include "lib/rbtree.h"
 #include "utils/memutils.h"
 
@@ -108,7 +109,7 @@ GetPermutation(int size)
 	 */
 	for (i = 1; i < size; i++)
 	{
-		int			j = random() % (i + 1);
+		int			j = pg_prng_u32(&pg_global_prng_state) % (i + 1);
 
 		if (j < i)				/* avoid fetching undefined data if j=i */
 			permutation[i] = permutation[j];
@@ -320,7 +321,7 @@ testdelete(int size, int delsize)
 
 	for (i = 0; i < delsize; i++)
 	{
-		int			k = random() % size;
+		int			k = pg_prng_u32(&pg_global_prng_state) % size;
 
 		while (chosen[k])
 			k = (k + 1) % size;
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 4362bd44fd..aa048d949f 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -99,9 +99,9 @@ sub mkvcbuild
 	$solution = CreateSolution($vsVersion, $config);
 
 	our @pgportfiles = qw(
-	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c random.c
-	  srandom.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
+	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c
+	  getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
+	  snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
 	  dirent.c dlopen.c getopt.c getopt_long.c link.c
 	  pread.c preadv.c pwrite.c pwritev.c pg_bitutils.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
 	  f2s.c file_perm.c file_utils.c hashfn.c ip.c jsonapi.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
-	  saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c username.c
-	  wait_error.c wchar.c);
+	  pg_get_line.c pg_lzcompress.c pg_prng.c pgfnames.c psprintf.c relpath.c
+	  rmtree.c saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c
+	  username.c wait_error.c wchar.c);
 
 	if ($solution->{options}->{openssl})
 	{
diff --git a/src/tools/testint128.c b/src/tools/testint128.c
index 71c345969a..43d569aeb5 100644
--- a/src/tools/testint128.c
+++ b/src/tools/testint128.c
@@ -27,6 +27,7 @@
 #endif
 
 #include "common/int128.h"
+#include "common/pg_prng.h"
 
 /*
  * We assume the parts of this union are laid out compatibly.
@@ -61,27 +62,11 @@ my_int128_compare(int128 x, int128 y)
 	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.
  *
  * 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 fix the prng state (hmmm...).
  *
  * 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;
 
+	pg_prng_seed(&pg_global_prng_state, 0);
+
 	if (argc >= 2)
 		count = strtol(argv[1], NULL, 0);
 	else
@@ -97,9 +84,9 @@ main(int argc, char **argv)
 
 	while (count-- > 0)
 	{
-		int64		x = get_random_uint64();
-		int64		y = get_random_uint64();
-		int64		z = get_random_uint64();
+		int64		x = pg_prng_u64(&pg_global_prng_state);
+		int64		y = pg_prng_u64(&pg_global_prng_state);
+		int64		z = pg_prng_u64(&pg_global_prng_state);
 		test128		t1;
 		test128		t2;
 
#48Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Fabien COELHO (#47)
Re: rand48 replacement

Just FTR, I strongly object to your removal of process-startup srandom()
calls.

Ok. The point of the patch is to replace and unify the postgres underlying
PRNG, so there was some logic behind this removal.

FTR, this was triggered by your comment on Jul 1:

[...] I see that you probably did that because random.c and srandom.c
depend on it, but I wonder why we don't make an effort to flush those
altogether. It's surely pretty confusing to newbies that what appears
to be a call of the libc primitives is no such thing.

I understood "flushing s?random.c" as that it would be a good thing to
remove their definitions, hence their calls, whereas in the initial patch
I provided a replacement for srandom & random.

--
Fabien.

#49Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Fabien COELHO (#47)
Re: rand48 replacement

Attached v15 also does call srandom if it is there, and fixes yet another
remaining random call.

I think that I have now removed all references to "random" from pg source.
However, the test still fails on windows, because the linker does not find
a global variable when compiling extensions, but it seems to find the
functions defined in the very same file...

Link:
4130 C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\x86_amd64\link.exe /ERRORREPORT:QUEUE /OUT:".\Release\tablefunc\tablefunc.dll" /INCREMENTAL:NO /NOLOGO Release/postgres/postgres.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /NODEFAULTLIB:libc /DEF:"./Release/tablefunc/tablefunc.def" /MANIFEST /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /manifest:embed /DEBUG /PDB:".\Release\tablefunc\tablefunc.pdb" /SUBSYSTEM:CONSOLE /STACK:"4194304" /TLBID:1 /DYNAMICBASE:NO /NXCOMPAT /IMPLIB:"Release/tablefunc/tablefunc.lib" /MACHINE:X64 /ignore:4197 /DLL .\Release\tablefunc\win32ver.res
4131 .\Release\tablefunc\tablefunc.obj
4132 Creating library Release/tablefunc/tablefunc.lib and object Release/tablefunc/tablefunc.exp
4133 tablefunc.obj : error LNK2001: unresolved external symbol pg_global_prng_state [C:\projects\postgresql\tablefunc.vcxproj]
4134 .\Release\tablefunc\tablefunc.dll : fatal error LNK1120: 1 unresolved externals [C:\projects\postgresql\tablefunc.vcxproj]
4135 Done Building Project "C:\projects\postgresql\tablefunc.vcxproj" (default targets) -- FAILED.

The missing symbol is really defined in common/pg_prng.c which AFAICT is
linked with postgres.

If someone experienced with the windows compilation chain could give a
hint of what is needed, I'd appreciate it!

--
Fabien.

#50Thomas Munro
thomas.munro@gmail.com
In reply to: Fabien COELHO (#49)
Re: rand48 replacement

On Thu, Sep 30, 2021 at 9:23 PM Fabien COELHO <coelho@cri.ensmp.fr> wrote:

The missing symbol is really defined in common/pg_prng.c which AFAICT is
linked with postgres.

If someone experienced with the windows compilation chain could give a
hint of what is needed, I'd appreciate it!

I guess the declaration needs PGDLLIMPORT.

#51Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Thomas Munro (#50)
1 attachment(s)
Re: rand48 replacement

I guess the declaration needs PGDLLIMPORT.

Indeed, thanks!

Attached v16 adds that.

--
Fabien.

Attachments:

prng-16.patchtext/x-diff; name=prng-16.patchDownload
diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c
index d19f73127c..b250ae912b 100644
--- a/contrib/amcheck/verify_nbtree.c
+++ b/contrib/amcheck/verify_nbtree.c
@@ -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"
@@ -465,8 +466,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();
+		/* Random seed relies on backend prng initialization to avoid repetition */
+		seed = pg_prng_u64(&pg_global_prng_state);
 		/* Create Bloom filter to fingerprint index */
 		state->filter = bloom_create(total_elems, maintenance_work_mem, seed);
 		state->heaptuplespresent = 0;
diff --git a/contrib/auto_explain/auto_explain.c b/contrib/auto_explain/auto_explain.c
index e9092ba359..0c4279447c 100644
--- a/contrib/auto_explain/auto_explain.c
+++ b/contrib/auto_explain/auto_explain.c
@@ -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"
@@ -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_f64(&pg_global_prng_state) < auto_explain_sample_rate;
 		else
 			current_query_sampled = false;
 	}
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 2c2f149fb0..146b524076 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -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]);
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 76d4fea21c..c139382170 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -5157,7 +5157,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]);
 		}
diff --git a/contrib/tablefunc/tablefunc.c b/contrib/tablefunc/tablefunc.c
index 52b272f298..ac6db426ac 100644
--- a/contrib/tablefunc/tablefunc.c
+++ b/contrib/tablefunc/tablefunc.c
@@ -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"
@@ -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_f64(&pg_global_prng_state);
+		u2 = pg_prng_f64(&pg_global_prng_state);
 
 		v1 = (2.0 * u1) - 1.0;
 		v2 = (2.0 * u2) - 1.0;
diff --git a/contrib/tsm_system_rows/tsm_system_rows.c b/contrib/tsm_system_rows/tsm_system_rows.c
index 4996612902..1a46d4b143 100644
--- a/contrib/tsm_system_rows/tsm_system_rows.c
+++ b/contrib/tsm_system_rows/tsm_system_rows.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/contrib/tsm_system_time/tsm_system_time.c b/contrib/tsm_system_time/tsm_system_time.c
index 788d8f9a68..36acc6c106 100644
--- a/contrib/tsm_system_time/tsm_system_time.c
+++ b/contrib/tsm_system_time/tsm_system_time.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index 03191e016c..3c2f24b653 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -16,6 +16,7 @@
 
 #include "access/gin_private.h"
 #include "access/relscan.h"
+#include "common/pg_prng.h"
 #include "miscadmin.h"
 #include "storage/predicate.h"
 #include "utils/datum.h"
@@ -787,7 +788,7 @@ entryLoadMoreItems(GinState *ginstate, GinScanEntry entry,
 	}
 }
 
-#define gin_rand() (((double) random()) / ((double) MAX_RANDOM_VALUE))
+#define gin_rand() pg_prng_f64(&pg_global_prng_state)
 #define dropItem(e) ( gin_rand() > ((double)GinFuzzySearchLimit)/((double)((e)->predictNumberResult)) )
 
 /*
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 43ba03b6eb..94dbabc198 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -19,6 +19,7 @@
 #include "access/htup_details.h"
 #include "access/reloptions.h"
 #include "catalog/pg_opclass.h"
+#include "common/pg_prng.h"
 #include "storage/indexfsm.h"
 #include "storage/lmgr.h"
 #include "utils/float.h"
@@ -507,7 +508,7 @@ gistchoose(Relation r, Page p, IndexTuple it,	/* it has compressed entry */
 			if (keep_current_best == -1)
 			{
 				/* we didn't make the random choice yet for this old best */
-				keep_current_best = (random() <= (MAX_RANDOM_VALUE / 2)) ? 1 : 0;
+				keep_current_best = pg_prng_bool(&pg_global_prng_state) ? 1 : 0;
 			}
 			if (keep_current_best == 0)
 			{
@@ -529,7 +530,7 @@ gistchoose(Relation r, Page p, IndexTuple it,	/* it has compressed entry */
 			if (keep_current_best == -1)
 			{
 				/* we didn't make the random choice yet for this old best */
-				keep_current_best = (random() <= (MAX_RANDOM_VALUE / 2)) ? 1 : 0;
+				keep_current_best = pg_prng_bool(&pg_global_prng_state) ? 1 : 0;
 			}
 			if (keep_current_best == 1)
 				break;
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index 7355e1dba1..37d8840ce0 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -19,6 +19,7 @@
 #include "access/nbtxlog.h"
 #include "access/transam.h"
 #include "access/xloginsert.h"
+#include "common/pg_prng.h"
 #include "lib/qunique.h"
 #include "miscadmin.h"
 #include "storage/lmgr.h"
@@ -968,7 +969,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_f64(&pg_global_prng_state) <= 0.01)
 				break;
 
 			_bt_stepright(rel, insertstate, stack);
diff --git a/src/backend/access/spgist/spgdoinsert.c b/src/backend/access/spgist/spgdoinsert.c
index 70557bcf3d..5389c93366 100644
--- a/src/backend/access/spgist/spgdoinsert.c
+++ b/src/backend/access/spgist/spgdoinsert.c
@@ -19,6 +19,7 @@
 #include "access/spgist_private.h"
 #include "access/spgxlog.h"
 #include "access/xloginsert.h"
+#include "common/pg_prng.h"
 #include "miscadmin.h"
 #include "storage/bufmgr.h"
 #include "utils/rel.h"
@@ -2210,7 +2211,7 @@ 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_u32(&pg_global_prng_state) % innerTuple->nNodes;
 			}
 
 			switch (out.resultType)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 6597ec45a9..d0557be7c9 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -37,6 +37,7 @@
 #include "commands/async.h"
 #include "commands/tablecmds.h"
 #include "commands/trigger.h"
+#include "common/pg_prng.h"
 #include "executor/spi.h"
 #include "libpq/be-fsstubs.h"
 #include "libpq/pqsignal.h"
@@ -1939,7 +1940,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_f64(&pg_global_prng_state) <= log_xact_sample_rate);
 
 	/*
 	 * initialize current transaction state fields
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 8bfb2ad958..fe623393d7 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -38,6 +38,7 @@
 #include "commands/progress.h"
 #include "commands/tablecmds.h"
 #include "commands/vacuum.h"
+#include "common/pg_prng.h"
 #include "executor/executor.h"
 #include "foreign/fdwapi.h"
 #include "miscadmin.h"
@@ -1173,7 +1174,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 	OldestXmin = GetOldestNonRemovableTransactionId(onerel);
 
 	/* Prepare for sampling block numbers */
-	randseed = random();
+	randseed = pg_prng_i32(&pg_global_prng_state);
 	nblocks = BlockSampler_Init(&bs, totalblocks, targrows, randseed);
 
 #ifdef USE_PREFETCH
@@ -1290,7 +1291,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 					 * Found a suitable tuple, so save it, replacing one old
 					 * tuple at random
 					 */
-					int			k = (int) (targrows * sampler_random_fract(rstate.randstate));
+					int			k = (int) (targrows * sampler_random_fract(&rstate.randstate));
 
 					Assert(k >= 0 && k < targrows);
 					heap_freetuple(rows[k]);
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 44232d50d0..09c34e96fa 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -17,6 +17,7 @@
 #include "access/relscan.h"
 #include "access/tableam.h"
 #include "access/tsmapi.h"
+#include "common/pg_prng.h"
 #include "executor/executor.h"
 #include "executor/nodeSamplescan.h"
 #include "miscadmin.h"
@@ -154,7 +155,7 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	 * do this just once, since the seed shouldn't change over rescans.
 	 */
 	if (tsc->repeatable == NULL)
-		scanstate->seed = random();
+		scanstate->seed = pg_prng_i32(&pg_global_prng_state);
 
 	/*
 	 * Finally, initialize the TABLESAMPLE method handler.
diff --git a/src/backend/optimizer/geqo/geqo_random.c b/src/backend/optimizer/geqo/geqo_random.c
index f21bc047e6..8b42a9ffba 100644
--- a/src/backend/optimizer/geqo/geqo_random.c
+++ b/src/backend/optimizer/geqo/geqo_random.c
@@ -21,14 +21,7 @@ geqo_set_seed(PlannerInfo *root, double seed)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	/*
-	 * XXX. This seeding algorithm could certainly be improved - but it is not
-	 * critical to do so.
-	 */
-	memset(private->random_state, 0, sizeof(private->random_state));
-	memcpy(private->random_state,
-		   &seed,
-		   Min(sizeof(private->random_state), sizeof(seed)));
+	pg_prng_fseed(&private->random_state, seed);
 }
 
 double
@@ -36,5 +29,5 @@ geqo_rand(PlannerInfo *root)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	return pg_erand48(private->random_state);
+	return pg_prng_f64(&private->random_state);
 }
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index e2a76ba055..09b4ec9b72 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -98,6 +98,7 @@
 #include "catalog/pg_control.h"
 #include "common/file_perm.h"
 #include "common/ip.h"
+#include "common/pg_prng.h"
 #include "common/string.h"
 #include "lib/ilist.h"
 #include "libpq/auth.h"
@@ -2675,19 +2676,22 @@ ClosePostmasterPorts(bool am_syslogger)
 void
 InitProcessGlobals(void)
 {
-	unsigned int rseed;
-
 	MyProcPid = getpid();
 	MyStartTimestamp = GetCurrentTimestamp();
 	MyStartTime = timestamptz_to_time_t(MyStartTimestamp);
 
 	/*
-	 * Set a different seed for random() in every process.  We want something
+	 * Set a different global seed in every process.  We want something
 	 * unpredictable, so if possible, use high-quality random bits for the
 	 * seed.  Otherwise, fall back to a seed based on timestamp and PID.
 	 */
-	if (!pg_strong_random(&rseed, sizeof(rseed)))
+	if (!pg_prng_strong_seed(&pg_global_prng_state))
 	{
+		uint64	rseed;
+
+		ereport(WARNING,
+				(errmsg_internal("pg_prng_strong_seed() failed, falling back to weaker seeding")));
+
 		/*
 		 * Since PIDs and timestamps tend to change more frequently in their
 		 * least significant bits, shift the timestamp left to allow a larger
@@ -2698,8 +2702,13 @@ InitProcessGlobals(void)
 		rseed = ((uint64) MyProcPid) ^
 			((uint64) MyStartTimestamp << 12) ^
 			((uint64) MyStartTimestamp >> 20);
+
+		pg_prng_seed(&pg_global_prng_state, rseed);
 	}
-	srandom(rseed);
+
+#ifdef HAVE_SRANDOM
+	srandom(pg_prng_i32(&pg_global_prng_state));
+#endif
 }
 
 
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index f9cda6906d..431931bcfe 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -92,6 +92,7 @@
 #include "catalog/pg_tablespace.h"
 #include "common/file_perm.h"
 #include "common/file_utils.h"
+#include "common/pg_prng.h"
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "port/pg_iovec.h"
@@ -2938,7 +2939,7 @@ SetTempTablespaces(Oid *tableSpaces, int numSpaces)
 	 * available tablespaces.
 	 */
 	if (numSpaces > 1)
-		nextTempTableSpace = random() % numSpaces;
+		nextTempTableSpace = pg_prng_u32(&pg_global_prng_state) % numSpaces;
 	else
 		nextTempTableSpace = 0;
 }
diff --git a/src/backend/storage/ipc/dsm.c b/src/backend/storage/ipc/dsm.c
index b461a5f7e9..e97844614e 100644
--- a/src/backend/storage/ipc/dsm.c
+++ b/src/backend/storage/ipc/dsm.c
@@ -33,6 +33,7 @@
 #endif
 #include <sys/stat.h>
 
+#include "common/pg_prng.h"
 #include "lib/ilist.h"
 #include "miscadmin.h"
 #include "port/pg_bitutils.h"
@@ -180,7 +181,7 @@ dsm_postmaster_startup(PGShmemHeader *shim)
 	{
 		Assert(dsm_control_address == NULL);
 		Assert(dsm_control_mapped_size == 0);
-		dsm_control_handle = random() << 1; /* Even numbers only */
+		dsm_control_handle = pg_prng_u32(&pg_global_prng_state) << 1; /* Even numbers only */
 		if (dsm_control_handle == DSM_HANDLE_INVALID)
 			continue;
 		if (dsm_impl_op(DSM_OP_CREATE, dsm_control_handle, segsize,
@@ -536,7 +537,7 @@ dsm_create(Size size, int flags)
 		for (;;)
 		{
 			Assert(seg->mapped_address == NULL && seg->mapped_size == 0);
-			seg->handle = random() << 1;	/* Even numbers only */
+			seg->handle = pg_prng_u32(&pg_global_prng_state) << 1;	/* Even numbers only */
 			if (seg->handle == DSM_HANDLE_INVALID)	/* Reserve sentinel */
 				continue;
 			if (dsm_impl_op(DSM_OP_CREATE, seg->handle, size, &seg->impl_private,
@@ -1237,7 +1238,7 @@ make_main_region_dsm_handle(int slot)
 	 */
 	handle = 1;
 	handle |= slot << 1;
-	handle |= random() << (pg_leftmost_one_pos32(dsm_control->maxitems) + 1);
+	handle |= pg_prng_i32(&pg_global_prng_state) << (pg_leftmost_one_pos32(dsm_control->maxitems) + 1);
 	return handle;
 }
 
diff --git a/src/backend/storage/lmgr/s_lock.c b/src/backend/storage/lmgr/s_lock.c
index 91322a40c1..74c6f251ce 100644
--- a/src/backend/storage/lmgr/s_lock.c
+++ b/src/backend/storage/lmgr/s_lock.c
@@ -50,6 +50,7 @@
 #include <time.h>
 #include <unistd.h>
 
+#include "common/pg_prng.h"
 #include "port/atomics.h"
 #include "storage/s_lock.h"
 
@@ -63,6 +64,7 @@
 slock_t		dummy_spinlock;
 
 static int	spins_per_delay = DEFAULT_SPINS_PER_DELAY;
+static pg_prng_state	prng_state;
 
 
 /*
@@ -143,8 +145,7 @@ perform_spin_delay(SpinDelayStatus *status)
 #endif
 
 		/* increase delay by a random fraction between 1X and 2X */
-		status->cur_delay += (int) (status->cur_delay *
-									((double) random() / (double) MAX_RANDOM_VALUE) + 0.5);
+		status->cur_delay += (int) (status->cur_delay * (pg_prng_f64(&prng_state) + 0.5));
 		/* wrap back to minimum delay when max is exceeded */
 		if (status->cur_delay > MAX_DELAY_USEC)
 			status->cur_delay = MIN_DELAY_USEC;
@@ -303,7 +304,7 @@ volatile struct test_lock_struct test_lock;
 int
 main()
 {
-	srandom((unsigned int) time(NULL));
+	pg_prng_seed(&prng_state, (unsigned int) time(NULL));
 
 	test_lock.pad1 = test_lock.pad2 = 0x44;
 
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 0775abe35d..ef6c27dade 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -42,6 +42,7 @@
 #include "catalog/pg_type.h"
 #include "commands/async.h"
 #include "commands/prepare.h"
+#include "common/pg_prng.h"
 #include "executor/spi.h"
 #include "jit/jit.h"
 #include "libpq/libpq.h"
@@ -2355,13 +2356,13 @@ check_log_duration(char *msec_str, bool was_logged)
 
 		/*
 		 * Do not log if log_statement_sample_rate = 0. Log a sample if
-		 * log_statement_sample_rate <= 1 and avoid unnecessary random() call
+		 * log_statement_sample_rate <= 1 and avoid unnecessary random call
 		 * if log_statement_sample_rate = 1.
 		 */
 		if (exceeded_sample_duration)
 			in_sample = log_statement_sample_rate != 0 &&
 				(log_statement_sample_rate == 1 ||
-				 random() <= log_statement_sample_rate * MAX_RANDOM_VALUE);
+				 pg_prng_f64(&pg_global_prng_state) <= log_statement_sample_rate);
 
 		if (exceeded_duration || in_sample || log_duration || xact_is_sampled)
 		{
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 098bbb372b..9fc0b108dd 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -21,6 +21,7 @@
 
 #include "catalog/pg_type.h"
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/shortest_dec.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
@@ -65,7 +66,7 @@ float8		degree_c_one = 1.0;
 
 /* State for drandom() and setseed() */
 static bool drandom_seed_set = false;
-static unsigned short drandom_seed[3] = {0, 0, 0};
+static pg_prng_state drandom_seed = PG_PRNG_DEFAULT_STATE;
 
 /* Local function prototypes */
 static double sind_q1(double x);
@@ -2762,22 +2763,20 @@ drandom(PG_FUNCTION_ARGS)
 		 * Should that fail for some reason, we fall back on a lower-quality
 		 * seed based on current time and PID.
 		 */
-		if (!pg_strong_random(drandom_seed, sizeof(drandom_seed)))
+		if (!pg_strong_random(&drandom_seed, sizeof(drandom_seed)))
 		{
 			TimestampTz now = GetCurrentTimestamp();
 			uint64		iseed;
 
 			/* Mix the PID with the most predictable bits of the timestamp */
 			iseed = (uint64) now ^ ((uint64) MyProcPid << 32);
-			drandom_seed[0] = (unsigned short) iseed;
-			drandom_seed[1] = (unsigned short) (iseed >> 16);
-			drandom_seed[2] = (unsigned short) (iseed >> 32);
+			pg_prng_seed(&drandom_seed, iseed);
 		}
 		drandom_seed_set = true;
 	}
 
-	/* pg_erand48 produces desired result range [0.0 - 1.0) */
-	result = pg_erand48(drandom_seed);
+	/* produces desired result range [0.0 - 1.0) */
+	result = pg_prng_f64(&drandom_seed);
 
 	PG_RETURN_FLOAT8(result);
 }
@@ -2790,7 +2789,6 @@ Datum
 setseed(PG_FUNCTION_ARGS)
 {
 	float8		seed = PG_GETARG_FLOAT8(0);
-	uint64		iseed;
 
 	if (seed < -1 || seed > 1 || isnan(seed))
 		ereport(ERROR,
@@ -2798,11 +2796,7 @@ setseed(PG_FUNCTION_ARGS)
 				 errmsg("setseed parameter %g is out of allowed range [-1,1]",
 						seed)));
 
-	/* Use sign bit + 47 fractional bits to fill drandom_seed[] */
-	iseed = (int64) (seed * (float8) UINT64CONST(0x7FFFFFFFFFFF));
-	drandom_seed[0] = (unsigned short) iseed;
-	drandom_seed[1] = (unsigned short) (iseed >> 16);
-	drandom_seed[2] = (unsigned short) (iseed >> 32);
+	pg_prng_fseed(&drandom_seed, seed);
 	drandom_seed_set = true;
 
 	PG_RETURN_VOID();
diff --git a/src/backend/utils/misc/sampling.c b/src/backend/utils/misc/sampling.c
index 0c327e823f..f1c86150d8 100644
--- a/src/backend/utils/misc/sampling.c
+++ b/src/backend/utils/misc/sampling.c
@@ -49,7 +49,7 @@ BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize,
 	bs->t = 0;					/* blocks scanned so far */
 	bs->m = 0;					/* blocks selected so far */
 
-	sampler_random_init_state(randseed, bs->randstate);
+	sampler_random_init_state(randseed, &bs->randstate);
 
 	return Min(bs->n, bs->N);
 }
@@ -98,7 +98,7 @@ BlockSampler_Next(BlockSampler bs)
 	 * less than k, which means that we cannot fail to select enough blocks.
 	 *----------
 	 */
-	V = sampler_random_fract(bs->randstate);
+	V = sampler_random_fract(&bs->randstate);
 	p = 1.0 - (double) k / (double) K;
 	while (V < p)
 	{
@@ -136,10 +136,10 @@ reservoir_init_selection_state(ReservoirState rs, int n)
 	 * Reservoir sampling is not used anywhere where it would need to return
 	 * repeatable results so we can initialize it randomly.
 	 */
-	sampler_random_init_state(random(), rs->randstate);
+	sampler_random_init_state(pg_prng_u32(&pg_global_prng_state), &rs->randstate);
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	rs->W = exp(-log(sampler_random_fract(rs->randstate)) / n);
+	rs->W = exp(-log(sampler_random_fract(&rs->randstate)) / n);
 }
 
 double
@@ -154,7 +154,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 		double		V,
 					quot;
 
-		V = sampler_random_fract(rs->randstate);	/* Generate V */
+		V = sampler_random_fract(&rs->randstate);	/* Generate V */
 		S = 0;
 		t += 1;
 		/* Note: "num" in Vitter's code is always equal to t - n */
@@ -186,7 +186,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 						tmp;
 
 			/* Generate U and X */
-			U = sampler_random_fract(rs->randstate);
+			U = sampler_random_fract(&rs->randstate);
 			X = t * (W - 1.0);
 			S = floor(X);		/* S is tentatively set to floor(X) */
 			/* Test if U <= h(S)/cg(X) in the manner of (6.3) */
@@ -215,7 +215,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 				y *= numer / denom;
 				denom -= 1;
 			}
-			W = exp(-log(sampler_random_fract(rs->randstate)) / n); /* Generate W in advance */
+			W = exp(-log(sampler_random_fract(&rs->randstate)) / n); /* Generate W in advance */
 			if (exp(log(y) / n) <= (t + X) / t)
 				break;
 		}
@@ -230,24 +230,22 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
  *----------
  */
 void
-sampler_random_init_state(long seed, SamplerRandomState randstate)
+sampler_random_init_state(long seed, pg_prng_state *randstate)
 {
-	randstate[0] = 0x330e;		/* same as pg_erand48, but could be anything */
-	randstate[1] = (unsigned short) seed;
-	randstate[2] = (unsigned short) (seed >> 16);
+	pg_prng_seed(randstate, (uint64_t) seed);
 }
 
 /* Select a random value R uniformly distributed in (0 - 1) */
 double
-sampler_random_fract(SamplerRandomState randstate)
+sampler_random_fract(pg_prng_state *randstate)
 {
 	double		res;
 
-	/* pg_erand48 returns a value in [0.0 - 1.0), so we must reject 0 */
+	/* pg_prng_f64 returned value in [0.0 - 1.0), so we must reject 0.0 */
 	do
 	{
-		res = pg_erand48(randstate);
-	} while (res == 0.0);
+		res = pg_prng_f64(randstate);
+	} while (unlikely(res == 0.0));
 	return res;
 }
 
@@ -266,22 +264,28 @@ double
 anl_random_fract(void)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(pg_prng_u32(&pg_global_prng_state), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* and compute a random fraction */
-	return sampler_random_fract(oldrs.randstate);
+	return sampler_random_fract(&oldrs.randstate);
 }
 
 double
 anl_init_selection_state(int n)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(pg_prng_u32(&pg_global_prng_state), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	return exp(-log(sampler_random_fract(oldrs.randstate)) / n);
+	return exp(-log(sampler_random_fract(&oldrs.randstate)) / n);
 }
 
 double
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 1ed4808d53..af9f5169e9 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -66,6 +66,7 @@
 #include "common/file_perm.h"
 #include "common/file_utils.h"
 #include "common/logging.h"
+#include "common/pg_prng.h"
 #include "common/restricted_token.h"
 #include "common/string.h"
 #include "common/username.h"
@@ -880,9 +881,10 @@ choose_dsm_implementation(void)
 {
 #ifdef HAVE_SHM_OPEN
 	int			ntries = 10;
+	pg_prng_state	prng_state;
 
-	/* Initialize random(); this function is its only user in this program. */
-	srandom((unsigned int) (getpid() ^ time(NULL)));
+	/* Initialize pgng state this function is its only user in this program. */
+	pg_prng_seed(&prng_state, ((unsigned int) (getpid() ^ time(NULL))));
 
 	while (ntries > 0)
 	{
@@ -890,7 +892,7 @@ choose_dsm_implementation(void)
 		char		name[64];
 		int			fd;
 
-		handle = random();
+		handle = pg_prng_u32(&prng_state);
 		snprintf(name, 64, "/PostgreSQL.%u", handle);
 		if ((fd = shm_open(name, O_CREAT | O_RDWR | O_EXCL, 0600)) != -1)
 		{
diff --git a/src/bin/pg_test_fsync/pg_test_fsync.c b/src/bin/pg_test_fsync/pg_test_fsync.c
index fef31844fa..5b3c197aa6 100644
--- a/src/bin/pg_test_fsync/pg_test_fsync.c
+++ b/src/bin/pg_test_fsync/pg_test_fsync.c
@@ -15,6 +15,7 @@
 
 #include "access/xlogdefs.h"
 #include "common/logging.h"
+#include "common/pg_prng.h"
 #include "getopt_long.h"
 
 /*
@@ -71,6 +72,7 @@ static char full_buf[DEFAULT_XLOG_SEG_SIZE],
 static struct timeval start_t,
 			stop_t;
 static bool alarm_triggered = false;
+static pg_prng_state	prng_state;
 
 
 static void handle_args(int argc, char *argv[]);
@@ -117,6 +119,7 @@ main(int argc, char *argv[])
 	pqsignal(SIGHUP, signal_cleanup);
 #endif
 
+	pg_prng_strong_seed(&prng_state);
 	prepare_buf();
 
 	test_open();
@@ -233,7 +236,7 @@ prepare_buf(void)
 
 	/* write random data into buffer */
 	for (ops = 0; ops < DEFAULT_XLOG_SEG_SIZE; ops++)
-		full_buf[ops] = random();
+		full_buf[ops] = pg_prng_u32(&prng_state);
 
 	buf = (char *) TYPEALIGN(XLOG_BLCKSZ, full_buf);
 }
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 433abd954b..64c7370ff5 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -58,6 +58,7 @@
 #endif
 
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/logging.h"
 #include "common/string.h"
 #include "common/username.h"
@@ -350,16 +351,8 @@ typedef struct StatsData
  */
 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. */
-static RandomState base_random_sequence;
+static pg_prng_state	base_random_sequence;
 
 /* Synchronization barrier for start and connection */
 static THREAD_BARRIER_T barrier;
@@ -461,7 +454,7 @@ typedef struct
 	 * Separate randomness for each client. This is used for random functions
 	 * 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			command;		/* command number in script */
@@ -498,9 +491,9 @@ typedef struct
 	 * random state to make all of them independent of each other and
 	 * therefore deterministic at the thread level.
 	 */
-	RandomState ts_choose_rs;	/* random state for selecting a script */
-	RandomState ts_throttle_rs; /* random state for transaction throttling */
-	RandomState ts_sample_rs;	/* random state for log sampling */
+	pg_prng_state	ts_choose_rs;	/* random state for selecting a script */
+	pg_prng_state	ts_throttle_rs; /* random state for transaction throttling */
+	pg_prng_state	ts_sample_rs;	/* random state for log sampling */
 
 	int64		throttle_trigger;	/* previous/next throttling (us) */
 	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.
  */
 static void
-initRandomState(RandomState *random_state)
+initRandomState(pg_prng_state *state)
 {
-	random_state->xseed[0] = (unsigned short)
-		(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);
+	pg_prng_seed(state, pg_prng_u64(&base_random_sequence));
 }
 
+
 /*
- * Random number generator: uniform distribution from min to max inclusive.
+ * PRNG: uniform distribution from min to max inclusive.
  *
  * Although the limits are expressed as int64, you can't generate the full
  * int64 range in one call, because the difference of the limits mustn't
- * overflow int64.  In practice it's unwise to ask for more than an int32
- * range, because of the limited precision of pg_erand48().
+ * overflow int64. This is not checked.
  */
 static int64
-getrand(RandomState *random_state, int64 min, int64 max)
+getrand(pg_prng_state *state, int64 min, int64 max)
 {
-	/*
-	 * 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));
+	return min + (int64) pg_prng_u64_range(state, 0, max - min);
 }
 
 /*
@@ -942,8 +921,7 @@ getrand(RandomState *random_state, int64 min, int64 max)
  * value is exp(-parameter).
  */
 static int64
-getExponentialRand(RandomState *random_state, int64 min, int64 max,
-				   double parameter)
+getExponentialRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		cut,
 				uniform,
@@ -953,7 +931,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 	Assert(parameter > 0.0);
 	cut = exp(-parameter);
 	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	uniform = 1.0 - pg_prng_f64(state);
 
 	/*
 	 * inner expression in (cut, 1] (if parameter > 0), rand in [0, 1)
@@ -966,8 +944,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 
 /* random number generator: gaussian distribution from min to max inclusive */
 static int64
-getGaussianRand(RandomState *random_state, int64 min, int64 max,
-				double parameter)
+getGaussianRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		stdev;
 	double		rand;
@@ -990,13 +967,13 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
 	do
 	{
 		/*
-		 * pg_erand48 generates [0,1), but for the basic version of the
+		 * pg_prng_f64 generates [0,1), but for the basic version of the
 		 * Box-Muller transform the two uniformly distributed random numbers
 		 * are expected in (0, 1] (see
 		 * https://en.wikipedia.org/wiki/Box-Muller_transform)
 		 */
-		double		rand1 = 1.0 - pg_erand48(random_state->xseed);
-		double		rand2 = 1.0 - pg_erand48(random_state->xseed);
+		double		rand1 = 1.0 - pg_prng_f64(state);
+		double		rand2 = 1.0 - pg_prng_f64(state);
 
 		/* Box-Muller basic form transform */
 		double		var_sqrt = sqrt(-2.0 * log(rand1));
@@ -1026,7 +1003,7 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
  * not be one.
  */
 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
@@ -1034,8 +1011,8 @@ getPoissonRand(RandomState *random_state, double center)
 	 */
 	double		uniform;
 
-	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	/* pseudo-random value in [0, 1), uniform in (0, 1] */
+	uniform = 1.0 - pg_prng_f64(state);
 
 	return (int64) (-log(uniform) * center + 0.5);
 }
@@ -1048,7 +1025,7 @@ getPoissonRand(RandomState *random_state, double center)
  * This works for s > 1.0, but may perform badly for s very close to 1.0.
  */
 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		x,
@@ -1063,8 +1040,8 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 	while (true)
 	{
 		/* random variates */
-		u = pg_erand48(random_state->xseed);
-		v = pg_erand48(random_state->xseed);
+		u = pg_prng_f64(state);
+		v = pg_prng_f64(state);
 
 		x = floor(pow(u, -1.0 / (s - 1.0)));
 
@@ -1078,14 +1055,14 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 
 /* random number generator: zipfian distribution from min to max inclusive */
 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;
 
 	/* abort if parameter is invalid */
 	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 +1119,7 @@ getHashMurmur2(int64 val, uint64 seed)
  * For small sizes, this generates each of the (size!) possible permutations
  * of integers in the range [0, size) with roughly equal probability.  Once
  * the size is larger than 20, the number of possible permutations exceeds the
- * number of distinct states of the internal pseudorandom number generators,
+ * number of distinct states of the internal pseudorandom number generator,
  * and so not all possible permutations can be generated, but the permutations
  * chosen should continue to give the appearance of being random.
  *
@@ -1152,25 +1129,19 @@ getHashMurmur2(int64 val, uint64 seed)
 static int64
 permute(const int64 val, const int64 isize, const int64 seed)
 {
-	RandomState random_state1;
-	RandomState random_state2;
-	uint64		size;
-	uint64		v;
-	int			masklen;
-	uint64		mask;
-	int			i;
+	/* using a high-end PRNG is probably overkill */
+	pg_prng_state	state;
+	uint64			size;
+	uint64			v;
+	int				masklen;
+	uint64			mask;
+	int				i;
 
 	if (isize < 2)
 		return 0;				/* nothing to permute */
 
-	/* Initialize a pair of random states using the seed */
-	random_state1.xseed[0] = seed & 0xFFFF;
-	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;
+	/* Initialize prng state using the seed */
+	pg_prng_seed(&state, (uint64) seed);
 
 	/* Computations are performed on unsigned values */
 	size = (uint64) isize;
@@ -1216,8 +1187,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 					t;
 
 		/* Random multiply (by an odd number), XOR and rotate of lower half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		if (v <= mask)
 		{
 			v = ((v * m) ^ r) & mask;
@@ -1225,8 +1196,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random multiply (by an odd number), XOR and rotate of upper half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		t = size - 1 - v;
 		if (t <= mask)
 		{
@@ -1236,7 +1207,7 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random offset */
-		r = (uint64) getrand(&random_state2, 0, size - 1);
+		r = pg_prng_u64(&state) % (size - 1);
 		v = (v + r) % size;
 	}
 
@@ -3827,7 +3798,7 @@ doLog(TState *thread, CState *st,
 	 * to the random sample.
 	 */
 	if (sample_rate != 0.0 &&
-		pg_erand48(thread->ts_sample_rs.xseed) > sample_rate)
+		pg_prng_f64(&thread->ts_sample_rs) > sample_rate)
 		return;
 
 	/* should we aggregate the results or not? */
@@ -5763,12 +5734,11 @@ set_random_seed(const char *seed)
 
 	if (seed != NULL)
 		pg_log_info("setting random seed to %llu", (unsigned long long) iseed);
+
 	random_seed = iseed;
 
 	/* Fill base_random_sequence with low-order bits of seed */
-	base_random_sequence.xseed[0] = iseed & 0xFFFF;
-	base_random_sequence.xseed[1] = (iseed >> 16) & 0xFFFF;
-	base_random_sequence.xseed[2] = (iseed >> 32) & 0xFFFF;
+	pg_prng_seed(&base_random_sequence, (uint64_t) iseed);
 
 	return true;
 }
@@ -6439,9 +6409,7 @@ main(int argc, char **argv)
 	/* set default seed for hash functions */
 	if (lookupVariable(&state[0], "default_seed") == NULL)
 	{
-		uint64		seed =
-		((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) |
-		(((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) << 32);
+		uint64		seed = pg_prng_u64(&base_random_sequence);
 
 		for (i = 0; i < nclients; i++)
 			if (!putVariableInt(&state[i], "startup", "default_seed", (int64) seed))
diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl
index ef53f6b2d9..13c9204a3d 100644
--- a/src/bin/pgbench/t/001_pgbench_with_server.pl
+++ b/src/bin/pgbench/t/001_pgbench_with_server.pl
@@ -427,9 +427,9 @@ pgbench(
 
 		# After explicit seeding, the four random checks (1-3,20) are
 		# deterministic
-		qr{command=1.: int 13\b},      # uniform random
-		qr{command=2.: int 116\b},     # exponential random
-		qr{command=3.: int 1498\b},    # gaussian random
+		qr{command=1.: int 17\b},      # uniform random
+		qr{command=2.: int 108\b},     # exponential random
+		qr{command=3.: int 1452\b},    # gaussian random
 		qr{command=4.: int 4\b},
 		qr{command=5.: int 5\b},
 		qr{command=6.: int 6\b},
@@ -496,6 +496,7 @@ pgbench(
 		qr{command=109.: boolean true\b},
 		qr{command=110.: boolean true\b},
 		qr{command=111.: boolean true\b},
+		qr{command=113.: boolean true\b},
 	],
 	'pgbench expressions',
 	{
@@ -639,8 +640,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))
 -- actual values
 \set t debug(permute(:v, 1) = 0)
-\set t debug(permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1 and \
-             permute(0, 2, 5435) = 1 and permute(1, 2, 5435) = 0)
+\set t debug(permute(0, 2, 5431) = 1 and permute(1, 2, 5431) = 0 and \
+             permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1)
+-- 63 bits tests for checking permute portability across architectures
+\set size debug(:max - 10)
+\set t debug(permute(:size-1, :size, 5432) = 4352241160009873020 and \
+             permute(:size-2, :size, 5432) = 4990004119691638710 and \
+             permute(:size-3, :size, 5432) = 5868029867256127549 and \
+             permute(:size-4, :size, 5432) = 1238830324886341378 and \
+             permute(:size-5, :size, 5432) = 7415914367102906897 and \
+             permute(:size-6, :size, 5432) = 3214501037984818995 and \
+             permute(:size-7, :size, 5432) =  600660351261124695)
 }
 	});
 
diff --git a/src/common/Makefile b/src/common/Makefile
index 880722fcf5..31c0dd366d 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -66,6 +66,7 @@ OBJS_COMMON = \
 	md5_common.o \
 	pg_get_line.o \
 	pg_lzcompress.o \
+	pg_prng.o \
 	pgfnames.o \
 	psprintf.o \
 	relpath.o \
diff --git a/src/common/pg_prng.c b/src/common/pg_prng.c
new file mode 100644
index 0000000000..8bcd65a323
--- /dev/null
+++ b/src/common/pg_prng.c
@@ -0,0 +1,244 @@
+/*
+ * Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * Previous version was the 1988 (?) rand48 standard with 48 bits state LCG,
+ * designed for running on 32 bits or even 16 bits architectures, and to produce
+ * 32 bit ints and floats (i.e. *not* doubles).
+ *
+ * The implied 16-bits unpacking/packing may have a detrimental performance impact
+ * on modern 64 bits architectures.
+ *
+ * We (probably) want:
+ * - one reasonable default PRNG for all pg internal uses.
+ * - NOT to invent a new design!
+ * - something fast, close to rand48 (which basically does 2 arithmetic ops)
+ *   no need for something cryptographic though, which would imply slow
+ * - to produce 64 bits integers & doubles with a 52 bits mantissa
+ *   so state size > 64 bits.
+ * - a small state though, because we might generate quite a few of them
+ *   for different purposes so state size <= 256 or even <= 128 bits
+ * - the state to be aligned to whatever, so state size = 128 bits
+ * - 64 bits operations for efficiency on modern architectures,
+ *   but NOT 128 bits operations.
+ * - not to depend on special hardware for speed (eg MMX/SSE/AES).
+ * - not something with obvious known and relevant defects.
+ * - not something with "rights" attached.
+ *
+ * These constraints reduce a lot the available options from
+ * https://en.wikipedia.org/wiki/List_of_random_number_generators
+ *
+ * LCG
+ * - POSIX rand48: m = 2^48, a = 25214903917, c = 11, extract 47...16
+ * - MMIX by Donald Knuth: m = 2^64, a = 6364136223846793005, c = 1442695040888963407
+ * - very bad on low bits, which MUST NOT BE USED
+ *   a 128 bits would do, but no clear standards, and it would imply 128-bit ops,
+ *   see PCG below.
+ *
+ * PCG Family (https://www.pcg-random.org/)
+ * - this is basically an LCG with an improved output function
+ *   so as to avoid typical LCG issues and pass stats tests.
+ * - recommands state size to be twice the output size,
+ *   which seems a reasonable requirement.
+ * - this implies 128 bit ops for our target parameters,
+ *   otherwise it is a fairly good candidate for simplicity,
+ *   efficiency and quality.
+ *
+ * Xor-Shift-Rotate PRNGs:
+ * - a generic annoying feature of this generator class is that there is one
+ *   special all-zero state which must not be used, inducing a constraint on
+ *   initialization, and when the generator get close to it (i.e. many zeros
+ *   in the state) it takes some iterations to recover, with small lumps
+ *   of close/related/bad numbers generated in the process.
+ * Xoroshiro128+
+ * - fails statistical linearity tests in the low bits.
+ * Xoroshiro128**
+ *   https://prng.di.unimi.it/xoroshiro128starstar.c
+ * - mild and irrelevant issues: close to zero state, stats issues after some transforms
+ * Xoshiro256**
+ * - it looks good, but some issues (no big deal for pg usage IMO)
+ *   https://www.pcg-random.org/posts/a-quick-look-at-xoshiro256.html
+ *   mild problems getting out of closes-to-zero state
+ *
+ * MT19937-64
+ * + default in many places, standard
+ * - huge 2.5 KiB state (but TinyMT, 127 bits state space)
+ * - slow (but SFMT, twice as fast)
+ * - vulnerabilities on tinyMT?
+ *
+ * WELL
+ * - large state, 32-bit oriented
+ *
+ * ACORN Family (https://en.wikipedia.org/wiki/ACORN_(PRNG))
+ * - Fortran / double oriented, no C implementation found?!
+ * - medium large state "In practice we recommend using k > 10, M = 2^60 (for general application)
+ *   or M = 2^120 (for demanding applications requiring high-quality pseudo-random numbers
+ *   that will consistently pass all the tests in standard test packages such as TestU01)
+ *   and choose any odd value less than M for the seed."
+ *   k = 11 => 88 bytes/704-bits state, too large:-(
+ *
+ * Splitmix?
+ * Others?
+ *
+ * The conclusion is that we use xor-shift-rotate generator below.
+ */
+
+#include "c.h"
+#include "common/pg_prng.h"
+#include "port/pg_bitutils.h"
+#include <math.h>
+
+/* global state */
+pg_prng_state	pg_global_prng_state;
+
+#define FIRST_BIT_MASK UINT64CONST(0x8000000000000000)
+#define RIGHT_HALF_MASK UINT64CONST(0x00000000FFFFFFFF)
+#define DMANTISSA_MASK UINT64CONST(0x000FFFFFFFFFFFFF)
+
+/* 64-bits rotate left */
+static inline uint64
+rotl(const uint64 x, const int bits)
+{
+	return (x << bits) | (x >> (64 - bits));
+}
+
+/* another 64-bits generator is used for state initialization or iterations */
+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);
+}
+
+/* seed prng state from a 64 bits integer, ensuring non zero */
+void
+pg_prng_seed(pg_prng_state *state, uint64 seed)
+{
+	state->s0 = splitmix64(&seed);
+	state->s1 = splitmix64(&seed);
+}
+
+/* seed with 53 bits (mantissa & sign) from a float */
+void
+pg_prng_fseed(pg_prng_state *state, double fseed)
+{
+	uint64 seed = (int64) (((double) DMANTISSA_MASK) * fseed);
+	pg_prng_seed(state, seed);
+}
+
+/* strong random seeding */
+bool
+pg_prng_strong_seed(pg_prng_state *state)
+{
+	bool ok = pg_strong_random((void *) state, sizeof(pg_prng_state));
+
+	/* avoid zero with Donald Knuth's LCG parameters */
+	if (unlikely(state->s0 == 0 && state->s1 == 0))
+	{
+		/* should it warn that something is amiss if we get there? */
+		pg_prng_state def = PG_PRNG_DEFAULT_STATE;
+		*state = def;
+	}
+
+	return ok;
+}
+
+/* generator & state update */
+static uint64
+xoroshiro128ss(pg_prng_state *state)
+{
+	const 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;
+}
+
+/* u64 generator */
+uint64
+pg_prng_u64(pg_prng_state *state)
+{
+	return xoroshiro128ss(state);
+}
+
+/*
+ * select in a range with bitmask rejection.
+ *
+ * if range is empty, rmin is returned.
+ *
+ * the prng may advance by several states or none,
+ * depending on the range value.
+ */
+uint64
+pg_prng_u64_range(pg_prng_state *state, uint64 rmin, uint64 rmax)
+{
+	uint64 val;
+
+	if (likely(rmax > rmin))
+	{
+		uint64 range = rmax - rmin;
+
+		/* bit position is between 0 and 63, so rshift >= 0 */
+		uint32 rshift = 63 - pg_leftmost_one_pos64(range);
+
+		do
+		{
+			val = xoroshiro128ss(state) >> rshift;
+		}
+		while (unlikely(val > range));
+	}
+	else
+		val = 0;
+
+	return rmin + val;
+}
+
+/* i64 generator */
+int64
+pg_prng_i64(pg_prng_state *state)
+{
+	return (int64) xoroshiro128ss(state);
+}
+
+/* u32 generator */
+uint32
+pg_prng_u32(pg_prng_state *state)
+{
+	const uint64 v = xoroshiro128ss(state);
+	return (uint32) (((v >> 32) ^ v) & RIGHT_HALF_MASK);
+}
+
+/* i32 generator */
+int32
+pg_prng_i32(pg_prng_state *state)
+{
+	const uint64 v = xoroshiro128ss(state);
+	return (int32) (((v >> 32) ^ v) & RIGHT_HALF_MASK);
+}
+
+/* double generator */
+double
+pg_prng_f64(pg_prng_state *state)
+{
+	uint64 v = xoroshiro128ss(state);
+	return ldexp((double) (v & DMANTISSA_MASK), -52);
+}
+
+/* bool generator */
+bool
+pg_prng_bool(pg_prng_state *state)
+{
+	uint64 v = xoroshiro128ss(state);
+	return (v & FIRST_BIT_MASK) == FIRST_BIT_MASK;
+}
diff --git a/src/include/common/pg_prng.h b/src/include/common/pg_prng.h
new file mode 100644
index 0000000000..066ddffc1d
--- /dev/null
+++ b/src/include/common/pg_prng.h
@@ -0,0 +1,37 @@
+/*-------------------------------------------------------------------------
+ *
+ * PRNG: internal Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * src/include/common/pg_prng.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_PRNG_H
+#define PG_PRNG_H
+
+/* 128 bits state */
+typedef struct pg_prng_state {
+	uint64	s0, s1;
+} pg_prng_state;
+
+/* global pseudo-random state */
+extern PGDLLIMPORT pg_prng_state	pg_global_prng_state;
+
+/* use Donald Knuth's LCG constants for default state */
+#define PG_PRNG_DEFAULT_STATE \
+	{ UINT64CONST(0x5851F42D4C957F2D), UINT64CONST(0x14057B7EF767814F) }
+
+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_strong_seed(pg_prng_state *state);
+extern uint64 pg_prng_u64(pg_prng_state *state);
+extern uint64 pg_prng_u64_range(pg_prng_state *state, uint64 rmin, uint64 rmax);
+extern int64 pg_prng_i64(pg_prng_state *state);
+extern uint32 pg_prng_u32(pg_prng_state *state);
+extern int32 pg_prng_i32(pg_prng_state *state);
+extern double pg_prng_f64(pg_prng_state *state);
+extern bool pg_prng_bool(pg_prng_state *state);
+
+#endif							/* PG_PRNG_H */
diff --git a/src/include/optimizer/geqo.h b/src/include/optimizer/geqo.h
index 24dcdfb6cc..b28e4f084a 100644
--- a/src/include/optimizer/geqo.h
+++ b/src/include/optimizer/geqo.h
@@ -22,6 +22,7 @@
 #ifndef GEQO_H
 #define GEQO_H
 
+#include "common/pg_prng.h"
 #include "nodes/pathnodes.h"
 #include "optimizer/geqo_gene.h"
 
@@ -72,8 +73,8 @@ extern double Geqo_seed;		/* 0 .. 1 */
  */
 typedef struct
 {
-	List	   *initial_rels;	/* the base relations we are joining */
-	unsigned short random_state[3]; /* state for pg_erand48() */
+	List		   *initial_rels;	/* the base relations we are joining */
+	pg_prng_state	random_state;   /* PRNG state */
 } GeqoPrivateData;
 
 
diff --git a/src/include/port.h b/src/include/port.h
index 82f63de325..ef84d00ea0 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -356,11 +356,6 @@ extern int	gettimeofday(struct timeval *tp, struct timezone *tzp);
 #define pgoff_t off_t
 #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
 extern int	fls(int mask);
 #endif
@@ -446,10 +441,6 @@ extern size_t strlcpy(char *dst, const char *src, size_t siz);
 extern size_t strnlen(const char *str, size_t maxlen);
 #endif
 
-#if !defined(HAVE_RANDOM)
-extern long random(void);
-#endif
-
 #ifndef HAVE_SETENV
 extern int	setenv(const char *name, const char *value, int overwrite);
 #endif
@@ -458,10 +449,6 @@ extern int	setenv(const char *name, const char *value, int overwrite);
 extern int	unsetenv(const char *name);
 #endif
 
-#ifndef HAVE_SRANDOM
-extern void srandom(unsigned int seed);
-#endif
-
 #ifndef HAVE_DLOPEN
 extern void *dlopen(const char *file, int mode);
 extern void *dlsym(void *handle, const char *symbol);
diff --git a/src/include/utils/sampling.h b/src/include/utils/sampling.h
index a58d14281b..c0b1c6a645 100644
--- a/src/include/utils/sampling.h
+++ b/src/include/utils/sampling.h
@@ -13,15 +13,14 @@
 #ifndef SAMPLING_H
 #define SAMPLING_H
 
+#include "common/pg_prng.h"
 #include "storage/block.h"		/* for typedef BlockNumber */
 
 
 /* Random generator for sampling code */
-typedef unsigned short SamplerRandomState[3];
-
 extern void sampler_random_init_state(long seed,
-									  SamplerRandomState randstate);
-extern double sampler_random_fract(SamplerRandomState randstate);
+									  pg_prng_state *randstate);
+extern double sampler_random_fract(pg_prng_state *randstate);
 
 /* Block sampling methods */
 
@@ -32,7 +31,7 @@ typedef struct
 	int			n;				/* desired sample size */
 	BlockNumber t;				/* current block number */
 	int			m;				/* blocks selected so far */
-	SamplerRandomState randstate;	/* random generator state */
+	pg_prng_state randstate;	/* random generator state */
 } BlockSamplerData;
 
 typedef BlockSamplerData *BlockSampler;
@@ -46,8 +45,9 @@ extern BlockNumber BlockSampler_Next(BlockSampler bs);
 
 typedef struct
 {
-	double		W;
-	SamplerRandomState randstate;	/* random generator state */
+	double			W;
+	pg_prng_state	randstate;	/* random generator state */
+	bool			randstate_initialized;
 } ReservoirStateData;
 
 typedef ReservoirStateData *ReservoirState;
diff --git a/src/port/Makefile b/src/port/Makefile
index 52dbf5783f..b3754d8940 100644
--- a/src/port/Makefile
+++ b/src/port/Makefile
@@ -42,7 +42,6 @@ OBJS = \
 	$(PG_CRC32C_OBJS) \
 	bsearch_arg.o \
 	chklocale.o \
-	erand48.o \
 	inet_net_ntop.o \
 	noblock.o \
 	path.o \
diff --git a/src/port/erand48.c b/src/port/erand48.c
deleted file mode 100644
index a82ea94220..0000000000
--- a/src/port/erand48.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/port/random.c b/src/port/random.c
deleted file mode 100644
index 2dd59a0829..0000000000
--- a/src/port/random.c
+++ /dev/null
@@ -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();
-}
diff --git a/src/port/srandom.c b/src/port/srandom.c
deleted file mode 100644
index cf1007b2ef..0000000000
--- a/src/port/srandom.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/test/modules/test_bloomfilter/test_bloomfilter.c b/src/test/modules/test_bloomfilter/test_bloomfilter.c
index 96c5011428..69425bd6fc 100644
--- a/src/test/modules/test_bloomfilter/test_bloomfilter.c
+++ b/src/test/modules/test_bloomfilter/test_bloomfilter.c
@@ -13,6 +13,7 @@
 #include "postgres.h"
 
 #include "fmgr.h"
+#include "common/pg_prng.h"
 #include "lib/bloomfilter.h"
 #include "miscadmin.h"
 
@@ -85,7 +86,7 @@ create_and_test_bloom(int power, int64 nelements, int callerseed)
 	 * 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_i32(&pg_global_prng_state) % PG_INT32_MAX : callerseed;
 
 	/* Create Bloom filter, populate it, and report on false positive rate */
 	filter = bloom_create(nelements, bloom_work_mem, seed);
diff --git a/src/test/modules/test_integerset/test_integerset.c b/src/test/modules/test_integerset/test_integerset.c
index 21c6f49b37..3e8cb7a005 100644
--- a/src/test/modules/test_integerset/test_integerset.c
+++ b/src/test/modules/test_integerset/test_integerset.c
@@ -12,6 +12,7 @@
  */
 #include "postgres.h"
 
+#include "common/pg_prng.h"
 #include "fmgr.h"
 #include "lib/integerset.h"
 #include "miscadmin.h"
@@ -99,12 +100,16 @@ static void check_with_filler(IntegerSet *intset, uint64 x, uint64 value, uint64
 static void test_single_value_and_filler(uint64 value, uint64 filler_min, uint64 filler_max);
 static void test_huge_distances(void);
 
+static pg_prng_state	pr_state;
+
 /*
  * SQL-callable entry point to perform all tests.
  */
 Datum
 test_integerset(PG_FUNCTION_ARGS)
 {
+	pg_prng_strong_seed(&pr_state);
+
 	/* Tests for various corner cases */
 	test_empty();
 	test_huge_distances();
@@ -248,8 +253,7 @@ test_pattern(const test_spec *spec)
 		 * only a small part of the integer space is used.  We would very
 		 * rarely hit values that are actually in the set.
 		 */
-		x = (pg_lrand48() << 31) | pg_lrand48();
-		x = x % (last_int + 1000);
+		x = pg_prng_u64(&pr_state) % (last_int + 1000);
 
 		/* Do we expect this value to be present in the set? */
 		if (x >= last_int)
@@ -571,7 +575,7 @@ test_huge_distances(void)
 	 */
 	while (num_values < 1000)
 	{
-		val += pg_lrand48();
+		val += pg_prng_u32(&pr_state);
 		values[num_values++] = val;
 	}
 
diff --git a/src/test/modules/test_rbtree/test_rbtree.c b/src/test/modules/test_rbtree/test_rbtree.c
index 713ebd1b26..59a571185e 100644
--- a/src/test/modules/test_rbtree/test_rbtree.c
+++ b/src/test/modules/test_rbtree/test_rbtree.c
@@ -14,6 +14,7 @@
 #include "postgres.h"
 
 #include "fmgr.h"
+#include "common/pg_prng.h"
 #include "lib/rbtree.h"
 #include "utils/memutils.h"
 
@@ -108,7 +109,7 @@ GetPermutation(int size)
 	 */
 	for (i = 1; i < size; i++)
 	{
-		int			j = random() % (i + 1);
+		int			j = pg_prng_u32(&pg_global_prng_state) % (i + 1);
 
 		if (j < i)				/* avoid fetching undefined data if j=i */
 			permutation[i] = permutation[j];
@@ -320,7 +321,7 @@ testdelete(int size, int delsize)
 
 	for (i = 0; i < delsize; i++)
 	{
-		int			k = random() % size;
+		int			k = pg_prng_u32(&pg_global_prng_state) % size;
 
 		while (chosen[k])
 			k = (k + 1) % size;
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 4362bd44fd..aa048d949f 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -99,9 +99,9 @@ sub mkvcbuild
 	$solution = CreateSolution($vsVersion, $config);
 
 	our @pgportfiles = qw(
-	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c random.c
-	  srandom.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
+	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c
+	  getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
+	  snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
 	  dirent.c dlopen.c getopt.c getopt_long.c link.c
 	  pread.c preadv.c pwrite.c pwritev.c pg_bitutils.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
 	  f2s.c file_perm.c file_utils.c hashfn.c ip.c jsonapi.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
-	  saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c username.c
-	  wait_error.c wchar.c);
+	  pg_get_line.c pg_lzcompress.c pg_prng.c pgfnames.c psprintf.c relpath.c
+	  rmtree.c saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c
+	  username.c wait_error.c wchar.c);
 
 	if ($solution->{options}->{openssl})
 	{
diff --git a/src/tools/testint128.c b/src/tools/testint128.c
index 71c345969a..43d569aeb5 100644
--- a/src/tools/testint128.c
+++ b/src/tools/testint128.c
@@ -27,6 +27,7 @@
 #endif
 
 #include "common/int128.h"
+#include "common/pg_prng.h"
 
 /*
  * We assume the parts of this union are laid out compatibly.
@@ -61,27 +62,11 @@ my_int128_compare(int128 x, int128 y)
 	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.
  *
  * 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 fix the prng state (hmmm...).
  *
  * 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;
 
+	pg_prng_seed(&pg_global_prng_state, 0);
+
 	if (argc >= 2)
 		count = strtol(argv[1], NULL, 0);
 	else
@@ -97,9 +84,9 @@ main(int argc, char **argv)
 
 	while (count-- > 0)
 	{
-		int64		x = get_random_uint64();
-		int64		y = get_random_uint64();
-		int64		z = get_random_uint64();
+		int64		x = pg_prng_u64(&pg_global_prng_state);
+		int64		y = pg_prng_u64(&pg_global_prng_state);
+		int64		z = pg_prng_u64(&pg_global_prng_state);
 		test128		t1;
 		test128		t2;
 
#52Aleksander Alekseev
aleksander@timescale.com
In reply to: Fabien COELHO (#51)
1 attachment(s)
Re: rand48 replacement

Hi hackers,

I guess the declaration needs PGDLLIMPORT.

Indeed, thanks!

Attached v16 adds that.

It looks like the patch is in pretty good shape. I noticed that the
return value of pg_prng_strong_seed() is not checked in several
places, also there was a typo in pg_trgm.c. The corrected patch is
attached. Assuming the new version will not upset cfbot, I would call
the patch "Ready for Committer".

--
Best regards,
Aleksander Alekseev

Attachments:

prng-17.patchapplication/octet-stream; name=prng-17.patchDownload
diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c
index 42a830c33b..8f49bbc2a7 100644
--- a/contrib/amcheck/verify_nbtree.c
+++ b/contrib/amcheck/verify_nbtree.c
@@ -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"
@@ -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();
+		/* Random seed relies on backend prng initialization to avoid repetition */
+		seed = pg_prng_u64(&pg_global_prng_state);
 		/* Create Bloom filter to fingerprint index */
 		state->filter = bloom_create(total_elems, maintenance_work_mem, seed);
 		state->heaptuplespresent = 0;
diff --git a/contrib/auto_explain/auto_explain.c b/contrib/auto_explain/auto_explain.c
index e9092ba359..0c4279447c 100644
--- a/contrib/auto_explain/auto_explain.c
+++ b/contrib/auto_explain/auto_explain.c
@@ -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"
@@ -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_f64(&pg_global_prng_state) < auto_explain_sample_rate;
 		else
 			current_query_sampled = false;
 	}
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 2c2f149fb0..146b524076 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -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]);
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 45a09337d0..f767fdcc6a 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -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]);
 		}
diff --git a/contrib/tablefunc/tablefunc.c b/contrib/tablefunc/tablefunc.c
index 52b272f298..ac6db426ac 100644
--- a/contrib/tablefunc/tablefunc.c
+++ b/contrib/tablefunc/tablefunc.c
@@ -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"
@@ -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_f64(&pg_global_prng_state);
+		u2 = pg_prng_f64(&pg_global_prng_state);
 
 		v1 = (2.0 * u1) - 1.0;
 		v2 = (2.0 * u2) - 1.0;
diff --git a/contrib/tsm_system_rows/tsm_system_rows.c b/contrib/tsm_system_rows/tsm_system_rows.c
index 4996612902..1a46d4b143 100644
--- a/contrib/tsm_system_rows/tsm_system_rows.c
+++ b/contrib/tsm_system_rows/tsm_system_rows.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/contrib/tsm_system_time/tsm_system_time.c b/contrib/tsm_system_time/tsm_system_time.c
index 788d8f9a68..36acc6c106 100644
--- a/contrib/tsm_system_time/tsm_system_time.c
+++ b/contrib/tsm_system_time/tsm_system_time.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index 03191e016c..3c2f24b653 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -16,6 +16,7 @@
 
 #include "access/gin_private.h"
 #include "access/relscan.h"
+#include "common/pg_prng.h"
 #include "miscadmin.h"
 #include "storage/predicate.h"
 #include "utils/datum.h"
@@ -787,7 +788,7 @@ entryLoadMoreItems(GinState *ginstate, GinScanEntry entry,
 	}
 }
 
-#define gin_rand() (((double) random()) / ((double) MAX_RANDOM_VALUE))
+#define gin_rand() pg_prng_f64(&pg_global_prng_state)
 #define dropItem(e) ( gin_rand() > ((double)GinFuzzySearchLimit)/((double)((e)->predictNumberResult)) )
 
 /*
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 43ba03b6eb..94dbabc198 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -19,6 +19,7 @@
 #include "access/htup_details.h"
 #include "access/reloptions.h"
 #include "catalog/pg_opclass.h"
+#include "common/pg_prng.h"
 #include "storage/indexfsm.h"
 #include "storage/lmgr.h"
 #include "utils/float.h"
@@ -507,7 +508,7 @@ gistchoose(Relation r, Page p, IndexTuple it,	/* it has compressed entry */
 			if (keep_current_best == -1)
 			{
 				/* we didn't make the random choice yet for this old best */
-				keep_current_best = (random() <= (MAX_RANDOM_VALUE / 2)) ? 1 : 0;
+				keep_current_best = pg_prng_bool(&pg_global_prng_state) ? 1 : 0;
 			}
 			if (keep_current_best == 0)
 			{
@@ -529,7 +530,7 @@ gistchoose(Relation r, Page p, IndexTuple it,	/* it has compressed entry */
 			if (keep_current_best == -1)
 			{
 				/* we didn't make the random choice yet for this old best */
-				keep_current_best = (random() <= (MAX_RANDOM_VALUE / 2)) ? 1 : 0;
+				keep_current_best = pg_prng_bool(&pg_global_prng_state) ? 1 : 0;
 			}
 			if (keep_current_best == 1)
 				break;
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index 0fe8c70939..be20b7db4f 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -19,6 +19,7 @@
 #include "access/nbtxlog.h"
 #include "access/transam.h"
 #include "access/xloginsert.h"
+#include "common/pg_prng.h"
 #include "lib/qunique.h"
 #include "miscadmin.h"
 #include "storage/lmgr.h"
@@ -965,7 +966,7 @@ _bt_findinsertloc(Relation rel,
 
 			if (P_RIGHTMOST(opaque) ||
 				_bt_compare(rel, itup_key, page, P_HIKEY) != 0 ||
-				random() <= (MAX_RANDOM_VALUE / 100))
+				pg_prng_f64(&pg_global_prng_state) <= 0.01)
 				break;
 
 			_bt_stepright(rel, insertstate, stack);
diff --git a/src/backend/access/spgist/spgdoinsert.c b/src/backend/access/spgist/spgdoinsert.c
index 70557bcf3d..5389c93366 100644
--- a/src/backend/access/spgist/spgdoinsert.c
+++ b/src/backend/access/spgist/spgdoinsert.c
@@ -19,6 +19,7 @@
 #include "access/spgist_private.h"
 #include "access/spgxlog.h"
 #include "access/xloginsert.h"
+#include "common/pg_prng.h"
 #include "miscadmin.h"
 #include "storage/bufmgr.h"
 #include "utils/rel.h"
@@ -2210,7 +2211,7 @@ 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_u32(&pg_global_prng_state) % innerTuple->nNodes;
 			}
 
 			switch (out.resultType)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 8e35c432f5..e3ce229bfb 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -37,6 +37,7 @@
 #include "commands/async.h"
 #include "commands/tablecmds.h"
 #include "commands/trigger.h"
+#include "common/pg_prng.h"
 #include "executor/spi.h"
 #include "libpq/be-fsstubs.h"
 #include "libpq/pqsignal.h"
@@ -1990,7 +1991,7 @@ StartTransaction(void)
 	/* Determine if statements are logged in this transaction */
 	xact_is_sampled = log_xact_sample_rate != 0 &&
 		(log_xact_sample_rate == 1 ||
-		 random() <= log_xact_sample_rate * MAX_RANDOM_VALUE);
+		 pg_prng_f64(&pg_global_prng_state) <= log_xact_sample_rate);
 
 	/*
 	 * initialize current transaction state fields
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 4928702aec..e6d7e7e70f 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -38,6 +38,7 @@
 #include "commands/progress.h"
 #include "commands/tablecmds.h"
 #include "commands/vacuum.h"
+#include "common/pg_prng.h"
 #include "executor/executor.h"
 #include "foreign/fdwapi.h"
 #include "miscadmin.h"
@@ -1162,7 +1163,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 	OldestXmin = GetOldestNonRemovableTransactionId(onerel);
 
 	/* Prepare for sampling block numbers */
-	randseed = random();
+	randseed = pg_prng_i32(&pg_global_prng_state);
 	nblocks = BlockSampler_Init(&bs, totalblocks, targrows, randseed);
 
 #ifdef USE_PREFETCH
@@ -1279,7 +1280,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 					 * Found a suitable tuple, so save it, replacing one old
 					 * tuple at random
 					 */
-					int			k = (int) (targrows * sampler_random_fract(rstate.randstate));
+					int			k = (int) (targrows * sampler_random_fract(&rstate.randstate));
 
 					Assert(k >= 0 && k < targrows);
 					heap_freetuple(rows[k]);
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 44232d50d0..09c34e96fa 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -17,6 +17,7 @@
 #include "access/relscan.h"
 #include "access/tableam.h"
 #include "access/tsmapi.h"
+#include "common/pg_prng.h"
 #include "executor/executor.h"
 #include "executor/nodeSamplescan.h"
 #include "miscadmin.h"
@@ -154,7 +155,7 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	 * do this just once, since the seed shouldn't change over rescans.
 	 */
 	if (tsc->repeatable == NULL)
-		scanstate->seed = random();
+		scanstate->seed = pg_prng_i32(&pg_global_prng_state);
 
 	/*
 	 * Finally, initialize the TABLESAMPLE method handler.
diff --git a/src/backend/optimizer/geqo/geqo_random.c b/src/backend/optimizer/geqo/geqo_random.c
index f21bc047e6..8b42a9ffba 100644
--- a/src/backend/optimizer/geqo/geqo_random.c
+++ b/src/backend/optimizer/geqo/geqo_random.c
@@ -21,14 +21,7 @@ geqo_set_seed(PlannerInfo *root, double seed)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	/*
-	 * XXX. This seeding algorithm could certainly be improved - but it is not
-	 * critical to do so.
-	 */
-	memset(private->random_state, 0, sizeof(private->random_state));
-	memcpy(private->random_state,
-		   &seed,
-		   Min(sizeof(private->random_state), sizeof(seed)));
+	pg_prng_fseed(&private->random_state, seed);
 }
 
 double
@@ -36,5 +29,5 @@ geqo_rand(PlannerInfo *root)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	return pg_erand48(private->random_state);
+	return pg_prng_f64(&private->random_state);
 }
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index db797c040b..144fd17150 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -98,6 +98,7 @@
 #include "catalog/pg_control.h"
 #include "common/file_perm.h"
 #include "common/ip.h"
+#include "common/pg_prng.h"
 #include "common/string.h"
 #include "lib/ilist.h"
 #include "libpq/auth.h"
@@ -2699,19 +2700,22 @@ ClosePostmasterPorts(bool am_syslogger)
 void
 InitProcessGlobals(void)
 {
-	unsigned int rseed;
-
 	MyProcPid = getpid();
 	MyStartTimestamp = GetCurrentTimestamp();
 	MyStartTime = timestamptz_to_time_t(MyStartTimestamp);
 
 	/*
-	 * Set a different seed for random() in every process.  We want something
+	 * Set a different global seed in every process.  We want something
 	 * unpredictable, so if possible, use high-quality random bits for the
 	 * seed.  Otherwise, fall back to a seed based on timestamp and PID.
 	 */
-	if (!pg_strong_random(&rseed, sizeof(rseed)))
+	if (unlikely(!pg_prng_strong_seed(&pg_global_prng_state)))
 	{
+		uint64	rseed;
+
+		ereport(WARNING,
+				(errmsg_internal("pg_prng_strong_seed() failed, falling back to weaker seeding")));
+
 		/*
 		 * Since PIDs and timestamps tend to change more frequently in their
 		 * least significant bits, shift the timestamp left to allow a larger
@@ -2722,8 +2726,13 @@ InitProcessGlobals(void)
 		rseed = ((uint64) MyProcPid) ^
 			((uint64) MyStartTimestamp << 12) ^
 			((uint64) MyStartTimestamp >> 20);
+
+		pg_prng_seed(&pg_global_prng_state, rseed);
 	}
-	srandom(rseed);
+
+#ifdef HAVE_SRANDOM
+	srandom(pg_prng_i32(&pg_global_prng_state));
+#endif
 }
 
 
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index cb1a8dd34f..5f15454fbe 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -92,6 +92,7 @@
 #include "catalog/pg_tablespace.h"
 #include "common/file_perm.h"
 #include "common/file_utils.h"
+#include "common/pg_prng.h"
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "port/pg_iovec.h"
@@ -2939,7 +2940,7 @@ SetTempTablespaces(Oid *tableSpaces, int numSpaces)
 	 * available tablespaces.
 	 */
 	if (numSpaces > 1)
-		nextTempTableSpace = random() % numSpaces;
+		nextTempTableSpace = pg_prng_u32(&pg_global_prng_state) % numSpaces;
 	else
 		nextTempTableSpace = 0;
 }
diff --git a/src/backend/storage/ipc/dsm.c b/src/backend/storage/ipc/dsm.c
index b461a5f7e9..e97844614e 100644
--- a/src/backend/storage/ipc/dsm.c
+++ b/src/backend/storage/ipc/dsm.c
@@ -33,6 +33,7 @@
 #endif
 #include <sys/stat.h>
 
+#include "common/pg_prng.h"
 #include "lib/ilist.h"
 #include "miscadmin.h"
 #include "port/pg_bitutils.h"
@@ -180,7 +181,7 @@ dsm_postmaster_startup(PGShmemHeader *shim)
 	{
 		Assert(dsm_control_address == NULL);
 		Assert(dsm_control_mapped_size == 0);
-		dsm_control_handle = random() << 1; /* Even numbers only */
+		dsm_control_handle = pg_prng_u32(&pg_global_prng_state) << 1; /* Even numbers only */
 		if (dsm_control_handle == DSM_HANDLE_INVALID)
 			continue;
 		if (dsm_impl_op(DSM_OP_CREATE, dsm_control_handle, segsize,
@@ -536,7 +537,7 @@ dsm_create(Size size, int flags)
 		for (;;)
 		{
 			Assert(seg->mapped_address == NULL && seg->mapped_size == 0);
-			seg->handle = random() << 1;	/* Even numbers only */
+			seg->handle = pg_prng_u32(&pg_global_prng_state) << 1;	/* Even numbers only */
 			if (seg->handle == DSM_HANDLE_INVALID)	/* Reserve sentinel */
 				continue;
 			if (dsm_impl_op(DSM_OP_CREATE, seg->handle, size, &seg->impl_private,
@@ -1237,7 +1238,7 @@ make_main_region_dsm_handle(int slot)
 	 */
 	handle = 1;
 	handle |= slot << 1;
-	handle |= random() << (pg_leftmost_one_pos32(dsm_control->maxitems) + 1);
+	handle |= pg_prng_i32(&pg_global_prng_state) << (pg_leftmost_one_pos32(dsm_control->maxitems) + 1);
 	return handle;
 }
 
diff --git a/src/backend/storage/lmgr/s_lock.c b/src/backend/storage/lmgr/s_lock.c
index 91322a40c1..74c6f251ce 100644
--- a/src/backend/storage/lmgr/s_lock.c
+++ b/src/backend/storage/lmgr/s_lock.c
@@ -50,6 +50,7 @@
 #include <time.h>
 #include <unistd.h>
 
+#include "common/pg_prng.h"
 #include "port/atomics.h"
 #include "storage/s_lock.h"
 
@@ -63,6 +64,7 @@
 slock_t		dummy_spinlock;
 
 static int	spins_per_delay = DEFAULT_SPINS_PER_DELAY;
+static pg_prng_state	prng_state;
 
 
 /*
@@ -143,8 +145,7 @@ perform_spin_delay(SpinDelayStatus *status)
 #endif
 
 		/* increase delay by a random fraction between 1X and 2X */
-		status->cur_delay += (int) (status->cur_delay *
-									((double) random() / (double) MAX_RANDOM_VALUE) + 0.5);
+		status->cur_delay += (int) (status->cur_delay * (pg_prng_f64(&prng_state) + 0.5));
 		/* wrap back to minimum delay when max is exceeded */
 		if (status->cur_delay > MAX_DELAY_USEC)
 			status->cur_delay = MIN_DELAY_USEC;
@@ -303,7 +304,7 @@ volatile struct test_lock_struct test_lock;
 int
 main()
 {
-	srandom((unsigned int) time(NULL));
+	pg_prng_seed(&prng_state, (unsigned int) time(NULL));
 
 	test_lock.pad1 = test_lock.pad2 = 0x44;
 
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 0775abe35d..ef6c27dade 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -42,6 +42,7 @@
 #include "catalog/pg_type.h"
 #include "commands/async.h"
 #include "commands/prepare.h"
+#include "common/pg_prng.h"
 #include "executor/spi.h"
 #include "jit/jit.h"
 #include "libpq/libpq.h"
@@ -2355,13 +2356,13 @@ check_log_duration(char *msec_str, bool was_logged)
 
 		/*
 		 * Do not log if log_statement_sample_rate = 0. Log a sample if
-		 * log_statement_sample_rate <= 1 and avoid unnecessary random() call
+		 * log_statement_sample_rate <= 1 and avoid unnecessary random call
 		 * if log_statement_sample_rate = 1.
 		 */
 		if (exceeded_sample_duration)
 			in_sample = log_statement_sample_rate != 0 &&
 				(log_statement_sample_rate == 1 ||
-				 random() <= log_statement_sample_rate * MAX_RANDOM_VALUE);
+				 pg_prng_f64(&pg_global_prng_state) <= log_statement_sample_rate);
 
 		if (exceeded_duration || in_sample || log_duration || xact_is_sampled)
 		{
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 098bbb372b..9fc0b108dd 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -21,6 +21,7 @@
 
 #include "catalog/pg_type.h"
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/shortest_dec.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
@@ -65,7 +66,7 @@ float8		degree_c_one = 1.0;
 
 /* State for drandom() and setseed() */
 static bool drandom_seed_set = false;
-static unsigned short drandom_seed[3] = {0, 0, 0};
+static pg_prng_state drandom_seed = PG_PRNG_DEFAULT_STATE;
 
 /* Local function prototypes */
 static double sind_q1(double x);
@@ -2762,22 +2763,20 @@ drandom(PG_FUNCTION_ARGS)
 		 * Should that fail for some reason, we fall back on a lower-quality
 		 * seed based on current time and PID.
 		 */
-		if (!pg_strong_random(drandom_seed, sizeof(drandom_seed)))
+		if (!pg_strong_random(&drandom_seed, sizeof(drandom_seed)))
 		{
 			TimestampTz now = GetCurrentTimestamp();
 			uint64		iseed;
 
 			/* Mix the PID with the most predictable bits of the timestamp */
 			iseed = (uint64) now ^ ((uint64) MyProcPid << 32);
-			drandom_seed[0] = (unsigned short) iseed;
-			drandom_seed[1] = (unsigned short) (iseed >> 16);
-			drandom_seed[2] = (unsigned short) (iseed >> 32);
+			pg_prng_seed(&drandom_seed, iseed);
 		}
 		drandom_seed_set = true;
 	}
 
-	/* pg_erand48 produces desired result range [0.0 - 1.0) */
-	result = pg_erand48(drandom_seed);
+	/* produces desired result range [0.0 - 1.0) */
+	result = pg_prng_f64(&drandom_seed);
 
 	PG_RETURN_FLOAT8(result);
 }
@@ -2790,7 +2789,6 @@ Datum
 setseed(PG_FUNCTION_ARGS)
 {
 	float8		seed = PG_GETARG_FLOAT8(0);
-	uint64		iseed;
 
 	if (seed < -1 || seed > 1 || isnan(seed))
 		ereport(ERROR,
@@ -2798,11 +2796,7 @@ setseed(PG_FUNCTION_ARGS)
 				 errmsg("setseed parameter %g is out of allowed range [-1,1]",
 						seed)));
 
-	/* Use sign bit + 47 fractional bits to fill drandom_seed[] */
-	iseed = (int64) (seed * (float8) UINT64CONST(0x7FFFFFFFFFFF));
-	drandom_seed[0] = (unsigned short) iseed;
-	drandom_seed[1] = (unsigned short) (iseed >> 16);
-	drandom_seed[2] = (unsigned short) (iseed >> 32);
+	pg_prng_fseed(&drandom_seed, seed);
 	drandom_seed_set = true;
 
 	PG_RETURN_VOID();
diff --git a/src/backend/utils/misc/sampling.c b/src/backend/utils/misc/sampling.c
index 0c327e823f..f1c86150d8 100644
--- a/src/backend/utils/misc/sampling.c
+++ b/src/backend/utils/misc/sampling.c
@@ -49,7 +49,7 @@ BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize,
 	bs->t = 0;					/* blocks scanned so far */
 	bs->m = 0;					/* blocks selected so far */
 
-	sampler_random_init_state(randseed, bs->randstate);
+	sampler_random_init_state(randseed, &bs->randstate);
 
 	return Min(bs->n, bs->N);
 }
@@ -98,7 +98,7 @@ BlockSampler_Next(BlockSampler bs)
 	 * less than k, which means that we cannot fail to select enough blocks.
 	 *----------
 	 */
-	V = sampler_random_fract(bs->randstate);
+	V = sampler_random_fract(&bs->randstate);
 	p = 1.0 - (double) k / (double) K;
 	while (V < p)
 	{
@@ -136,10 +136,10 @@ reservoir_init_selection_state(ReservoirState rs, int n)
 	 * Reservoir sampling is not used anywhere where it would need to return
 	 * repeatable results so we can initialize it randomly.
 	 */
-	sampler_random_init_state(random(), rs->randstate);
+	sampler_random_init_state(pg_prng_u32(&pg_global_prng_state), &rs->randstate);
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	rs->W = exp(-log(sampler_random_fract(rs->randstate)) / n);
+	rs->W = exp(-log(sampler_random_fract(&rs->randstate)) / n);
 }
 
 double
@@ -154,7 +154,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 		double		V,
 					quot;
 
-		V = sampler_random_fract(rs->randstate);	/* Generate V */
+		V = sampler_random_fract(&rs->randstate);	/* Generate V */
 		S = 0;
 		t += 1;
 		/* Note: "num" in Vitter's code is always equal to t - n */
@@ -186,7 +186,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 						tmp;
 
 			/* Generate U and X */
-			U = sampler_random_fract(rs->randstate);
+			U = sampler_random_fract(&rs->randstate);
 			X = t * (W - 1.0);
 			S = floor(X);		/* S is tentatively set to floor(X) */
 			/* Test if U <= h(S)/cg(X) in the manner of (6.3) */
@@ -215,7 +215,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 				y *= numer / denom;
 				denom -= 1;
 			}
-			W = exp(-log(sampler_random_fract(rs->randstate)) / n); /* Generate W in advance */
+			W = exp(-log(sampler_random_fract(&rs->randstate)) / n); /* Generate W in advance */
 			if (exp(log(y) / n) <= (t + X) / t)
 				break;
 		}
@@ -230,24 +230,22 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
  *----------
  */
 void
-sampler_random_init_state(long seed, SamplerRandomState randstate)
+sampler_random_init_state(long seed, pg_prng_state *randstate)
 {
-	randstate[0] = 0x330e;		/* same as pg_erand48, but could be anything */
-	randstate[1] = (unsigned short) seed;
-	randstate[2] = (unsigned short) (seed >> 16);
+	pg_prng_seed(randstate, (uint64_t) seed);
 }
 
 /* Select a random value R uniformly distributed in (0 - 1) */
 double
-sampler_random_fract(SamplerRandomState randstate)
+sampler_random_fract(pg_prng_state *randstate)
 {
 	double		res;
 
-	/* pg_erand48 returns a value in [0.0 - 1.0), so we must reject 0 */
+	/* pg_prng_f64 returned value in [0.0 - 1.0), so we must reject 0.0 */
 	do
 	{
-		res = pg_erand48(randstate);
-	} while (res == 0.0);
+		res = pg_prng_f64(randstate);
+	} while (unlikely(res == 0.0));
 	return res;
 }
 
@@ -266,22 +264,28 @@ double
 anl_random_fract(void)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(pg_prng_u32(&pg_global_prng_state), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* and compute a random fraction */
-	return sampler_random_fract(oldrs.randstate);
+	return sampler_random_fract(&oldrs.randstate);
 }
 
 double
 anl_init_selection_state(int n)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(pg_prng_u32(&pg_global_prng_state), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	return exp(-log(sampler_random_fract(oldrs.randstate)) / n);
+	return exp(-log(sampler_random_fract(&oldrs.randstate)) / n);
 }
 
 double
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 3c61c789e4..270053997f 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -66,6 +66,7 @@
 #include "common/file_perm.h"
 #include "common/file_utils.h"
 #include "common/logging.h"
+#include "common/pg_prng.h"
 #include "common/restricted_token.h"
 #include "common/string.h"
 #include "common/username.h"
@@ -880,9 +881,10 @@ choose_dsm_implementation(void)
 {
 #ifdef HAVE_SHM_OPEN
 	int			ntries = 10;
+	pg_prng_state	prng_state;
 
-	/* Initialize random(); this function is its only user in this program. */
-	srandom((unsigned int) (getpid() ^ time(NULL)));
+	/* Initialize pgng state this function is its only user in this program. */
+	pg_prng_seed(&prng_state, ((unsigned int) (getpid() ^ time(NULL))));
 
 	while (ntries > 0)
 	{
@@ -890,7 +892,7 @@ choose_dsm_implementation(void)
 		char		name[64];
 		int			fd;
 
-		handle = random();
+		handle = pg_prng_u32(&prng_state);
 		snprintf(name, 64, "/PostgreSQL.%u", handle);
 		if ((fd = shm_open(name, O_CREAT | O_RDWR | O_EXCL, 0600)) != -1)
 		{
diff --git a/src/bin/pg_test_fsync/pg_test_fsync.c b/src/bin/pg_test_fsync/pg_test_fsync.c
index fef31844fa..e52c5a1394 100644
--- a/src/bin/pg_test_fsync/pg_test_fsync.c
+++ b/src/bin/pg_test_fsync/pg_test_fsync.c
@@ -15,6 +15,7 @@
 
 #include "access/xlogdefs.h"
 #include "common/logging.h"
+#include "common/pg_prng.h"
 #include "getopt_long.h"
 
 /*
@@ -71,6 +72,7 @@ static char full_buf[DEFAULT_XLOG_SEG_SIZE],
 static struct timeval start_t,
 			stop_t;
 static bool alarm_triggered = false;
+static pg_prng_state	prng_state;
 
 
 static void handle_args(int argc, char *argv[]);
@@ -117,6 +119,12 @@ main(int argc, char *argv[])
 	pqsignal(SIGHUP, signal_cleanup);
 #endif
 
+	if(unlikely(!pg_prng_strong_seed(&prng_state)))
+	{
+		pg_log_error("pg_prng_strong_seed() failed");
+		exit(1);
+	}
+
 	prepare_buf();
 
 	test_open();
@@ -233,7 +241,7 @@ prepare_buf(void)
 
 	/* write random data into buffer */
 	for (ops = 0; ops < DEFAULT_XLOG_SEG_SIZE; ops++)
-		full_buf[ops] = random();
+		full_buf[ops] = pg_prng_u32(&prng_state);
 
 	buf = (char *) TYPEALIGN(XLOG_BLCKSZ, full_buf);
 }
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index c12b6f0615..21d386a827 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -58,6 +58,7 @@
 #endif
 
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/logging.h"
 #include "common/string.h"
 #include "common/username.h"
@@ -350,16 +351,8 @@ typedef struct StatsData
  */
 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. */
-static RandomState base_random_sequence;
+static pg_prng_state	base_random_sequence;
 
 /* Synchronization barrier for start and connection */
 static THREAD_BARRIER_T barrier;
@@ -461,7 +454,7 @@ typedef struct
 	 * Separate randomness for each client. This is used for random functions
 	 * 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			command;		/* command number in script */
@@ -498,9 +491,9 @@ typedef struct
 	 * random state to make all of them independent of each other and
 	 * therefore deterministic at the thread level.
 	 */
-	RandomState ts_choose_rs;	/* random state for selecting a script */
-	RandomState ts_throttle_rs; /* random state for transaction throttling */
-	RandomState ts_sample_rs;	/* random state for log sampling */
+	pg_prng_state	ts_choose_rs;	/* random state for selecting a script */
+	pg_prng_state	ts_throttle_rs; /* random state for transaction throttling */
+	pg_prng_state	ts_sample_rs;	/* random state for log sampling */
 
 	int64		throttle_trigger;	/* previous/next throttling (us) */
 	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.
  */
 static void
-initRandomState(RandomState *random_state)
-{
-	random_state->xseed[0] = (unsigned short)
-		(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);
+initRandomState(pg_prng_state *state)
+{
+	pg_prng_seed(state, pg_prng_u64(&base_random_sequence));
 }
 
+
 /*
- * Random number generator: uniform distribution from min to max inclusive.
+ * PRNG: uniform distribution from min to max inclusive.
  *
  * Although the limits are expressed as int64, you can't generate the full
  * int64 range in one call, because the difference of the limits mustn't
- * overflow int64.  In practice it's unwise to ask for more than an int32
- * range, because of the limited precision of pg_erand48().
+ * overflow int64. This is not checked.
  */
 static int64
-getrand(RandomState *random_state, int64 min, int64 max)
+getrand(pg_prng_state *state, int64 min, int64 max)
 {
-	/*
-	 * 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));
+	return min + (int64) pg_prng_u64_range(state, 0, max - min);
 }
 
 /*
@@ -942,8 +921,7 @@ getrand(RandomState *random_state, int64 min, int64 max)
  * value is exp(-parameter).
  */
 static int64
-getExponentialRand(RandomState *random_state, int64 min, int64 max,
-				   double parameter)
+getExponentialRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		cut,
 				uniform,
@@ -953,7 +931,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 	Assert(parameter > 0.0);
 	cut = exp(-parameter);
 	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	uniform = 1.0 - pg_prng_f64(state);
 
 	/*
 	 * inner expression in (cut, 1] (if parameter > 0), rand in [0, 1)
@@ -966,8 +944,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 
 /* random number generator: gaussian distribution from min to max inclusive */
 static int64
-getGaussianRand(RandomState *random_state, int64 min, int64 max,
-				double parameter)
+getGaussianRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		stdev;
 	double		rand;
@@ -990,13 +967,13 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
 	do
 	{
 		/*
-		 * pg_erand48 generates [0,1), but for the basic version of the
+		 * pg_prng_f64 generates [0,1), but for the basic version of the
 		 * Box-Muller transform the two uniformly distributed random numbers
 		 * are expected in (0, 1] (see
 		 * https://en.wikipedia.org/wiki/Box-Muller_transform)
 		 */
-		double		rand1 = 1.0 - pg_erand48(random_state->xseed);
-		double		rand2 = 1.0 - pg_erand48(random_state->xseed);
+		double		rand1 = 1.0 - pg_prng_f64(state);
+		double		rand2 = 1.0 - pg_prng_f64(state);
 
 		/* Box-Muller basic form transform */
 		double		var_sqrt = sqrt(-2.0 * log(rand1));
@@ -1026,7 +1003,7 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
  * not be one.
  */
 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
@@ -1034,8 +1011,8 @@ getPoissonRand(RandomState *random_state, double center)
 	 */
 	double		uniform;
 
-	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	/* pseudo-random value in [0, 1), uniform in (0, 1] */
+	uniform = 1.0 - pg_prng_f64(state);
 
 	return (int64) (-log(uniform) * center + 0.5);
 }
@@ -1048,7 +1025,7 @@ getPoissonRand(RandomState *random_state, double center)
  * This works for s > 1.0, but may perform badly for s very close to 1.0.
  */
 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		x,
@@ -1063,8 +1040,8 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 	while (true)
 	{
 		/* random variates */
-		u = pg_erand48(random_state->xseed);
-		v = pg_erand48(random_state->xseed);
+		u = pg_prng_f64(state);
+		v = pg_prng_f64(state);
 
 		x = floor(pow(u, -1.0 / (s - 1.0)));
 
@@ -1078,14 +1055,14 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 
 /* random number generator: zipfian distribution from min to max inclusive */
 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;
 
 	/* abort if parameter is invalid */
 	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 +1119,7 @@ getHashMurmur2(int64 val, uint64 seed)
  * For small sizes, this generates each of the (size!) possible permutations
  * of integers in the range [0, size) with roughly equal probability.  Once
  * the size is larger than 20, the number of possible permutations exceeds the
- * number of distinct states of the internal pseudorandom number generators,
+ * number of distinct states of the internal pseudorandom number generator,
  * and so not all possible permutations can be generated, but the permutations
  * chosen should continue to give the appearance of being random.
  *
@@ -1152,25 +1129,19 @@ getHashMurmur2(int64 val, uint64 seed)
 static int64
 permute(const int64 val, const int64 isize, const int64 seed)
 {
-	RandomState random_state1;
-	RandomState random_state2;
-	uint64		size;
-	uint64		v;
-	int			masklen;
-	uint64		mask;
-	int			i;
+	/* using a high-end PRNG is probably overkill */
+	pg_prng_state	state;
+	uint64			size;
+	uint64			v;
+	int				masklen;
+	uint64			mask;
+	int				i;
 
 	if (isize < 2)
 		return 0;				/* nothing to permute */
 
-	/* Initialize a pair of random states using the seed */
-	random_state1.xseed[0] = seed & 0xFFFF;
-	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;
+	/* Initialize prng state using the seed */
+	pg_prng_seed(&state, (uint64) seed);
 
 	/* Computations are performed on unsigned values */
 	size = (uint64) isize;
@@ -1216,8 +1187,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 					t;
 
 		/* Random multiply (by an odd number), XOR and rotate of lower half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		if (v <= mask)
 		{
 			v = ((v * m) ^ r) & mask;
@@ -1225,8 +1196,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random multiply (by an odd number), XOR and rotate of upper half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_u64(&state) & mask) | 1;
+		r = pg_prng_u64(&state) & mask;
 		t = size - 1 - v;
 		if (t <= mask)
 		{
@@ -1236,7 +1207,7 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random offset */
-		r = (uint64) getrand(&random_state2, 0, size - 1);
+		r = pg_prng_u64(&state) % (size - 1);
 		v = (v + r) % size;
 	}
 
@@ -3831,7 +3802,7 @@ doLog(TState *thread, CState *st,
 	 * to the random sample.
 	 */
 	if (sample_rate != 0.0 &&
-		pg_erand48(thread->ts_sample_rs.xseed) > sample_rate)
+		pg_prng_f64(&thread->ts_sample_rs) > sample_rate)
 		return;
 
 	/* should we aggregate the results or not? */
@@ -5770,12 +5741,11 @@ set_random_seed(const char *seed)
 
 	if (seed != NULL)
 		pg_log_info("setting random seed to %llu", (unsigned long long) iseed);
+
 	random_seed = iseed;
 
 	/* Fill base_random_sequence with low-order bits of seed */
-	base_random_sequence.xseed[0] = iseed & 0xFFFF;
-	base_random_sequence.xseed[1] = (iseed >> 16) & 0xFFFF;
-	base_random_sequence.xseed[2] = (iseed >> 32) & 0xFFFF;
+	pg_prng_seed(&base_random_sequence, (uint64_t) iseed);
 
 	return true;
 }
@@ -6449,9 +6419,7 @@ main(int argc, char **argv)
 	/* set default seed for hash functions */
 	if (lookupVariable(&state[0], "default_seed") == NULL)
 	{
-		uint64		seed =
-		((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) |
-		(((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) << 32);
+		uint64		seed = pg_prng_u64(&base_random_sequence);
 
 		for (i = 0; i < nclients; i++)
 			if (!putVariableInt(&state[i], "startup", "default_seed", (int64) seed))
diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl
index 69ffa595dd..feec9a377b 100644
--- a/src/bin/pgbench/t/001_pgbench_with_server.pl
+++ b/src/bin/pgbench/t/001_pgbench_with_server.pl
@@ -379,9 +379,9 @@ $node->pgbench(
 
 		# After explicit seeding, the four random checks (1-3,20) are
 		# deterministic
-		qr{command=1.: int 13\b},      # uniform random
-		qr{command=2.: int 116\b},     # exponential random
-		qr{command=3.: int 1498\b},    # gaussian random
+		qr{command=1.: int 17\b},      # uniform random
+		qr{command=2.: int 108\b},     # exponential random
+		qr{command=3.: int 1452\b},    # gaussian random
 		qr{command=4.: int 4\b},
 		qr{command=5.: int 5\b},
 		qr{command=6.: int 6\b},
@@ -448,6 +448,7 @@ $node->pgbench(
 		qr{command=109.: boolean true\b},
 		qr{command=110.: boolean true\b},
 		qr{command=111.: boolean true\b},
+		qr{command=113.: boolean true\b},
 	],
 	'pgbench expressions',
 	{
@@ -591,8 +592,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))
 -- actual values
 \set t debug(permute(:v, 1) = 0)
-\set t debug(permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1 and \
-             permute(0, 2, 5435) = 1 and permute(1, 2, 5435) = 0)
+\set t debug(permute(0, 2, 5431) = 1 and permute(1, 2, 5431) = 0 and \
+             permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1)
+-- 63 bits tests for checking permute portability across architectures
+\set size debug(:max - 10)
+\set t debug(permute(:size-1, :size, 5432) = 4352241160009873020 and \
+             permute(:size-2, :size, 5432) = 4990004119691638710 and \
+             permute(:size-3, :size, 5432) = 5868029867256127549 and \
+             permute(:size-4, :size, 5432) = 1238830324886341378 and \
+             permute(:size-5, :size, 5432) = 7415914367102906897 and \
+             permute(:size-6, :size, 5432) = 3214501037984818995 and \
+             permute(:size-7, :size, 5432) =  600660351261124695)
 }
 	});
 
diff --git a/src/common/Makefile b/src/common/Makefile
index 880722fcf5..31c0dd366d 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -66,6 +66,7 @@ OBJS_COMMON = \
 	md5_common.o \
 	pg_get_line.o \
 	pg_lzcompress.o \
+	pg_prng.o \
 	pgfnames.o \
 	psprintf.o \
 	relpath.o \
diff --git a/src/common/pg_prng.c b/src/common/pg_prng.c
new file mode 100644
index 0000000000..a493252e5e
--- /dev/null
+++ b/src/common/pg_prng.c
@@ -0,0 +1,244 @@
+/*
+ * Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * Previous version was the 1988 (?) rand48 standard with 48 bits state LCG,
+ * designed for running on 32 bits or even 16 bits architectures, and to produce
+ * 32 bit ints and floats (i.e. *not* doubles).
+ *
+ * The implied 16-bits unpacking/packing may have a detrimental performance impact
+ * on modern 64 bits architectures.
+ *
+ * We (probably) want:
+ * - one reasonable default PRNG for all pg internal uses.
+ * - NOT to invent a new design!
+ * - something fast, close to rand48 (which basically does 2 arithmetic ops)
+ *   no need for something cryptographic though, which would imply slow
+ * - to produce 64 bits integers & doubles with a 52 bits mantissa
+ *   so state size > 64 bits.
+ * - a small state though, because we might generate quite a few of them
+ *   for different purposes so state size <= 256 or even <= 128 bits
+ * - the state to be aligned to whatever, so state size = 128 bits
+ * - 64 bits operations for efficiency on modern architectures,
+ *   but NOT 128 bits operations.
+ * - not to depend on special hardware for speed (eg MMX/SSE/AES).
+ * - not something with obvious known and relevant defects.
+ * - not something with "rights" attached.
+ *
+ * These constraints reduce a lot the available options from
+ * https://en.wikipedia.org/wiki/List_of_random_number_generators
+ *
+ * LCG
+ * - POSIX rand48: m = 2^48, a = 25214903917, c = 11, extract 47...16
+ * - MMIX by Donald Knuth: m = 2^64, a = 6364136223846793005, c = 1442695040888963407
+ * - very bad on low bits, which MUST NOT BE USED
+ *   a 128 bits would do, but no clear standards, and it would imply 128-bit ops,
+ *   see PCG below.
+ *
+ * PCG Family (https://www.pcg-random.org/)
+ * - this is basically an LCG with an improved output function
+ *   so as to avoid typical LCG issues and pass stats tests.
+ * - recommends state size to be twice the output size,
+ *   which seems a reasonable requirement.
+ * - this implies 128 bit ops for our target parameters,
+ *   otherwise it is a fairly good candidate for simplicity,
+ *   efficiency and quality.
+ *
+ * Xor-Shift-Rotate PRNGs:
+ * - a generic annoying feature of this generator class is that there is one
+ *   special all-zero state which must not be used, inducing a constraint on
+ *   initialization, and when the generator get close to it (i.e. many zeros
+ *   in the state) it takes some iterations to recover, with small lumps
+ *   of close/related/bad numbers generated in the process.
+ * Xoroshiro128+
+ * - fails statistical linearity tests in the low bits.
+ * Xoroshiro128**
+ *   https://prng.di.unimi.it/xoroshiro128starstar.c
+ * - mild and irrelevant issues: close to zero state, stats issues after some transforms
+ * Xoshiro256**
+ * - it looks good, but some issues (no big deal for pg usage IMO)
+ *   https://www.pcg-random.org/posts/a-quick-look-at-xoshiro256.html
+ *   mild problems getting out of closes-to-zero state
+ *
+ * MT19937-64
+ * + default in many places, standard
+ * - huge 2.5 KiB state (but TinyMT, 127 bits state space)
+ * - slow (but SFMT, twice as fast)
+ * - vulnerabilities on tinyMT?
+ *
+ * WELL
+ * - large state, 32-bit oriented
+ *
+ * ACORN Family (https://en.wikipedia.org/wiki/ACORN_(PRNG))
+ * - Fortran / double oriented, no C implementation found?!
+ * - medium large state "In practice we recommend using k > 10, M = 2^60 (for general application)
+ *   or M = 2^120 (for demanding applications requiring high-quality pseudo-random numbers
+ *   that will consistently pass all the tests in standard test packages such as TestU01)
+ *   and choose any odd value less than M for the seed."
+ *   k = 11 => 88 bytes/704-bits state, too large:-(
+ *
+ * Splitmix?
+ * Others?
+ *
+ * The conclusion is that we use xor-shift-rotate generator below.
+ */
+
+#include "c.h"
+#include "common/pg_prng.h"
+#include "port/pg_bitutils.h"
+#include <math.h>
+
+/* global state */
+pg_prng_state	pg_global_prng_state;
+
+#define FIRST_BIT_MASK UINT64CONST(0x8000000000000000)
+#define RIGHT_HALF_MASK UINT64CONST(0x00000000FFFFFFFF)
+#define DMANTISSA_MASK UINT64CONST(0x000FFFFFFFFFFFFF)
+
+/* 64-bits rotate left */
+static inline uint64
+rotl(const uint64 x, const int bits)
+{
+	return (x << bits) | (x >> (64 - bits));
+}
+
+/* another 64-bits generator is used for state initialization or iterations */
+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);
+}
+
+/* seed prng state from a 64 bits integer, ensuring non zero */
+void
+pg_prng_seed(pg_prng_state *state, uint64 seed)
+{
+	state->s0 = splitmix64(&seed);
+	state->s1 = splitmix64(&seed);
+}
+
+/* seed with 53 bits (mantissa & sign) from a float */
+void
+pg_prng_fseed(pg_prng_state *state, double fseed)
+{
+	uint64 seed = (int64) (((double) DMANTISSA_MASK) * fseed);
+	pg_prng_seed(state, seed);
+}
+
+/* strong random seeding */
+bool
+pg_prng_strong_seed(pg_prng_state *state)
+{
+	bool ok = pg_strong_random((void *) state, sizeof(pg_prng_state));
+
+	/* avoid zero with Donald Knuth's LCG parameters */
+	if (unlikely(state->s0 == 0 && state->s1 == 0))
+	{
+		/* should it warn that something is amiss if we get there? */
+		pg_prng_state def = PG_PRNG_DEFAULT_STATE;
+		*state = def;
+	}
+
+	return ok;
+}
+
+/* generator & state update */
+static uint64
+xoroshiro128ss(pg_prng_state *state)
+{
+	const 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;
+}
+
+/* u64 generator */
+uint64
+pg_prng_u64(pg_prng_state *state)
+{
+	return xoroshiro128ss(state);
+}
+
+/*
+ * select in a range with bitmask rejection.
+ *
+ * if range is empty, rmin is returned.
+ *
+ * the prng may advance by several states or none,
+ * depending on the range value.
+ */
+uint64
+pg_prng_u64_range(pg_prng_state *state, uint64 rmin, uint64 rmax)
+{
+	uint64 val;
+
+	if (likely(rmax > rmin))
+	{
+		uint64 range = rmax - rmin;
+
+		/* bit position is between 0 and 63, so rshift >= 0 */
+		uint32 rshift = 63 - pg_leftmost_one_pos64(range);
+
+		do
+		{
+			val = xoroshiro128ss(state) >> rshift;
+		}
+		while (unlikely(val > range));
+	}
+	else
+		val = 0;
+
+	return rmin + val;
+}
+
+/* i64 generator */
+int64
+pg_prng_i64(pg_prng_state *state)
+{
+	return (int64) xoroshiro128ss(state);
+}
+
+/* u32 generator */
+uint32
+pg_prng_u32(pg_prng_state *state)
+{
+	const uint64 v = xoroshiro128ss(state);
+	return (uint32) (((v >> 32) ^ v) & RIGHT_HALF_MASK);
+}
+
+/* i32 generator */
+int32
+pg_prng_i32(pg_prng_state *state)
+{
+	const uint64 v = xoroshiro128ss(state);
+	return (int32) (((v >> 32) ^ v) & RIGHT_HALF_MASK);
+}
+
+/* double generator */
+double
+pg_prng_f64(pg_prng_state *state)
+{
+	uint64 v = xoroshiro128ss(state);
+	return ldexp((double) (v & DMANTISSA_MASK), -52);
+}
+
+/* bool generator */
+bool
+pg_prng_bool(pg_prng_state *state)
+{
+	uint64 v = xoroshiro128ss(state);
+	return (v & FIRST_BIT_MASK) == FIRST_BIT_MASK;
+}
diff --git a/src/include/common/pg_prng.h b/src/include/common/pg_prng.h
new file mode 100644
index 0000000000..066ddffc1d
--- /dev/null
+++ b/src/include/common/pg_prng.h
@@ -0,0 +1,37 @@
+/*-------------------------------------------------------------------------
+ *
+ * PRNG: internal Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * src/include/common/pg_prng.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_PRNG_H
+#define PG_PRNG_H
+
+/* 128 bits state */
+typedef struct pg_prng_state {
+	uint64	s0, s1;
+} pg_prng_state;
+
+/* global pseudo-random state */
+extern PGDLLIMPORT pg_prng_state	pg_global_prng_state;
+
+/* use Donald Knuth's LCG constants for default state */
+#define PG_PRNG_DEFAULT_STATE \
+	{ UINT64CONST(0x5851F42D4C957F2D), UINT64CONST(0x14057B7EF767814F) }
+
+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_strong_seed(pg_prng_state *state);
+extern uint64 pg_prng_u64(pg_prng_state *state);
+extern uint64 pg_prng_u64_range(pg_prng_state *state, uint64 rmin, uint64 rmax);
+extern int64 pg_prng_i64(pg_prng_state *state);
+extern uint32 pg_prng_u32(pg_prng_state *state);
+extern int32 pg_prng_i32(pg_prng_state *state);
+extern double pg_prng_f64(pg_prng_state *state);
+extern bool pg_prng_bool(pg_prng_state *state);
+
+#endif							/* PG_PRNG_H */
diff --git a/src/include/optimizer/geqo.h b/src/include/optimizer/geqo.h
index 24dcdfb6cc..b28e4f084a 100644
--- a/src/include/optimizer/geqo.h
+++ b/src/include/optimizer/geqo.h
@@ -22,6 +22,7 @@
 #ifndef GEQO_H
 #define GEQO_H
 
+#include "common/pg_prng.h"
 #include "nodes/pathnodes.h"
 #include "optimizer/geqo_gene.h"
 
@@ -72,8 +73,8 @@ extern double Geqo_seed;		/* 0 .. 1 */
  */
 typedef struct
 {
-	List	   *initial_rels;	/* the base relations we are joining */
-	unsigned short random_state[3]; /* state for pg_erand48() */
+	List		   *initial_rels;	/* the base relations we are joining */
+	pg_prng_state	random_state;   /* PRNG state */
 } GeqoPrivateData;
 
 
diff --git a/src/include/port.h b/src/include/port.h
index 49b4d38131..806fb795ed 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -362,11 +362,6 @@ extern int	gettimeofday(struct timeval *tp, struct timezone *tzp);
 #define pgoff_t off_t
 #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
 extern int	fls(int mask);
 #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);
 #endif
 
-#if !defined(HAVE_RANDOM)
-extern long random(void);
-#endif
-
 #ifndef HAVE_SETENV
 extern int	setenv(const char *name, const char *value, int overwrite);
 #endif
@@ -464,10 +455,6 @@ extern int	setenv(const char *name, const char *value, int overwrite);
 extern int	unsetenv(const char *name);
 #endif
 
-#ifndef HAVE_SRANDOM
-extern void srandom(unsigned int seed);
-#endif
-
 #ifndef HAVE_DLOPEN
 extern void *dlopen(const char *file, int mode);
 extern void *dlsym(void *handle, const char *symbol);
diff --git a/src/include/utils/sampling.h b/src/include/utils/sampling.h
index a58d14281b..c0b1c6a645 100644
--- a/src/include/utils/sampling.h
+++ b/src/include/utils/sampling.h
@@ -13,15 +13,14 @@
 #ifndef SAMPLING_H
 #define SAMPLING_H
 
+#include "common/pg_prng.h"
 #include "storage/block.h"		/* for typedef BlockNumber */
 
 
 /* Random generator for sampling code */
-typedef unsigned short SamplerRandomState[3];
-
 extern void sampler_random_init_state(long seed,
-									  SamplerRandomState randstate);
-extern double sampler_random_fract(SamplerRandomState randstate);
+									  pg_prng_state *randstate);
+extern double sampler_random_fract(pg_prng_state *randstate);
 
 /* Block sampling methods */
 
@@ -32,7 +31,7 @@ typedef struct
 	int			n;				/* desired sample size */
 	BlockNumber t;				/* current block number */
 	int			m;				/* blocks selected so far */
-	SamplerRandomState randstate;	/* random generator state */
+	pg_prng_state randstate;	/* random generator state */
 } BlockSamplerData;
 
 typedef BlockSamplerData *BlockSampler;
@@ -46,8 +45,9 @@ extern BlockNumber BlockSampler_Next(BlockSampler bs);
 
 typedef struct
 {
-	double		W;
-	SamplerRandomState randstate;	/* random generator state */
+	double			W;
+	pg_prng_state	randstate;	/* random generator state */
+	bool			randstate_initialized;
 } ReservoirStateData;
 
 typedef ReservoirStateData *ReservoirState;
diff --git a/src/port/Makefile b/src/port/Makefile
index 52dbf5783f..b3754d8940 100644
--- a/src/port/Makefile
+++ b/src/port/Makefile
@@ -42,7 +42,6 @@ OBJS = \
 	$(PG_CRC32C_OBJS) \
 	bsearch_arg.o \
 	chklocale.o \
-	erand48.o \
 	inet_net_ntop.o \
 	noblock.o \
 	path.o \
diff --git a/src/port/erand48.c b/src/port/erand48.c
deleted file mode 100644
index a82ea94220..0000000000
--- a/src/port/erand48.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/port/random.c b/src/port/random.c
deleted file mode 100644
index 2dd59a0829..0000000000
--- a/src/port/random.c
+++ /dev/null
@@ -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();
-}
diff --git a/src/port/srandom.c b/src/port/srandom.c
deleted file mode 100644
index cf1007b2ef..0000000000
--- a/src/port/srandom.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/test/modules/test_bloomfilter/test_bloomfilter.c b/src/test/modules/test_bloomfilter/test_bloomfilter.c
index 96c5011428..69425bd6fc 100644
--- a/src/test/modules/test_bloomfilter/test_bloomfilter.c
+++ b/src/test/modules/test_bloomfilter/test_bloomfilter.c
@@ -13,6 +13,7 @@
 #include "postgres.h"
 
 #include "fmgr.h"
+#include "common/pg_prng.h"
 #include "lib/bloomfilter.h"
 #include "miscadmin.h"
 
@@ -85,7 +86,7 @@ create_and_test_bloom(int power, int64 nelements, int callerseed)
 	 * 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_i32(&pg_global_prng_state) % PG_INT32_MAX : callerseed;
 
 	/* Create Bloom filter, populate it, and report on false positive rate */
 	filter = bloom_create(nelements, bloom_work_mem, seed);
diff --git a/src/test/modules/test_integerset/test_integerset.c b/src/test/modules/test_integerset/test_integerset.c
index 21c6f49b37..6f3ef47525 100644
--- a/src/test/modules/test_integerset/test_integerset.c
+++ b/src/test/modules/test_integerset/test_integerset.c
@@ -12,6 +12,7 @@
  */
 #include "postgres.h"
 
+#include "common/pg_prng.h"
 #include "fmgr.h"
 #include "lib/integerset.h"
 #include "miscadmin.h"
@@ -99,12 +100,20 @@ static void check_with_filler(IntegerSet *intset, uint64 x, uint64 value, uint64
 static void test_single_value_and_filler(uint64 value, uint64 filler_min, uint64 filler_max);
 static void test_huge_distances(void);
 
+static pg_prng_state	pr_state;
+
 /*
  * SQL-callable entry point to perform all tests.
  */
 Datum
 test_integerset(PG_FUNCTION_ARGS)
 {
+	if(unlikely(!pg_prng_strong_seed(&pr_state)))
+	{
+		elog(ERROR, "pg_prng_strong_seed() failed");
+		PG_RETURN_VOID();
+	}
+
 	/* Tests for various corner cases */
 	test_empty();
 	test_huge_distances();
@@ -248,8 +257,7 @@ test_pattern(const test_spec *spec)
 		 * only a small part of the integer space is used.  We would very
 		 * rarely hit values that are actually in the set.
 		 */
-		x = (pg_lrand48() << 31) | pg_lrand48();
-		x = x % (last_int + 1000);
+		x = pg_prng_u64(&pr_state) % (last_int + 1000);
 
 		/* Do we expect this value to be present in the set? */
 		if (x >= last_int)
@@ -571,7 +579,7 @@ test_huge_distances(void)
 	 */
 	while (num_values < 1000)
 	{
-		val += pg_lrand48();
+		val += pg_prng_u32(&pr_state);
 		values[num_values++] = val;
 	}
 
diff --git a/src/test/modules/test_rbtree/test_rbtree.c b/src/test/modules/test_rbtree/test_rbtree.c
index 713ebd1b26..59a571185e 100644
--- a/src/test/modules/test_rbtree/test_rbtree.c
+++ b/src/test/modules/test_rbtree/test_rbtree.c
@@ -14,6 +14,7 @@
 #include "postgres.h"
 
 #include "fmgr.h"
+#include "common/pg_prng.h"
 #include "lib/rbtree.h"
 #include "utils/memutils.h"
 
@@ -108,7 +109,7 @@ GetPermutation(int size)
 	 */
 	for (i = 1; i < size; i++)
 	{
-		int			j = random() % (i + 1);
+		int			j = pg_prng_u32(&pg_global_prng_state) % (i + 1);
 
 		if (j < i)				/* avoid fetching undefined data if j=i */
 			permutation[i] = permutation[j];
@@ -320,7 +321,7 @@ testdelete(int size, int delsize)
 
 	for (i = 0; i < delsize; i++)
 	{
-		int			k = random() % size;
+		int			k = pg_prng_u32(&pg_global_prng_state) % size;
 
 		while (chosen[k])
 			k = (k + 1) % size;
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 41172eab36..5a374a4727 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -99,9 +99,9 @@ sub mkvcbuild
 	$solution = CreateSolution($vsVersion, $config);
 
 	our @pgportfiles = qw(
-	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c random.c
-	  srandom.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
+	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c
+	  getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
+	  snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
 	  dirent.c dlopen.c getopt.c getopt_long.c link.c
 	  pread.c preadv.c pwrite.c pwritev.c pg_bitutils.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
 	  f2s.c file_perm.c file_utils.c hashfn.c ip.c jsonapi.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
-	  saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c username.c
-	  wait_error.c wchar.c);
+	  pg_get_line.c pg_lzcompress.c pg_prng.c pgfnames.c psprintf.c relpath.c
+	  rmtree.c saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c
+	  username.c wait_error.c wchar.c);
 
 	if ($solution->{options}->{openssl})
 	{
diff --git a/src/tools/testint128.c b/src/tools/testint128.c
index 71c345969a..43d569aeb5 100644
--- a/src/tools/testint128.c
+++ b/src/tools/testint128.c
@@ -27,6 +27,7 @@
 #endif
 
 #include "common/int128.h"
+#include "common/pg_prng.h"
 
 /*
  * We assume the parts of this union are laid out compatibly.
@@ -61,27 +62,11 @@ my_int128_compare(int128 x, int128 y)
 	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.
  *
  * 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 fix the prng state (hmmm...).
  *
  * 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;
 
+	pg_prng_seed(&pg_global_prng_state, 0);
+
 	if (argc >= 2)
 		count = strtol(argv[1], NULL, 0);
 	else
@@ -97,9 +84,9 @@ main(int argc, char **argv)
 
 	while (count-- > 0)
 	{
-		int64		x = get_random_uint64();
-		int64		y = get_random_uint64();
-		int64		z = get_random_uint64();
+		int64		x = pg_prng_u64(&pg_global_prng_state);
+		int64		y = pg_prng_u64(&pg_global_prng_state);
+		int64		z = pg_prng_u64(&pg_global_prng_state);
 		test128		t1;
 		test128		t2;
 
#53Tom Lane
tgl@sss.pgh.pa.us
In reply to: Aleksander Alekseev (#52)
Re: rand48 replacement

Aleksander Alekseev <aleksander@timescale.com> writes:

It looks like the patch is in pretty good shape. I noticed that the
return value of pg_prng_strong_seed() is not checked in several
places, also there was a typo in pg_trgm.c. The corrected patch is
attached. Assuming the new version will not upset cfbot, I would call
the patch "Ready for Committer".

I took a quick look through this. The biggest substantive point
I found was that you didn't update the configure script. It's
certainly not appropriate for configure to continue to do
AC_REPLACE_FUNCS on random and srandom when you've removed the
src/port files that that would attempt to include.

The simplest change here is just to delete those entries from the
list, but that would also result in not #define'ing HAVE_RANDOM
or HAVE_SRANDOM, and I see that the patch introduces a dependency
on the latter. I'm inclined to think that's misguided. srandom()
has been required by POSIX since SUSv2, and we certainly have not
got any non-Windows buildfarm members that lack it. So I don't
think we really need a configure check. What we do need is a decision
about what to do on Windows. We could write it like

+#ifndef WIN32
+	srandom(pg_prng_i32(&pg_global_prng_state));
+#endif

but I have a different modest suggestion: add

#define srandom(seed) srand(seed)

in win32_port.h. As far as I can see from Microsoft's docs [1]https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/srand?view=msvc-170,
srand() is exactly like srandom(), they just had some compulsion
to not be POSIX-compatible.

BTW, the commentary in InitProcessGlobals is now completely
inadequate; it's unclear to a reader why we should be bothering
ith srandom(). I suggest adding a comment right before the
srandom() call, along the lines of

/*
* Also make sure that we've set a good seed for random() (or rand()
* on Windows). Use of those functions is deprecated in core
* Postgres, but they might get used by extensions.
*/

+/* use Donald Knuth's LCG constants for default state */

How did Knuth get into this? This algorithm is certainly not his,
so why are those constants at all relevant?

Other cosmetic/commentary issues:

* I could do without the stream-of-consciousness notes in pg_prng.c.
I think what's appropriate is to say "We use thus-and-such a generator
which is documented here", maybe with a line or two about its properties.

* Function names like these convey practically nothing to readers:

+extern int64 pg_prng_i64(pg_prng_state *state);
+extern uint32 pg_prng_u32(pg_prng_state *state);
+extern int32 pg_prng_i32(pg_prng_state *state);
+extern double pg_prng_f64(pg_prng_state *state);
+extern bool pg_prng_bool(pg_prng_state *state);

and these functions' header comments add a grand total of zero bits
of information. What someone generally wants to know first about
a PRNG is (a) is it uniform and (b) what is the range of outputs,
neither of which are specified anywhere.

+#define FIRST_BIT_MASK UINT64CONST(0x8000000000000000)
+#define RIGHT_HALF_MASK UINT64CONST(0x00000000FFFFFFFF)
+#define DMANTISSA_MASK UINT64CONST(0x000FFFFFFFFFFFFF)

I'm not sure that these named constants are any more readable than
writing the underlying constant, maybe less so --- in particular
I think something based on (1<<52)-1 would be more appropriate for
the float mantissa operations. We don't need RIGHT_HALF_MASK at
all, the casts to uint32 or int32 will accomplish that just fine.

BTW, why are we bothering with FIRST_BIT_MASK in the first place,
rather than returning "v & 1" for pg_prng_bool? Is xoroshiro128ss
less random in the low-order bits than the higher? If so, that would
be a pretty important thing to document. If it's not, we shouldn't
make the code look like it is.

+ * select in a range with bitmask rejection.

What is "bitmask rejection"? Is it actually important to callers?
I think this should be documented more like "Produce a random
integer uniformly selected from the range [rmin, rmax)."

regards, tom lane

[1]: https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/srand?view=msvc-170

#54Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tom Lane (#53)
Re: rand48 replacement

I wrote:

... What we do need is a decision
about what to do on Windows.  We could write it like
+#ifndef WIN32
+	srandom(pg_prng_i32(&pg_global_prng_state));
+#endif
but I have a different modest suggestion: add
#define srandom(seed) srand(seed)
in win32_port.h.  As far as I can see from Microsoft's docs [1],
srand() is exactly like srandom(), they just had some compulsion
to not be POSIX-compatible.

Oh, wait, I take that back --- rand()/srand() are also in POSIX,
and in the C99 standard (which presumably is where Microsoft got
them from). They're deprecated by POSIX on the grounds that the
spec only allows them to have 32 bits of state, so they can't be
terribly random. Given that, I think we should just avert our eyes;
anybody depending on those functions is destined to lose anyway.
Probably the "#ifndef WIN32" fragment suggested above is enough.
I suppose we could *also* call srand() but that feels a bit silly.

regards, tom lane

#55Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Tom Lane (#53)
Re: rand48 replacement

Hello Tom,

Thanks for the feedback.

+/* use Donald Knuth's LCG constants for default state */

How did Knuth get into this? This algorithm is certainly not his,
so why are those constants at all relevant?

They are not more nor less relevant than any other "random" constant. The
state needs a default initialization. The point of using DK's is that it
is somehow cannot be some specially crafted value which would have some
special property only know to the purveyor of the constant and could be
used by them to break the algorithm.

https://en.wikipedia.org/wiki/Dual_EC_DRBG

* I could do without the stream-of-consciousness notes in pg_prng.c.
I think what's appropriate is to say "We use thus-and-such a generator
which is documented here", maybe with a line or two about its properties.

The stuff was really written essentially as a "why this" for the first
patch, and to prevent questions about "why not this other generator"
later, because it could never stop.

* Function names like these convey practically nothing to readers:

+extern int64 pg_prng_i64(pg_prng_state *state);
+extern uint32 pg_prng_u32(pg_prng_state *state);
+extern int32 pg_prng_i32(pg_prng_state *state);
+extern double pg_prng_f64(pg_prng_state *state);
+extern bool pg_prng_bool(pg_prng_state *state);

The intention is obviously "postgres pseudo-random number generator for
<type>". ISTM that it conveys (1) that it is a postgres-specific stuff,
(2) that it is a PRNG (which I find *MUCH* more informative than the
misleading statement that something is random when it is not, and it is
shorter) and (3) about the type it returns, because C does require
functions to have distinct names.

What would you suggest?

and these functions' header comments add a grand total of zero bits
of information.

Yes, probably. I do not like not to comment at all on a function.

What someone generally wants to know first about a PRNG is (a) is it
uniform and (b) what is the range of outputs, neither of which are
specified anywhere.

ISTM (b) is suggested thanks to the type and (a) I'm not sure about a PRNG
which would claim not at least claim to be uniform. Non uniform PRNG are
usually built based on a uniform one.

What do you suggest as alternate names?

+#define FIRST_BIT_MASK UINT64CONST(0x8000000000000000)
+#define RIGHT_HALF_MASK UINT64CONST(0x00000000FFFFFFFF)
+#define DMANTISSA_MASK UINT64CONST(0x000FFFFFFFFFFFFF)

I'm not sure that these named constants are any more readable than
writing the underlying constant, maybe less so --- in particular
I think something based on (1<<52)-1 would be more appropriate for
the float mantissa operations. We don't need RIGHT_HALF_MASK at
all, the casts to uint32 or int32 will accomplish that just fine.

Yep. I did it for uniformity.

BTW, why are we bothering with FIRST_BIT_MASK in the first place,
rather than returning "v & 1" for pg_prng_bool?

Because some PRNG are very bad in the low bits, not xoroshiro stuff,
though.

Is xoroshiro128ss less random in the low-order bits than the higher?
If so, that would be a pretty important thing to document. If it's not,
we shouldn't make the code look like it is.

Dunno. Why should we prefer low bits?

+ * select in a range with bitmask rejection.

What is "bitmask rejection"? Is it actually important to callers?

No, it is important to understand how it does it. That is the name of the
technique which is implemented, which helps if you want to understand what
is going on by googling it. This point could be moved inside the function.

I think this should be documented more like "Produce a random
integer uniformly selected from the range [rmin, rmax)."

Sure.

--
Fabien.

#56Tom Lane
tgl@sss.pgh.pa.us
In reply to: Fabien COELHO (#55)
Re: rand48 replacement

Fabien COELHO <coelho@cri.ensmp.fr> writes:

How did Knuth get into this? This algorithm is certainly not his,
so why are those constants at all relevant?

They are not more nor less relevant than any other "random" constant. The
state needs a default initialization. The point of using DK's is that it
is somehow cannot be some specially crafted value which would have some
special property only know to the purveyor of the constant and could be
used by them to break the algorithm.

Well, none of that is in the comment, which is probably just as well
because it reads like baseless paranoia. *Any* initialization vector
should be as good as any other; if it's not, that's an algorithm fault.
(OK, I'll give it a pass for zeroes being bad, but otherwise not.)

* Function names like these convey practically nothing to readers:

+extern int64 pg_prng_i64(pg_prng_state *state);
+extern uint32 pg_prng_u32(pg_prng_state *state);
+extern int32 pg_prng_i32(pg_prng_state *state);
+extern double pg_prng_f64(pg_prng_state *state);
+extern bool pg_prng_bool(pg_prng_state *state);

The intention is obviously "postgres pseudo-random number generator for
<type>". ISTM that it conveys (1) that it is a postgres-specific stuff,
(2) that it is a PRNG (which I find *MUCH* more informative than the
misleading statement that something is random when it is not, and it is
shorter) and (3) about the type it returns, because C does require
functions to have distinct names.

What would you suggest?

We have names for these types, and those abbreviations are (mostly)
not them. Name-wise I'd be all right with pg_prng_int64 and so on,
but I still expect that these functions' header comments should be
explicit about uniformity and about the precise output range.
As an example, it's far from obvious whether the minimum value
of pg_prng_int32 should be zero or INT_MIN. (Actually, I suspect
you ought to provide both of those cases.) And the output range
of pg_prng_float8 is not merely unobvious, but not very easy
to deduce from examining the code either; not that users should
have to.

BTW, why are we bothering with FIRST_BIT_MASK in the first place,
rather than returning "v & 1" for pg_prng_bool?

Because some PRNG are very bad in the low bits, not xoroshiro stuff,
though.

Good, but then you shouldn't write associated code as if that's still
a problem, because you'll cause other people to think it's still a
problem and write equally contorted code elsewhere. "v & 1" is a
transparent way of producing a bool, while this code isn't.

regards, tom lane

#57Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Tom Lane (#56)
1 attachment(s)
Re: rand48 replacement

Hello,

They are not more nor less relevant than any other "random" constant. The
state needs a default initialization. The point of using DK's is that it
is somehow cannot be some specially crafted value which would have some
special property only know to the purveyor of the constant and could be
used by them to break the algorithm.

Well, none of that is in the comment, which is probably just as well
because it reads like baseless paranoia.

Sure. Welcome to cryptography:-)

*Any* initialization vector should be as good as any other; if it's not,
that's an algorithm fault.

Yep.

(OK, I'll give it a pass for zeroes being bad, but otherwise not.)

Ok. We can use any non-zero constant. What's wrong with constants provided
by a Turing award computer scientist? I find them more attractive that
some stupid 0x0123456789….

* Function names like these convey practically nothing to readers:

+extern int64 pg_prng_i64(pg_prng_state *state); [...]

The intention is obviously "postgres pseudo-random number generator for
<type>". [...]

What would you suggest?

We have names for these types, and those abbreviations are (mostly)
not them. Name-wise I'd be all right with pg_prng_int64 and so on,

Ok. You prefer "uint64" to "u64".

but I still expect that these functions' header comments should be
explicit about uniformity and about the precise output range.

Ok.

As an example, it's far from obvious whether the minimum value
of pg_prng_int32 should be zero or INT_MIN.
(Actually, I suspect you ought to provide both of those cases.)

I agree that it is not obvious. I added "p" for "positive" variants. I
found one place where one could be used.

And the output range of pg_prng_float8 is not merely unobvious, but not
very easy to deduce from examining the code either; not that users
should have to.

Ok.

BTW, why are we bothering with FIRST_BIT_MASK in the first place,
rather than returning "v & 1" for pg_prng_bool?

Because some PRNG are very bad in the low bits, not xoroshiro stuff,
though.

Good, but then you shouldn't write associated code as if that's still
a problem, because you'll cause other people to think it's still a
problem and write equally contorted code elsewhere. "v & 1" is a
transparent way of producing a bool, while this code isn't.

"v & 1" really produces an integer, not a bool. I'd prefer to actually
generate a boolean and let the compiler optimizer do the cleaning.

Some Xoshiro-family generators have "linear artifacts in the low bits",
Although Xoroshiro128** is supposed to be immune, I thought better to keep
away from these, and I could not see why the last bit would be better than
any other bit, so taking the first looked okay to me at least.

I think that the attached v18 addresses most of your concerns.

--
Fabien.

Attachments:

prng-18.patchtext/x-diff; name=prng-18.patchDownload
diff --git a/configure b/configure
index 896b781473..f8c8164428 100755
--- a/configure
+++ b/configure
@@ -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
diff --git a/configure.ac b/configure.ac
index b50130b323..a5c10b8d56 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1858,8 +1858,6 @@ AC_REPLACE_FUNCS(m4_normalize([
 	mkdtemp
 	pread
 	pwrite
-	random
-	srandom
 	strlcat
 	strlcpy
 	strnlen
diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c
index 42a830c33b..659ac05655 100644
--- a/contrib/amcheck/verify_nbtree.c
+++ b/contrib/amcheck/verify_nbtree.c
@@ -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"
@@ -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();
+		/* Random seed relies on backend prng initialization 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;
diff --git a/contrib/auto_explain/auto_explain.c b/contrib/auto_explain/auto_explain.c
index e9092ba359..286d8c5098 100644
--- a/contrib/auto_explain/auto_explain.c
+++ b/contrib/auto_explain/auto_explain.c
@@ -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"
@@ -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;
 	}
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 2c2f149fb0..146b524076 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -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]);
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 45a09337d0..f767fdcc6a 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -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]);
 		}
diff --git a/contrib/tablefunc/tablefunc.c b/contrib/tablefunc/tablefunc.c
index 52b272f298..63bb91a646 100644
--- a/contrib/tablefunc/tablefunc.c
+++ b/contrib/tablefunc/tablefunc.c
@@ -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"
@@ -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;
diff --git a/contrib/tsm_system_rows/tsm_system_rows.c b/contrib/tsm_system_rows/tsm_system_rows.c
index 4996612902..1a46d4b143 100644
--- a/contrib/tsm_system_rows/tsm_system_rows.c
+++ b/contrib/tsm_system_rows/tsm_system_rows.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/contrib/tsm_system_time/tsm_system_time.c b/contrib/tsm_system_time/tsm_system_time.c
index 788d8f9a68..36acc6c106 100644
--- a/contrib/tsm_system_time/tsm_system_time.c
+++ b/contrib/tsm_system_time/tsm_system_time.c
@@ -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);
 
 
 /*
@@ -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 */
@@ -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;
 
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index 03191e016c..e93bf29999 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -16,6 +16,7 @@
 
 #include "access/gin_private.h"
 #include "access/relscan.h"
+#include "common/pg_prng.h"
 #include "miscadmin.h"
 #include "storage/predicate.h"
 #include "utils/datum.h"
@@ -787,7 +788,7 @@ entryLoadMoreItems(GinState *ginstate, GinScanEntry entry,
 	}
 }
 
-#define gin_rand() (((double) random()) / ((double) MAX_RANDOM_VALUE))
+#define gin_rand() pg_prng_double(&pg_global_prng_state)
 #define dropItem(e) ( gin_rand() > ((double)GinFuzzySearchLimit)/((double)((e)->predictNumberResult)) )
 
 /*
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 43ba03b6eb..94dbabc198 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -19,6 +19,7 @@
 #include "access/htup_details.h"
 #include "access/reloptions.h"
 #include "catalog/pg_opclass.h"
+#include "common/pg_prng.h"
 #include "storage/indexfsm.h"
 #include "storage/lmgr.h"
 #include "utils/float.h"
@@ -507,7 +508,7 @@ gistchoose(Relation r, Page p, IndexTuple it,	/* it has compressed entry */
 			if (keep_current_best == -1)
 			{
 				/* we didn't make the random choice yet for this old best */
-				keep_current_best = (random() <= (MAX_RANDOM_VALUE / 2)) ? 1 : 0;
+				keep_current_best = pg_prng_bool(&pg_global_prng_state) ? 1 : 0;
 			}
 			if (keep_current_best == 0)
 			{
@@ -529,7 +530,7 @@ gistchoose(Relation r, Page p, IndexTuple it,	/* it has compressed entry */
 			if (keep_current_best == -1)
 			{
 				/* we didn't make the random choice yet for this old best */
-				keep_current_best = (random() <= (MAX_RANDOM_VALUE / 2)) ? 1 : 0;
+				keep_current_best = pg_prng_bool(&pg_global_prng_state) ? 1 : 0;
 			}
 			if (keep_current_best == 1)
 				break;
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index 0fe8c70939..2b898d312c 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -19,6 +19,7 @@
 #include "access/nbtxlog.h"
 #include "access/transam.h"
 #include "access/xloginsert.h"
+#include "common/pg_prng.h"
 #include "lib/qunique.h"
 #include "miscadmin.h"
 #include "storage/lmgr.h"
@@ -965,7 +966,7 @@ _bt_findinsertloc(Relation rel,
 
 			if (P_RIGHTMOST(opaque) ||
 				_bt_compare(rel, itup_key, page, P_HIKEY) != 0 ||
-				random() <= (MAX_RANDOM_VALUE / 100))
+				pg_prng_double(&pg_global_prng_state) <= 0.01)
 				break;
 
 			_bt_stepright(rel, insertstate, stack);
diff --git a/src/backend/access/spgist/spgdoinsert.c b/src/backend/access/spgist/spgdoinsert.c
index 70557bcf3d..70dd86b19a 100644
--- a/src/backend/access/spgist/spgdoinsert.c
+++ b/src/backend/access/spgist/spgdoinsert.c
@@ -19,6 +19,7 @@
 #include "access/spgist_private.h"
 #include "access/spgxlog.h"
 #include "access/xloginsert.h"
+#include "common/pg_prng.h"
 #include "miscadmin.h"
 #include "storage/bufmgr.h"
 #include "utils/rel.h"
@@ -2210,7 +2211,7 @@ 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_uint32(&pg_global_prng_state) % innerTuple->nNodes;
 			}
 
 			switch (out.resultType)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 8e35c432f5..e7b0bc804d 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -37,6 +37,7 @@
 #include "commands/async.h"
 #include "commands/tablecmds.h"
 #include "commands/trigger.h"
+#include "common/pg_prng.h"
 #include "executor/spi.h"
 #include "libpq/be-fsstubs.h"
 #include "libpq/pqsignal.h"
@@ -1990,7 +1991,7 @@ StartTransaction(void)
 	/* Determine if statements are logged in this transaction */
 	xact_is_sampled = log_xact_sample_rate != 0 &&
 		(log_xact_sample_rate == 1 ||
-		 random() <= log_xact_sample_rate * MAX_RANDOM_VALUE);
+		 pg_prng_double(&pg_global_prng_state) <= log_xact_sample_rate);
 
 	/*
 	 * initialize current transaction state fields
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 4928702aec..af96a914f7 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -38,6 +38,7 @@
 #include "commands/progress.h"
 #include "commands/tablecmds.h"
 #include "commands/vacuum.h"
+#include "common/pg_prng.h"
 #include "executor/executor.h"
 #include "foreign/fdwapi.h"
 #include "miscadmin.h"
@@ -1162,7 +1163,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 	OldestXmin = GetOldestNonRemovableTransactionId(onerel);
 
 	/* Prepare for sampling block numbers */
-	randseed = random();
+	randseed = pg_prng_int32(&pg_global_prng_state);
 	nblocks = BlockSampler_Init(&bs, totalblocks, targrows, randseed);
 
 #ifdef USE_PREFETCH
@@ -1279,7 +1280,7 @@ acquire_sample_rows(Relation onerel, int elevel,
 					 * Found a suitable tuple, so save it, replacing one old
 					 * tuple at random
 					 */
-					int			k = (int) (targrows * sampler_random_fract(rstate.randstate));
+					int			k = (int) (targrows * sampler_random_fract(&rstate.randstate));
 
 					Assert(k >= 0 && k < targrows);
 					heap_freetuple(rows[k]);
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 44232d50d0..20f4e1f119 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -17,6 +17,7 @@
 #include "access/relscan.h"
 #include "access/tableam.h"
 #include "access/tsmapi.h"
+#include "common/pg_prng.h"
 #include "executor/executor.h"
 #include "executor/nodeSamplescan.h"
 #include "miscadmin.h"
@@ -154,7 +155,7 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	 * do this just once, since the seed shouldn't change over rescans.
 	 */
 	if (tsc->repeatable == NULL)
-		scanstate->seed = random();
+		scanstate->seed = pg_prng_int32(&pg_global_prng_state);
 
 	/*
 	 * Finally, initialize the TABLESAMPLE method handler.
diff --git a/src/backend/optimizer/geqo/geqo_random.c b/src/backend/optimizer/geqo/geqo_random.c
index f21bc047e6..776fce495d 100644
--- a/src/backend/optimizer/geqo/geqo_random.c
+++ b/src/backend/optimizer/geqo/geqo_random.c
@@ -21,14 +21,7 @@ geqo_set_seed(PlannerInfo *root, double seed)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	/*
-	 * XXX. This seeding algorithm could certainly be improved - but it is not
-	 * critical to do so.
-	 */
-	memset(private->random_state, 0, sizeof(private->random_state));
-	memcpy(private->random_state,
-		   &seed,
-		   Min(sizeof(private->random_state), sizeof(seed)));
+	pg_prng_fseed(&private->random_state, seed);
 }
 
 double
@@ -36,5 +29,5 @@ geqo_rand(PlannerInfo *root)
 {
 	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 
-	return pg_erand48(private->random_state);
+	return pg_prng_double(&private->random_state);
 }
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index db797c040b..5d68257bb5 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -98,6 +98,7 @@
 #include "catalog/pg_control.h"
 #include "common/file_perm.h"
 #include "common/ip.h"
+#include "common/pg_prng.h"
 #include "common/string.h"
 #include "lib/ilist.h"
 #include "libpq/auth.h"
@@ -2699,19 +2700,22 @@ ClosePostmasterPorts(bool am_syslogger)
 void
 InitProcessGlobals(void)
 {
-	unsigned int rseed;
-
 	MyProcPid = getpid();
 	MyStartTimestamp = GetCurrentTimestamp();
 	MyStartTime = timestamptz_to_time_t(MyStartTimestamp);
 
 	/*
-	 * Set a different seed for random() in every process.  We want something
+	 * Set a different global seed in every process.  We want something
 	 * unpredictable, so if possible, use high-quality random bits for the
 	 * seed.  Otherwise, fall back to a seed based on timestamp and PID.
 	 */
-	if (!pg_strong_random(&rseed, sizeof(rseed)))
+	if (unlikely(!pg_prng_strong_seed(&pg_global_prng_state)))
 	{
+		uint64	rseed;
+
+		ereport(WARNING,
+				(errmsg_internal("pg_prng_strong_seed() failed, falling back to weaker seeding")));
+
 		/*
 		 * Since PIDs and timestamps tend to change more frequently in their
 		 * least significant bits, shift the timestamp left to allow a larger
@@ -2722,8 +2726,14 @@ InitProcessGlobals(void)
 		rseed = ((uint64) MyProcPid) ^
 			((uint64) MyStartTimestamp << 12) ^
 			((uint64) MyStartTimestamp >> 20);
+
+		pg_prng_seed(&pg_global_prng_state, rseed);
 	}
-	srandom(rseed);
+
+#ifndef WIN32
+	/* random() should not be used anywhere, but just in case */
+	srandom(pg_prng_int32(&pg_global_prng_state));
+#endif
 }
 
 
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index cb1a8dd34f..463b119d6c 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -92,6 +92,7 @@
 #include "catalog/pg_tablespace.h"
 #include "common/file_perm.h"
 #include "common/file_utils.h"
+#include "common/pg_prng.h"
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "port/pg_iovec.h"
@@ -2939,7 +2940,7 @@ SetTempTablespaces(Oid *tableSpaces, int numSpaces)
 	 * available tablespaces.
 	 */
 	if (numSpaces > 1)
-		nextTempTableSpace = random() % numSpaces;
+		nextTempTableSpace = pg_prng_uint32(&pg_global_prng_state) % numSpaces;
 	else
 		nextTempTableSpace = 0;
 }
diff --git a/src/backend/storage/ipc/dsm.c b/src/backend/storage/ipc/dsm.c
index b461a5f7e9..cfb10e9f3e 100644
--- a/src/backend/storage/ipc/dsm.c
+++ b/src/backend/storage/ipc/dsm.c
@@ -33,6 +33,7 @@
 #endif
 #include <sys/stat.h>
 
+#include "common/pg_prng.h"
 #include "lib/ilist.h"
 #include "miscadmin.h"
 #include "port/pg_bitutils.h"
@@ -180,7 +181,7 @@ dsm_postmaster_startup(PGShmemHeader *shim)
 	{
 		Assert(dsm_control_address == NULL);
 		Assert(dsm_control_mapped_size == 0);
-		dsm_control_handle = random() << 1; /* Even numbers only */
+		dsm_control_handle = pg_prng_uint32(&pg_global_prng_state) << 1; /* Even numbers only */
 		if (dsm_control_handle == DSM_HANDLE_INVALID)
 			continue;
 		if (dsm_impl_op(DSM_OP_CREATE, dsm_control_handle, segsize,
@@ -536,7 +537,7 @@ dsm_create(Size size, int flags)
 		for (;;)
 		{
 			Assert(seg->mapped_address == NULL && seg->mapped_size == 0);
-			seg->handle = random() << 1;	/* Even numbers only */
+			seg->handle = pg_prng_uint32(&pg_global_prng_state) << 1;	/* Even numbers only */
 			if (seg->handle == DSM_HANDLE_INVALID)	/* Reserve sentinel */
 				continue;
 			if (dsm_impl_op(DSM_OP_CREATE, seg->handle, size, &seg->impl_private,
@@ -1237,7 +1238,7 @@ make_main_region_dsm_handle(int slot)
 	 */
 	handle = 1;
 	handle |= slot << 1;
-	handle |= random() << (pg_leftmost_one_pos32(dsm_control->maxitems) + 1);
+	handle |= pg_prng_int32(&pg_global_prng_state) << (pg_leftmost_one_pos32(dsm_control->maxitems) + 1);
 	return handle;
 }
 
diff --git a/src/backend/storage/lmgr/s_lock.c b/src/backend/storage/lmgr/s_lock.c
index 91322a40c1..2f0ed6fd9e 100644
--- a/src/backend/storage/lmgr/s_lock.c
+++ b/src/backend/storage/lmgr/s_lock.c
@@ -50,6 +50,7 @@
 #include <time.h>
 #include <unistd.h>
 
+#include "common/pg_prng.h"
 #include "port/atomics.h"
 #include "storage/s_lock.h"
 
@@ -63,6 +64,7 @@
 slock_t		dummy_spinlock;
 
 static int	spins_per_delay = DEFAULT_SPINS_PER_DELAY;
+static pg_prng_state	prng_state;
 
 
 /*
@@ -143,8 +145,7 @@ perform_spin_delay(SpinDelayStatus *status)
 #endif
 
 		/* increase delay by a random fraction between 1X and 2X */
-		status->cur_delay += (int) (status->cur_delay *
-									((double) random() / (double) MAX_RANDOM_VALUE) + 0.5);
+		status->cur_delay += (int) (status->cur_delay * (pg_prng_double(&prng_state) + 0.5));
 		/* wrap back to minimum delay when max is exceeded */
 		if (status->cur_delay > MAX_DELAY_USEC)
 			status->cur_delay = MIN_DELAY_USEC;
@@ -303,7 +304,7 @@ volatile struct test_lock_struct test_lock;
 int
 main()
 {
-	srandom((unsigned int) time(NULL));
+	pg_prng_seed(&prng_state, (unsigned int) time(NULL));
 
 	test_lock.pad1 = test_lock.pad2 = 0x44;
 
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 0775abe35d..902d4e7ff8 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -42,6 +42,7 @@
 #include "catalog/pg_type.h"
 #include "commands/async.h"
 #include "commands/prepare.h"
+#include "common/pg_prng.h"
 #include "executor/spi.h"
 #include "jit/jit.h"
 #include "libpq/libpq.h"
@@ -2355,13 +2356,13 @@ check_log_duration(char *msec_str, bool was_logged)
 
 		/*
 		 * Do not log if log_statement_sample_rate = 0. Log a sample if
-		 * log_statement_sample_rate <= 1 and avoid unnecessary random() call
+		 * log_statement_sample_rate <= 1 and avoid unnecessary random call
 		 * if log_statement_sample_rate = 1.
 		 */
 		if (exceeded_sample_duration)
 			in_sample = log_statement_sample_rate != 0 &&
 				(log_statement_sample_rate == 1 ||
-				 random() <= log_statement_sample_rate * MAX_RANDOM_VALUE);
+				 pg_prng_double(&pg_global_prng_state) <= log_statement_sample_rate);
 
 		if (exceeded_duration || in_sample || log_duration || xact_is_sampled)
 		{
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 098bbb372b..1af6fdaf14 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -21,6 +21,7 @@
 
 #include "catalog/pg_type.h"
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/shortest_dec.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
@@ -65,7 +66,7 @@ float8		degree_c_one = 1.0;
 
 /* State for drandom() and setseed() */
 static bool drandom_seed_set = false;
-static unsigned short drandom_seed[3] = {0, 0, 0};
+static pg_prng_state drandom_seed = PG_PRNG_DEFAULT_STATE;
 
 /* Local function prototypes */
 static double sind_q1(double x);
@@ -2762,22 +2763,20 @@ drandom(PG_FUNCTION_ARGS)
 		 * Should that fail for some reason, we fall back on a lower-quality
 		 * seed based on current time and PID.
 		 */
-		if (!pg_strong_random(drandom_seed, sizeof(drandom_seed)))
+		if (!pg_strong_random(&drandom_seed, sizeof(drandom_seed)))
 		{
 			TimestampTz now = GetCurrentTimestamp();
 			uint64		iseed;
 
 			/* Mix the PID with the most predictable bits of the timestamp */
 			iseed = (uint64) now ^ ((uint64) MyProcPid << 32);
-			drandom_seed[0] = (unsigned short) iseed;
-			drandom_seed[1] = (unsigned short) (iseed >> 16);
-			drandom_seed[2] = (unsigned short) (iseed >> 32);
+			pg_prng_seed(&drandom_seed, iseed);
 		}
 		drandom_seed_set = true;
 	}
 
-	/* pg_erand48 produces desired result range [0.0 - 1.0) */
-	result = pg_erand48(drandom_seed);
+	/* produces desired result range [0.0 - 1.0) */
+	result = pg_prng_double(&drandom_seed);
 
 	PG_RETURN_FLOAT8(result);
 }
@@ -2790,7 +2789,6 @@ Datum
 setseed(PG_FUNCTION_ARGS)
 {
 	float8		seed = PG_GETARG_FLOAT8(0);
-	uint64		iseed;
 
 	if (seed < -1 || seed > 1 || isnan(seed))
 		ereport(ERROR,
@@ -2798,11 +2796,7 @@ setseed(PG_FUNCTION_ARGS)
 				 errmsg("setseed parameter %g is out of allowed range [-1,1]",
 						seed)));
 
-	/* Use sign bit + 47 fractional bits to fill drandom_seed[] */
-	iseed = (int64) (seed * (float8) UINT64CONST(0x7FFFFFFFFFFF));
-	drandom_seed[0] = (unsigned short) iseed;
-	drandom_seed[1] = (unsigned short) (iseed >> 16);
-	drandom_seed[2] = (unsigned short) (iseed >> 32);
+	pg_prng_fseed(&drandom_seed, seed);
 	drandom_seed_set = true;
 
 	PG_RETURN_VOID();
diff --git a/src/backend/utils/misc/sampling.c b/src/backend/utils/misc/sampling.c
index 0c327e823f..4dd3463441 100644
--- a/src/backend/utils/misc/sampling.c
+++ b/src/backend/utils/misc/sampling.c
@@ -49,7 +49,7 @@ BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize,
 	bs->t = 0;					/* blocks scanned so far */
 	bs->m = 0;					/* blocks selected so far */
 
-	sampler_random_init_state(randseed, bs->randstate);
+	sampler_random_init_state(randseed, &bs->randstate);
 
 	return Min(bs->n, bs->N);
 }
@@ -98,7 +98,7 @@ BlockSampler_Next(BlockSampler bs)
 	 * less than k, which means that we cannot fail to select enough blocks.
 	 *----------
 	 */
-	V = sampler_random_fract(bs->randstate);
+	V = sampler_random_fract(&bs->randstate);
 	p = 1.0 - (double) k / (double) K;
 	while (V < p)
 	{
@@ -136,10 +136,10 @@ reservoir_init_selection_state(ReservoirState rs, int n)
 	 * Reservoir sampling is not used anywhere where it would need to return
 	 * repeatable results so we can initialize it randomly.
 	 */
-	sampler_random_init_state(random(), rs->randstate);
+	sampler_random_init_state(pg_prng_uint32(&pg_global_prng_state), &rs->randstate);
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	rs->W = exp(-log(sampler_random_fract(rs->randstate)) / n);
+	rs->W = exp(-log(sampler_random_fract(&rs->randstate)) / n);
 }
 
 double
@@ -154,7 +154,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 		double		V,
 					quot;
 
-		V = sampler_random_fract(rs->randstate);	/* Generate V */
+		V = sampler_random_fract(&rs->randstate);	/* Generate V */
 		S = 0;
 		t += 1;
 		/* Note: "num" in Vitter's code is always equal to t - n */
@@ -186,7 +186,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 						tmp;
 
 			/* Generate U and X */
-			U = sampler_random_fract(rs->randstate);
+			U = sampler_random_fract(&rs->randstate);
 			X = t * (W - 1.0);
 			S = floor(X);		/* S is tentatively set to floor(X) */
 			/* Test if U <= h(S)/cg(X) in the manner of (6.3) */
@@ -215,7 +215,7 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
 				y *= numer / denom;
 				denom -= 1;
 			}
-			W = exp(-log(sampler_random_fract(rs->randstate)) / n); /* Generate W in advance */
+			W = exp(-log(sampler_random_fract(&rs->randstate)) / n); /* Generate W in advance */
 			if (exp(log(y) / n) <= (t + X) / t)
 				break;
 		}
@@ -230,24 +230,22 @@ reservoir_get_next_S(ReservoirState rs, double t, int n)
  *----------
  */
 void
-sampler_random_init_state(long seed, SamplerRandomState randstate)
+sampler_random_init_state(long seed, pg_prng_state *randstate)
 {
-	randstate[0] = 0x330e;		/* same as pg_erand48, but could be anything */
-	randstate[1] = (unsigned short) seed;
-	randstate[2] = (unsigned short) (seed >> 16);
+	pg_prng_seed(randstate, (uint64_t) seed);
 }
 
 /* Select a random value R uniformly distributed in (0 - 1) */
 double
-sampler_random_fract(SamplerRandomState randstate)
+sampler_random_fract(pg_prng_state *randstate)
 {
 	double		res;
 
-	/* pg_erand48 returns a value in [0.0 - 1.0), so we must reject 0 */
+	/* pg_prng_double returned value in [0.0 - 1.0), so we must reject 0.0 */
 	do
 	{
-		res = pg_erand48(randstate);
-	} while (res == 0.0);
+		res = pg_prng_double(randstate);
+	} while (unlikely(res == 0.0));
 	return res;
 }
 
@@ -266,22 +264,28 @@ double
 anl_random_fract(void)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(pg_prng_uint32(&pg_global_prng_state), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* and compute a random fraction */
-	return sampler_random_fract(oldrs.randstate);
+	return sampler_random_fract(&oldrs.randstate);
 }
 
 double
 anl_init_selection_state(int n)
 {
 	/* initialize if first time through */
-	if (oldrs.randstate[0] == 0)
-		sampler_random_init_state(random(), oldrs.randstate);
+	if (!oldrs.randstate_initialized)
+	{
+		sampler_random_init_state(pg_prng_uint32(&pg_global_prng_state), &oldrs.randstate);
+		oldrs.randstate_initialized = true;
+	}
 
 	/* Initial value of W (for use when Algorithm Z is first applied) */
-	return exp(-log(sampler_random_fract(oldrs.randstate)) / n);
+	return exp(-log(sampler_random_fract(&oldrs.randstate)) / n);
 }
 
 double
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 3c61c789e4..78ccb0944c 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -66,6 +66,7 @@
 #include "common/file_perm.h"
 #include "common/file_utils.h"
 #include "common/logging.h"
+#include "common/pg_prng.h"
 #include "common/restricted_token.h"
 #include "common/string.h"
 #include "common/username.h"
@@ -880,9 +881,10 @@ choose_dsm_implementation(void)
 {
 #ifdef HAVE_SHM_OPEN
 	int			ntries = 10;
+	pg_prng_state	prng_state;
 
-	/* Initialize random(); this function is its only user in this program. */
-	srandom((unsigned int) (getpid() ^ time(NULL)));
+	/* Initialize pgng state this function is its only user in this program. */
+	pg_prng_seed(&prng_state, ((unsigned int) (getpid() ^ time(NULL))));
 
 	while (ntries > 0)
 	{
@@ -890,7 +892,7 @@ choose_dsm_implementation(void)
 		char		name[64];
 		int			fd;
 
-		handle = random();
+		handle = pg_prng_uint32(&prng_state);
 		snprintf(name, 64, "/PostgreSQL.%u", handle);
 		if ((fd = shm_open(name, O_CREAT | O_RDWR | O_EXCL, 0600)) != -1)
 		{
diff --git a/src/bin/pg_test_fsync/pg_test_fsync.c b/src/bin/pg_test_fsync/pg_test_fsync.c
index fef31844fa..422070c487 100644
--- a/src/bin/pg_test_fsync/pg_test_fsync.c
+++ b/src/bin/pg_test_fsync/pg_test_fsync.c
@@ -15,6 +15,7 @@
 
 #include "access/xlogdefs.h"
 #include "common/logging.h"
+#include "common/pg_prng.h"
 #include "getopt_long.h"
 
 /*
@@ -71,6 +72,7 @@ static char full_buf[DEFAULT_XLOG_SEG_SIZE],
 static struct timeval start_t,
 			stop_t;
 static bool alarm_triggered = false;
+static pg_prng_state	prng_state;
 
 
 static void handle_args(int argc, char *argv[]);
@@ -117,6 +119,12 @@ main(int argc, char *argv[])
 	pqsignal(SIGHUP, signal_cleanup);
 #endif
 
+	if(unlikely(!pg_prng_strong_seed(&prng_state)))
+	{
+		pg_log_error("pg_prng_strong_seed() failed");
+		exit(1);
+	}
+
 	prepare_buf();
 
 	test_open();
@@ -233,7 +241,7 @@ prepare_buf(void)
 
 	/* write random data into buffer */
 	for (ops = 0; ops < DEFAULT_XLOG_SEG_SIZE; ops++)
-		full_buf[ops] = random();
+		full_buf[ops] = pg_prng_uint32(&prng_state);
 
 	buf = (char *) TYPEALIGN(XLOG_BLCKSZ, full_buf);
 }
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index c12b6f0615..6b91c5b11e 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -58,6 +58,7 @@
 #endif
 
 #include "common/int.h"
+#include "common/pg_prng.h"
 #include "common/logging.h"
 #include "common/string.h"
 #include "common/username.h"
@@ -350,16 +351,8 @@ typedef struct StatsData
  */
 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. */
-static RandomState base_random_sequence;
+static pg_prng_state	base_random_sequence;
 
 /* Synchronization barrier for start and connection */
 static THREAD_BARRIER_T barrier;
@@ -461,7 +454,7 @@ typedef struct
 	 * Separate randomness for each client. This is used for random functions
 	 * 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			command;		/* command number in script */
@@ -498,9 +491,9 @@ typedef struct
 	 * random state to make all of them independent of each other and
 	 * therefore deterministic at the thread level.
 	 */
-	RandomState ts_choose_rs;	/* random state for selecting a script */
-	RandomState ts_throttle_rs; /* random state for transaction throttling */
-	RandomState ts_sample_rs;	/* random state for log sampling */
+	pg_prng_state	ts_choose_rs;	/* random state for selecting a script */
+	pg_prng_state	ts_throttle_rs; /* random state for transaction throttling */
+	pg_prng_state	ts_sample_rs;	/* random state for log sampling */
 
 	int64		throttle_trigger;	/* previous/next throttling (us) */
 	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.
  */
 static void
-initRandomState(RandomState *random_state)
+initRandomState(pg_prng_state *state)
 {
-	random_state->xseed[0] = (unsigned short)
-		(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);
+	pg_prng_seed(state, pg_prng_uint64(&base_random_sequence));
 }
 
+
 /*
- * Random number generator: uniform distribution from min to max inclusive.
+ * PRNG: uniform distribution from min to max inclusive.
  *
  * Although the limits are expressed as int64, you can't generate the full
  * int64 range in one call, because the difference of the limits mustn't
- * overflow int64.  In practice it's unwise to ask for more than an int32
- * range, because of the limited precision of pg_erand48().
+ * overflow int64. This is not checked.
  */
 static int64
-getrand(RandomState *random_state, int64 min, int64 max)
+getrand(pg_prng_state *state, int64 min, int64 max)
 {
-	/*
-	 * 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));
+	return min + (int64) pg_prng_uint64_range(state, 0, max - min);
 }
 
 /*
@@ -942,8 +921,7 @@ getrand(RandomState *random_state, int64 min, int64 max)
  * value is exp(-parameter).
  */
 static int64
-getExponentialRand(RandomState *random_state, int64 min, int64 max,
-				   double parameter)
+getExponentialRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		cut,
 				uniform,
@@ -953,7 +931,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 	Assert(parameter > 0.0);
 	cut = exp(-parameter);
 	/* erand 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)
@@ -966,8 +944,7 @@ getExponentialRand(RandomState *random_state, int64 min, int64 max,
 
 /* random number generator: gaussian distribution from min to max inclusive */
 static int64
-getGaussianRand(RandomState *random_state, int64 min, int64 max,
-				double parameter)
+getGaussianRand(pg_prng_state *state, int64 min, int64 max, double parameter)
 {
 	double		stdev;
 	double		rand;
@@ -990,13 +967,13 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
 	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
 		 * are expected in (0, 1] (see
 		 * https://en.wikipedia.org/wiki/Box-Muller_transform)
 		 */
-		double		rand1 = 1.0 - pg_erand48(random_state->xseed);
-		double		rand2 = 1.0 - pg_erand48(random_state->xseed);
+		double		rand1 = 1.0 - pg_prng_double(state);
+		double		rand2 = 1.0 - pg_prng_double(state);
 
 		/* Box-Muller basic form transform */
 		double		var_sqrt = sqrt(-2.0 * log(rand1));
@@ -1026,7 +1003,7 @@ getGaussianRand(RandomState *random_state, int64 min, int64 max,
  * not be one.
  */
 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
@@ -1034,8 +1011,8 @@ getPoissonRand(RandomState *random_state, double center)
 	 */
 	double		uniform;
 
-	/* erand in [0, 1), uniform in (0, 1] */
-	uniform = 1.0 - pg_erand48(random_state->xseed);
+	/* pseudo-random value in [0, 1), uniform in (0, 1] */
+	uniform = 1.0 - pg_prng_double(state);
 
 	return (int64) (-log(uniform) * center + 0.5);
 }
@@ -1048,7 +1025,7 @@ getPoissonRand(RandomState *random_state, double center)
  * This works for s > 1.0, but may perform badly for s very close to 1.0.
  */
 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		x,
@@ -1063,8 +1040,8 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 	while (true)
 	{
 		/* random variates */
-		u = pg_erand48(random_state->xseed);
-		v = pg_erand48(random_state->xseed);
+		u = pg_prng_double(state);
+		v = pg_prng_double(state);
 
 		x = floor(pow(u, -1.0 / (s - 1.0)));
 
@@ -1078,14 +1055,14 @@ computeIterativeZipfian(RandomState *random_state, int64 n, double s)
 
 /* random number generator: zipfian distribution from min to max inclusive */
 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;
 
 	/* abort if parameter is invalid */
 	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 +1119,7 @@ getHashMurmur2(int64 val, uint64 seed)
  * For small sizes, this generates each of the (size!) possible permutations
  * of integers in the range [0, size) with roughly equal probability.  Once
  * the size is larger than 20, the number of possible permutations exceeds the
- * number of distinct states of the internal pseudorandom number generators,
+ * number of distinct states of the internal pseudorandom number generator,
  * and so not all possible permutations can be generated, but the permutations
  * chosen should continue to give the appearance of being random.
  *
@@ -1152,25 +1129,19 @@ getHashMurmur2(int64 val, uint64 seed)
 static int64
 permute(const int64 val, const int64 isize, const int64 seed)
 {
-	RandomState random_state1;
-	RandomState random_state2;
-	uint64		size;
-	uint64		v;
-	int			masklen;
-	uint64		mask;
-	int			i;
+	/* using a high-end PRNG is probably overkill */
+	pg_prng_state	state;
+	uint64			size;
+	uint64			v;
+	int				masklen;
+	uint64			mask;
+	int				i;
 
 	if (isize < 2)
 		return 0;				/* nothing to permute */
 
-	/* Initialize a pair of random states using the seed */
-	random_state1.xseed[0] = seed & 0xFFFF;
-	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;
+	/* Initialize prng state using the seed */
+	pg_prng_seed(&state, (uint64) seed);
 
 	/* Computations are performed on unsigned values */
 	size = (uint64) isize;
@@ -1216,8 +1187,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 					t;
 
 		/* Random multiply (by an odd number), XOR and rotate of lower half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_uint64(&state) & mask) | 1;
+		r = pg_prng_uint64(&state) & mask;
 		if (v <= mask)
 		{
 			v = ((v * m) ^ r) & mask;
@@ -1225,8 +1196,8 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random multiply (by an odd number), XOR and rotate of upper half */
-		m = (uint64) getrand(&random_state1, 0, mask) | 1;
-		r = (uint64) getrand(&random_state2, 0, mask);
+		m = (pg_prng_uint64(&state) & mask) | 1;
+		r = pg_prng_uint64(&state) & mask;
 		t = size - 1 - v;
 		if (t <= mask)
 		{
@@ -1236,7 +1207,7 @@ permute(const int64 val, const int64 isize, const int64 seed)
 		}
 
 		/* Random offset */
-		r = (uint64) getrand(&random_state2, 0, size - 1);
+		r = pg_prng_uint64(&state) % (size - 1);
 		v = (v + r) % size;
 	}
 
@@ -3831,7 +3802,7 @@ doLog(TState *thread, CState *st,
 	 * to the random sample.
 	 */
 	if (sample_rate != 0.0 &&
-		pg_erand48(thread->ts_sample_rs.xseed) > sample_rate)
+		pg_prng_double(&thread->ts_sample_rs) > sample_rate)
 		return;
 
 	/* should we aggregate the results or not? */
@@ -5770,12 +5741,11 @@ set_random_seed(const char *seed)
 
 	if (seed != NULL)
 		pg_log_info("setting random seed to %llu", (unsigned long long) iseed);
+
 	random_seed = iseed;
 
 	/* Fill base_random_sequence with low-order bits of seed */
-	base_random_sequence.xseed[0] = iseed & 0xFFFF;
-	base_random_sequence.xseed[1] = (iseed >> 16) & 0xFFFF;
-	base_random_sequence.xseed[2] = (iseed >> 32) & 0xFFFF;
+	pg_prng_seed(&base_random_sequence, (uint64_t) iseed);
 
 	return true;
 }
@@ -6449,9 +6419,7 @@ main(int argc, char **argv)
 	/* set default seed for hash functions */
 	if (lookupVariable(&state[0], "default_seed") == NULL)
 	{
-		uint64		seed =
-		((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) |
-		(((uint64) pg_jrand48(base_random_sequence.xseed) & 0xFFFFFFFF) << 32);
+		uint64		seed = pg_prng_uint64(&base_random_sequence);
 
 		for (i = 0; i < nclients; i++)
 			if (!putVariableInt(&state[i], "startup", "default_seed", (int64) seed))
diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl
index 69ffa595dd..feec9a377b 100644
--- a/src/bin/pgbench/t/001_pgbench_with_server.pl
+++ b/src/bin/pgbench/t/001_pgbench_with_server.pl
@@ -379,9 +379,9 @@ $node->pgbench(
 
 		# After explicit seeding, the four random checks (1-3,20) are
 		# deterministic
-		qr{command=1.: int 13\b},      # uniform random
-		qr{command=2.: int 116\b},     # exponential random
-		qr{command=3.: int 1498\b},    # gaussian random
+		qr{command=1.: int 17\b},      # uniform random
+		qr{command=2.: int 108\b},     # exponential random
+		qr{command=3.: int 1452\b},    # gaussian random
 		qr{command=4.: int 4\b},
 		qr{command=5.: int 5\b},
 		qr{command=6.: int 6\b},
@@ -448,6 +448,7 @@ $node->pgbench(
 		qr{command=109.: boolean true\b},
 		qr{command=110.: boolean true\b},
 		qr{command=111.: boolean true\b},
+		qr{command=113.: boolean true\b},
 	],
 	'pgbench expressions',
 	{
@@ -591,8 +592,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))
 -- actual values
 \set t debug(permute(:v, 1) = 0)
-\set t debug(permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1 and \
-             permute(0, 2, 5435) = 1 and permute(1, 2, 5435) = 0)
+\set t debug(permute(0, 2, 5431) = 1 and permute(1, 2, 5431) = 0 and \
+             permute(0, 2, 5432) = 0 and permute(1, 2, 5432) = 1)
+-- 63 bits tests for checking permute portability across architectures
+\set size debug(:max - 10)
+\set t debug(permute(:size-1, :size, 5432) = 4352241160009873020 and \
+             permute(:size-2, :size, 5432) = 4990004119691638710 and \
+             permute(:size-3, :size, 5432) = 5868029867256127549 and \
+             permute(:size-4, :size, 5432) = 1238830324886341378 and \
+             permute(:size-5, :size, 5432) = 7415914367102906897 and \
+             permute(:size-6, :size, 5432) = 3214501037984818995 and \
+             permute(:size-7, :size, 5432) =  600660351261124695)
 }
 	});
 
diff --git a/src/common/Makefile b/src/common/Makefile
index 880722fcf5..31c0dd366d 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -66,6 +66,7 @@ OBJS_COMMON = \
 	md5_common.o \
 	pg_get_line.o \
 	pg_lzcompress.o \
+	pg_prng.o \
 	pgfnames.o \
 	psprintf.o \
 	relpath.o \
diff --git a/src/common/pg_prng.c b/src/common/pg_prng.c
new file mode 100644
index 0000000000..3464e52459
--- /dev/null
+++ b/src/common/pg_prng.c
@@ -0,0 +1,185 @@
+/*
+ * Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * https://en.wikipedia.org/wiki/List_of_random_number_generators
+ *
+ * We chose a xoshiro 128 bit state to have a small, fast PRNG suitable
+ * for generating reasonably good quality uniform 64 bit data.
+ *
+ * About these generators: https://prng.di.unimi.it/
+ */
+
+#include "c.h"
+#include "common/pg_prng.h"
+#include "port/pg_bitutils.h"
+#include <math.h>
+
+/* global state */
+pg_prng_state	pg_global_prng_state;
+
+#define LAST_32BIT_MASK UINT64CONST(0x00000000FFFFFFFF)
+#define LAST_31BIT_MASK UINT64CONST(0x000000007FFFFFFF)
+#define LAST_63BIT_MASK UINT64CONST(0x7FFFFFFFFFFFFFFF)
+#define DMANTISSA_MASK  UINT64CONST(0x000FFFFFFFFFFFFF)
+
+/* 64-bits rotate left */
+static inline uint64
+rotl(const uint64 x, const int bits)
+{
+	return (x << bits) | (x >> (64 - bits));
+}
+
+/* another 64-bits generator is used for state initialization or iterations */
+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);
+}
+
+/* seed prng state from a 64 bits integer, ensuring non zero */
+void
+pg_prng_seed(pg_prng_state *state, uint64 seed)
+{
+	state->s0 = splitmix64(&seed);
+	state->s1 = splitmix64(&seed);
+}
+
+/* seed with 53 bits (mantissa & sign) from a float */
+void
+pg_prng_fseed(pg_prng_state *state, double fseed)
+{
+	uint64 seed = (int64) (((double) DMANTISSA_MASK) * fseed);
+	pg_prng_seed(state, seed);
+}
+
+/* strong random seeding */
+bool
+pg_prng_strong_seed(pg_prng_state *state)
+{
+	bool ok = pg_strong_random((void *) state, sizeof(pg_prng_state));
+
+	/* avoid zero with Donald Knuth's LCG parameters */
+	if (unlikely(state->s0 == 0 && state->s1 == 0))
+	{
+		/* should it warn that something is amiss if we get there? */
+		pg_prng_state def = PG_PRNG_DEFAULT_STATE;
+		*state = def;
+	}
+
+	return ok;
+}
+
+/* basic generator & state update used for all types */
+static uint64
+xoroshiro128ss(pg_prng_state *state)
+{
+	const 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;
+}
+
+/* return a uniform uint64 */
+uint64
+pg_prng_uint64(pg_prng_state *state)
+{
+	return xoroshiro128ss(state);
+}
+
+/* return a uniform uint64 in range [rmin, rmax], or rmin if range is empty */
+uint64
+pg_prng_uint64_range(pg_prng_state *state, uint64 rmin, uint64 rmax)
+{
+	uint64 val;
+
+	if (likely(rmax > rmin))
+	{
+		uint64 range = rmax - rmin;
+
+		/* bit position is between 0 and 63, so rshift >= 0 */
+		uint32 rshift = 63 - pg_leftmost_one_pos64(range);
+
+		/*
+		 * iterate with a bitmask rejection method.
+		 * the prng may advance by several states or none,
+		 * depending on the range value.
+		 */
+		do
+		{
+			val = xoroshiro128ss(state) >> rshift;
+		}
+		while (unlikely(val > range));
+	}
+	else
+		val = 0;
+
+	return rmin + val;
+}
+
+/* return a uniform int64 in [-2^63, 2^63) */
+int64
+pg_prng_int64(pg_prng_state *state)
+{
+	return (int64) xoroshiro128ss(state);
+}
+
+/* return a uniform int64 in [0, 2^63) */
+int64
+pg_prng_int64p(pg_prng_state *state)
+{
+	return (int64) xoroshiro128ss(state) & LAST_63BIT_MASK;
+}
+
+/* return a uniform uint32 */
+uint32
+pg_prng_uint32(pg_prng_state *state)
+{
+	const uint64 v = xoroshiro128ss(state);
+	return (uint32) (((v >> 32) ^ v) & LAST_32BIT_MASK);
+}
+
+/* return a uniform int32 in [-2^31, 2^31) */
+int32
+pg_prng_int32(pg_prng_state *state)
+{
+	const uint64 v = xoroshiro128ss(state);
+	return (int32) (((v >> 32) ^ v) & LAST_32BIT_MASK);
+}
+
+/* return a uniform int32 in [0, 2^31) */
+int32
+pg_prng_int32p(pg_prng_state *state)
+{
+	const uint64 v = xoroshiro128ss(state);
+	return (int32) (((v >> 32) ^ v) & LAST_31BIT_MASK);
+}
+
+/* return a uniform double in [0.0, 1.0) */
+double
+pg_prng_double(pg_prng_state *state)
+{
+	uint64 v = xoroshiro128ss(state);
+	return ldexp((double) (v & DMANTISSA_MASK), -52);
+}
+
+/* return a uniform bool */
+bool
+pg_prng_bool(pg_prng_state *state)
+{
+	uint64 v = xoroshiro128ss(state);
+	return (v & 1) == 1;
+}
diff --git a/src/include/common/pg_prng.h b/src/include/common/pg_prng.h
new file mode 100644
index 0000000000..2d0878e18d
--- /dev/null
+++ b/src/include/common/pg_prng.h
@@ -0,0 +1,39 @@
+/*-------------------------------------------------------------------------
+ *
+ * PRNG: internal Pseudo-Random Number Generator
+ *
+ * Copyright (c) 2021-2021, PostgreSQL Global Development Group
+ *
+ * src/include/common/pg_prng.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_PRNG_H
+#define PG_PRNG_H
+
+/* 128 bits state */
+typedef struct pg_prng_state {
+	uint64	s0, s1;
+} pg_prng_state;
+
+/* global pseudo-random state */
+extern PGDLLIMPORT pg_prng_state	pg_global_prng_state;
+
+/* arbitrarily use Donald Knuth's LCG constants for default state */
+#define PG_PRNG_DEFAULT_STATE \
+	{ UINT64CONST(0x5851F42D4C957F2D), UINT64CONST(0x14057B7EF767814F) }
+
+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_strong_seed(pg_prng_state *state);
+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 */
diff --git a/src/include/optimizer/geqo.h b/src/include/optimizer/geqo.h
index 24dcdfb6cc..b28e4f084a 100644
--- a/src/include/optimizer/geqo.h
+++ b/src/include/optimizer/geqo.h
@@ -22,6 +22,7 @@
 #ifndef GEQO_H
 #define GEQO_H
 
+#include "common/pg_prng.h"
 #include "nodes/pathnodes.h"
 #include "optimizer/geqo_gene.h"
 
@@ -72,8 +73,8 @@ extern double Geqo_seed;		/* 0 .. 1 */
  */
 typedef struct
 {
-	List	   *initial_rels;	/* the base relations we are joining */
-	unsigned short random_state[3]; /* state for pg_erand48() */
+	List		   *initial_rels;	/* the base relations we are joining */
+	pg_prng_state	random_state;   /* PRNG state */
 } GeqoPrivateData;
 
 
diff --git a/src/include/port.h b/src/include/port.h
index 49b4d38131..806fb795ed 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -362,11 +362,6 @@ extern int	gettimeofday(struct timeval *tp, struct timezone *tzp);
 #define pgoff_t off_t
 #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
 extern int	fls(int mask);
 #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);
 #endif
 
-#if !defined(HAVE_RANDOM)
-extern long random(void);
-#endif
-
 #ifndef HAVE_SETENV
 extern int	setenv(const char *name, const char *value, int overwrite);
 #endif
@@ -464,10 +455,6 @@ extern int	setenv(const char *name, const char *value, int overwrite);
 extern int	unsetenv(const char *name);
 #endif
 
-#ifndef HAVE_SRANDOM
-extern void srandom(unsigned int seed);
-#endif
-
 #ifndef HAVE_DLOPEN
 extern void *dlopen(const char *file, int mode);
 extern void *dlsym(void *handle, const char *symbol);
diff --git a/src/include/utils/sampling.h b/src/include/utils/sampling.h
index a58d14281b..c0b1c6a645 100644
--- a/src/include/utils/sampling.h
+++ b/src/include/utils/sampling.h
@@ -13,15 +13,14 @@
 #ifndef SAMPLING_H
 #define SAMPLING_H
 
+#include "common/pg_prng.h"
 #include "storage/block.h"		/* for typedef BlockNumber */
 
 
 /* Random generator for sampling code */
-typedef unsigned short SamplerRandomState[3];
-
 extern void sampler_random_init_state(long seed,
-									  SamplerRandomState randstate);
-extern double sampler_random_fract(SamplerRandomState randstate);
+									  pg_prng_state *randstate);
+extern double sampler_random_fract(pg_prng_state *randstate);
 
 /* Block sampling methods */
 
@@ -32,7 +31,7 @@ typedef struct
 	int			n;				/* desired sample size */
 	BlockNumber t;				/* current block number */
 	int			m;				/* blocks selected so far */
-	SamplerRandomState randstate;	/* random generator state */
+	pg_prng_state randstate;	/* random generator state */
 } BlockSamplerData;
 
 typedef BlockSamplerData *BlockSampler;
@@ -46,8 +45,9 @@ extern BlockNumber BlockSampler_Next(BlockSampler bs);
 
 typedef struct
 {
-	double		W;
-	SamplerRandomState randstate;	/* random generator state */
+	double			W;
+	pg_prng_state	randstate;	/* random generator state */
+	bool			randstate_initialized;
 } ReservoirStateData;
 
 typedef ReservoirStateData *ReservoirState;
diff --git a/src/port/Makefile b/src/port/Makefile
index 52dbf5783f..b3754d8940 100644
--- a/src/port/Makefile
+++ b/src/port/Makefile
@@ -42,7 +42,6 @@ OBJS = \
 	$(PG_CRC32C_OBJS) \
 	bsearch_arg.o \
 	chklocale.o \
-	erand48.o \
 	inet_net_ntop.o \
 	noblock.o \
 	path.o \
diff --git a/src/port/erand48.c b/src/port/erand48.c
deleted file mode 100644
index a82ea94220..0000000000
--- a/src/port/erand48.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/port/random.c b/src/port/random.c
deleted file mode 100644
index 2dd59a0829..0000000000
--- a/src/port/random.c
+++ /dev/null
@@ -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();
-}
diff --git a/src/port/srandom.c b/src/port/srandom.c
deleted file mode 100644
index cf1007b2ef..0000000000
--- a/src/port/srandom.c
+++ /dev/null
@@ -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);
-}
diff --git a/src/test/modules/test_bloomfilter/test_bloomfilter.c b/src/test/modules/test_bloomfilter/test_bloomfilter.c
index 96c5011428..c60220de7b 100644
--- a/src/test/modules/test_bloomfilter/test_bloomfilter.c
+++ b/src/test/modules/test_bloomfilter/test_bloomfilter.c
@@ -13,6 +13,7 @@
 #include "postgres.h"
 
 #include "fmgr.h"
+#include "common/pg_prng.h"
 #include "lib/bloomfilter.h"
 #include "miscadmin.h"
 
@@ -85,7 +86,7 @@ create_and_test_bloom(int power, int64 nelements, int callerseed)
 	 * 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 */
 	filter = bloom_create(nelements, bloom_work_mem, seed);
diff --git a/src/test/modules/test_integerset/test_integerset.c b/src/test/modules/test_integerset/test_integerset.c
index 21c6f49b37..6d251b110a 100644
--- a/src/test/modules/test_integerset/test_integerset.c
+++ b/src/test/modules/test_integerset/test_integerset.c
@@ -12,6 +12,7 @@
  */
 #include "postgres.h"
 
+#include "common/pg_prng.h"
 #include "fmgr.h"
 #include "lib/integerset.h"
 #include "miscadmin.h"
@@ -99,12 +100,20 @@ static void check_with_filler(IntegerSet *intset, uint64 x, uint64 value, uint64
 static void test_single_value_and_filler(uint64 value, uint64 filler_min, uint64 filler_max);
 static void test_huge_distances(void);
 
+static pg_prng_state	pr_state;
+
 /*
  * SQL-callable entry point to perform all tests.
  */
 Datum
 test_integerset(PG_FUNCTION_ARGS)
 {
+	if(unlikely(!pg_prng_strong_seed(&pr_state)))
+	{
+		elog(ERROR, "pg_prng_strong_seed() failed");
+		PG_RETURN_VOID();
+	}
+
 	/* Tests for various corner cases */
 	test_empty();
 	test_huge_distances();
@@ -248,8 +257,7 @@ test_pattern(const test_spec *spec)
 		 * only a small part of the integer space is used.  We would very
 		 * rarely hit values that are actually in the set.
 		 */
-		x = (pg_lrand48() << 31) | pg_lrand48();
-		x = x % (last_int + 1000);
+		x = pg_prng_uint64(&pr_state) % (last_int + 1000);
 
 		/* Do we expect this value to be present in the set? */
 		if (x >= last_int)
@@ -571,7 +579,7 @@ test_huge_distances(void)
 	 */
 	while (num_values < 1000)
 	{
-		val += pg_lrand48();
+		val += pg_prng_uint32(&pr_state);
 		values[num_values++] = val;
 	}
 
diff --git a/src/test/modules/test_rbtree/test_rbtree.c b/src/test/modules/test_rbtree/test_rbtree.c
index 713ebd1b26..d5496ced5b 100644
--- a/src/test/modules/test_rbtree/test_rbtree.c
+++ b/src/test/modules/test_rbtree/test_rbtree.c
@@ -14,6 +14,7 @@
 #include "postgres.h"
 
 #include "fmgr.h"
+#include "common/pg_prng.h"
 #include "lib/rbtree.h"
 #include "utils/memutils.h"
 
@@ -108,7 +109,7 @@ GetPermutation(int size)
 	 */
 	for (i = 1; i < size; i++)
 	{
-		int			j = random() % (i + 1);
+		int			j = pg_prng_uint32(&pg_global_prng_state) % (i + 1);
 
 		if (j < i)				/* avoid fetching undefined data if j=i */
 			permutation[i] = permutation[j];
@@ -320,7 +321,7 @@ testdelete(int size, int delsize)
 
 	for (i = 0; i < delsize; i++)
 	{
-		int			k = random() % size;
+		int			k = pg_prng_uint32(&pg_global_prng_state) % size;
 
 		while (chosen[k])
 			k = (k + 1) % size;
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 41172eab36..5a374a4727 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -99,9 +99,9 @@ sub mkvcbuild
 	$solution = CreateSolution($vsVersion, $config);
 
 	our @pgportfiles = qw(
-	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c random.c
-	  srandom.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
+	  chklocale.c explicit_bzero.c fls.c getpeereid.c getrusage.c inet_aton.c
+	  getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
+	  snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
 	  dirent.c dlopen.c getopt.c getopt_long.c link.c
 	  pread.c preadv.c pwrite.c pwritev.c pg_bitutils.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
 	  f2s.c file_perm.c file_utils.c hashfn.c ip.c jsonapi.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
-	  saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c username.c
-	  wait_error.c wchar.c);
+	  pg_get_line.c pg_lzcompress.c pg_prng.c pgfnames.c psprintf.c relpath.c
+	  rmtree.c saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c
+	  username.c wait_error.c wchar.c);
 
 	if ($solution->{options}->{openssl})
 	{
diff --git a/src/tools/testint128.c b/src/tools/testint128.c
index 71c345969a..446ddfac65 100644
--- a/src/tools/testint128.c
+++ b/src/tools/testint128.c
@@ -27,6 +27,7 @@
 #endif
 
 #include "common/int128.h"
+#include "common/pg_prng.h"
 
 /*
  * We assume the parts of this union are laid out compatibly.
@@ -61,27 +62,11 @@ my_int128_compare(int128 x, int128 y)
 	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.
  *
  * 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 fix the prng state (hmmm...).
  *
  * 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;
 
+	pg_prng_seed(&pg_global_prng_state, 0);
+
 	if (argc >= 2)
 		count = strtol(argv[1], NULL, 0);
 	else
@@ -97,9 +84,9 @@ main(int argc, char **argv)
 
 	while (count-- > 0)
 	{
-		int64		x = get_random_uint64();
-		int64		y = get_random_uint64();
-		int64		z = get_random_uint64();
+		int64		x = pg_prng_uint64(&pg_global_prng_state);
+		int64		y = pg_prng_uint64(&pg_global_prng_state);
+		int64		z = pg_prng_uint64(&pg_global_prng_state);
 		test128		t1;
 		test128		t2;
 
#58Tom Lane
tgl@sss.pgh.pa.us
In reply to: Fabien COELHO (#57)
Re: rand48 replacement

Fabien COELHO <coelho@cri.ensmp.fr> writes:

Good, but then you shouldn't write associated code as if that's still
a problem, because you'll cause other people to think it's still a
problem and write equally contorted code elsewhere. "v & 1" is a
transparent way of producing a bool, while this code isn't.

Some Xoshiro-family generators have "linear artifacts in the low bits",
Although Xoroshiro128** is supposed to be immune, I thought better to keep
away from these, and I could not see why the last bit would be better than
any other bit, so taking the first looked okay to me at least.

Meh. If we're going to trust the high bits more than the lower ones,
we should do so consistently; it makes no sense to do that in one
pg_prng.c function and not its siblings.

Pushed with that change and some others, notably:

* Rewrote a lot of the comments.
* Refactored so that pg_strong_random() is not called from pg_prng.c.
As it stood, that would result in pulling in OpenSSL in programs that
have no need of it. (ldexp() still creates a dependency on libm, but
I figured that was OK.)
* Changed a number of call sites that were using modulo reduction
to use pg_prng_uint64_range instead. Given the lengthy discussion
we had, it seems silly not to apply the conclusion everywhere.

regards, tom lane

#59Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Tom Lane (#58)
Re: rand48 replacement

Pushed with that change and some others, notably:

Thanks for the improvements and the push!

--
Fabien.