Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 4092319

Browse files
committed
pgbench: introduce a RandomState struct
This becomes useful when used to retry a transaction after a serialization error or deadlock abort. (We don't yet have that feature, but this is preparation for it.) While at it, use separate random state for thread administratrivia such as deciding which script to run, how long to delay for throttling, or whether to log a message when sampling; this not only makes these tasks independent of each other, but makes the actual thread run deterministic. Author: Marina Polyakova Reviewed-by: Fabien Coelho Discussion: https://postgr.es/m/72a0d590d6ba06f242d75c2e641820ec@postgrespro.ru
1 parent a7aa608 commit 4092319

File tree

1 file changed

+74
-32
lines changed

1 file changed

+74
-32
lines changed

src/bin/pgbench/pgbench.c

Lines changed: 74 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,14 @@ typedef struct StatsData
279279
SimpleStats lag;
280280
} StatsData;
281281

282+
/*
283+
* Struct to keep random state.
284+
*/
285+
typedef struct RandomState
286+
{
287+
unsigned short xseed[3];
288+
} RandomState;
289+
282290
/*
283291
* Connection state machine states.
284292
*/
@@ -360,6 +368,12 @@ typedef struct
360368
ConnectionStateEnum state; /* state machine's current state. */
361369
ConditionalStack cstack; /* enclosing conditionals state */
362370

371+
/*
372+
* Separate randomness for each client. This is used for random functions
373+
* PGBENCH_RANDOM_* during the execution of the script.
374+
*/
375+
RandomState cs_func_rs;
376+
363377
int use_file; /* index in sql_script for this client */
364378
int command; /* command number in script */
365379

@@ -419,7 +433,16 @@ typedef struct
419433
pthread_t thread; /* thread handle */
420434
CState *state; /* array of CState */
421435
int nstate; /* length of state[] */
422-
unsigned short random_state[3]; /* separate randomness for each thread */
436+
437+
/*
438+
* Separate randomness for each thread. Each thread option uses its own
439+
* random state to make all of them independent of each other and therefore
440+
* deterministic at the thread level.
441+
*/
442+
RandomState ts_choose_rs; /* random state for selecting a script */
443+
RandomState ts_throttle_rs; /* random state for transaction throttling */
444+
RandomState ts_sample_rs; /* random state for log sampling */
445+
423446
int64 throttle_trigger; /* previous/next throttling (us) */
424447
FILE *logfile; /* where to log, or NULL */
425448
ZipfCache zipf_cache; /* for thread-safe zipfian random number
@@ -769,9 +792,20 @@ strtodouble(const char *str, bool errorOK, double *dv)
769792
return true;
770793
}
771794

795+
/*
796+
* Initialize a random state struct.
797+
*/
798+
static void
799+
initRandomState(RandomState *random_state)
800+
{
801+
random_state->xseed[0] = random();
802+
random_state->xseed[1] = random();
803+
random_state->xseed[2] = random();
804+
}
805+
772806
/* random number generator: uniform distribution from min to max inclusive */
773807
static int64
774-
getrand(TState *thread, int64 min, int64 max)
808+
getrand(RandomState *random_state, int64 min, int64 max)
775809
{
776810
/*
777811
* Odd coding is so that min and max have approximately the same chance of
@@ -782,7 +816,7 @@ getrand(TState *thread, int64 min, int64 max)
782816
* protected by a mutex, and therefore a bottleneck on machines with many
783817
* CPUs.
784818
*/
785-
return min + (int64) ((max - min + 1) * pg_erand48(thread->random_state));
819+
return min + (int64) ((max - min + 1) * pg_erand48(random_state->xseed));
786820
}
787821

788822
/*
@@ -791,7 +825,8 @@ getrand(TState *thread, int64 min, int64 max)
791825
* value is exp(-parameter).
792826
*/
793827
static int64
794-
getExponentialRand(TState *thread, int64 min, int64 max, double parameter)
828+
getExponentialRand(RandomState *random_state, int64 min, int64 max,
829+
double parameter)
795830
{
796831
double cut,
797832
uniform,
@@ -801,7 +836,7 @@ getExponentialRand(TState *thread, int64 min, int64 max, double parameter)
801836
Assert(parameter > 0.0);
802837
cut = exp(-parameter);
803838
/* erand in [0, 1), uniform in (0, 1] */
804-
uniform = 1.0 - pg_erand48(thread->random_state);
839+
uniform = 1.0 - pg_erand48(random_state->xseed);
805840

806841
/*
807842
* inner expression in (cut, 1] (if parameter > 0), rand in [0, 1)
@@ -814,7 +849,8 @@ getExponentialRand(TState *thread, int64 min, int64 max, double parameter)
814849

815850
/* random number generator: gaussian distribution from min to max inclusive */
816851
static int64
817-
getGaussianRand(TState *thread, int64 min, int64 max, double parameter)
852+
getGaussianRand(RandomState *random_state, int64 min, int64 max,
853+
double parameter)
818854
{
819855
double stdev;
820856
double rand;
@@ -842,8 +878,8 @@ getGaussianRand(TState *thread, int64 min, int64 max, double parameter)
842878
* are expected in (0, 1] (see
843879
* https://en.wikipedia.org/wiki/Box-Muller_transform)
844880
*/
845-
double rand1 = 1.0 - pg_erand48(thread->random_state);
846-
double rand2 = 1.0 - pg_erand48(thread->random_state);
881+
double rand1 = 1.0 - pg_erand48(random_state->xseed);
882+
double rand2 = 1.0 - pg_erand48(random_state->xseed);
847883

848884
/* Box-Muller basic form transform */
849885
double var_sqrt = sqrt(-2.0 * log(rand1));
@@ -873,7 +909,7 @@ getGaussianRand(TState *thread, int64 min, int64 max, double parameter)
873909
* not be one.
874910
*/
875911
static int64
876-
getPoissonRand(TState *thread, double center)
912+
getPoissonRand(RandomState *random_state, double center)
877913
{
878914
/*
879915
* Use inverse transform sampling to generate a value > 0, such that the
@@ -882,7 +918,7 @@ getPoissonRand(TState *thread, double center)
882918
double uniform;
883919

884920
/* erand in [0, 1), uniform in (0, 1] */
885-
uniform = 1.0 - pg_erand48(thread->random_state);
921+
uniform = 1.0 - pg_erand48(random_state->xseed);
886922

887923
return (int64) (-log(uniform) * center + 0.5);
888924
}
@@ -960,7 +996,7 @@ zipfFindOrCreateCacheCell(ZipfCache *cache, int64 n, double s)
960996
* Luc Devroye, p. 550-551, Springer 1986.
961997
*/
962998
static int64
963-
computeIterativeZipfian(TState *thread, int64 n, double s)
999+
computeIterativeZipfian(RandomState *random_state, int64 n, double s)
9641000
{
9651001
double b = pow(2.0, s - 1.0);
9661002
double x,
@@ -971,8 +1007,8 @@ computeIterativeZipfian(TState *thread, int64 n, double s)
9711007
while (true)
9721008
{
9731009
/* random variates */
974-
u = pg_erand48(thread->random_state);
975-
v = pg_erand48(thread->random_state);
1010+
u = pg_erand48(random_state->xseed);
1011+
v = pg_erand48(random_state->xseed);
9761012

9771013
x = floor(pow(u, -1.0 / (s - 1.0)));
9781014

@@ -990,10 +1026,11 @@ computeIterativeZipfian(TState *thread, int64 n, double s)
9901026
* Jim Gray et al, SIGMOD 1994
9911027
*/
9921028
static int64
993-
computeHarmonicZipfian(TState *thread, int64 n, double s)
1029+
computeHarmonicZipfian(ZipfCache *zipf_cache, RandomState *random_state,
1030+
int64 n, double s)
9941031
{
995-
ZipfCell *cell = zipfFindOrCreateCacheCell(&thread->zipf_cache, n, s);
996-
double uniform = pg_erand48(thread->random_state);
1032+
ZipfCell *cell = zipfFindOrCreateCacheCell(zipf_cache, n, s);
1033+
double uniform = pg_erand48(random_state->xseed);
9971034
double uz = uniform * cell->harmonicn;
9981035

9991036
if (uz < 1.0)
@@ -1005,17 +1042,17 @@ computeHarmonicZipfian(TState *thread, int64 n, double s)
10051042

10061043
/* random number generator: zipfian distribution from min to max inclusive */
10071044
static int64
1008-
getZipfianRand(TState *thread, int64 min, int64 max, double s)
1045+
getZipfianRand(ZipfCache *zipf_cache, RandomState *random_state, int64 min,
1046+
int64 max, double s)
10091047
{
10101048
int64 n = max - min + 1;
10111049

10121050
/* abort if parameter is invalid */
10131051
Assert(s > 0.0 && s != 1.0 && s <= MAX_ZIPFIAN_PARAM);
10141052

1015-
10161053
return min - 1 + ((s > 1)
1017-
? computeIterativeZipfian(thread, n, s)
1018-
: computeHarmonicZipfian(thread, n, s));
1054+
? computeIterativeZipfian(random_state, n, s)
1055+
: computeHarmonicZipfian(zipf_cache, random_state, n, s));
10191056
}
10201057

10211058
/*
@@ -2310,7 +2347,7 @@ evalStandardFunc(TState *thread, CState *st,
23102347
if (func == PGBENCH_RANDOM)
23112348
{
23122349
Assert(nargs == 2);
2313-
setIntValue(retval, getrand(thread, imin, imax));
2350+
setIntValue(retval, getrand(&st->cs_func_rs, imin, imax));
23142351
}
23152352
else /* gaussian & exponential */
23162353
{
@@ -2332,7 +2369,8 @@ evalStandardFunc(TState *thread, CState *st,
23322369
}
23332370

23342371
setIntValue(retval,
2335-
getGaussianRand(thread, imin, imax, param));
2372+
getGaussianRand(&st->cs_func_rs,
2373+
imin, imax, param));
23362374
}
23372375
else if (func == PGBENCH_RANDOM_ZIPFIAN)
23382376
{
@@ -2344,7 +2382,9 @@ evalStandardFunc(TState *thread, CState *st,
23442382
return false;
23452383
}
23462384
setIntValue(retval,
2347-
getZipfianRand(thread, imin, imax, param));
2385+
getZipfianRand(&thread->zipf_cache,
2386+
&st->cs_func_rs,
2387+
imin, imax, param));
23482388
}
23492389
else /* exponential */
23502390
{
@@ -2357,7 +2397,8 @@ evalStandardFunc(TState *thread, CState *st,
23572397
}
23582398

23592399
setIntValue(retval,
2360-
getExponentialRand(thread, imin, imax, param));
2400+
getExponentialRand(&st->cs_func_rs,
2401+
imin, imax, param));
23612402
}
23622403
}
23632404

@@ -2652,7 +2693,7 @@ chooseScript(TState *thread)
26522693
if (num_scripts == 1)
26532694
return 0;
26542695

2655-
w = getrand(thread, 0, total_weight - 1);
2696+
w = getrand(&thread->ts_choose_rs, 0, total_weight - 1);
26562697
do
26572698
{
26582699
w -= sql_script[i++].weight;
@@ -2846,7 +2887,7 @@ doCustom(TState *thread, CState *st, StatsData *agg)
28462887
* away.
28472888
*/
28482889
Assert(throttle_delay > 0);
2849-
wait = getPoissonRand(thread, throttle_delay);
2890+
wait = getPoissonRand(&thread->ts_throttle_rs, throttle_delay);
28502891

28512892
thread->throttle_trigger += wait;
28522893
st->txn_scheduled = thread->throttle_trigger;
@@ -2880,7 +2921,8 @@ doCustom(TState *thread, CState *st, StatsData *agg)
28802921
{
28812922
processXactStats(thread, st, &now, true, agg);
28822923
/* next rendez-vous */
2883-
wait = getPoissonRand(thread, throttle_delay);
2924+
wait = getPoissonRand(&thread->ts_throttle_rs,
2925+
throttle_delay);
28842926
thread->throttle_trigger += wait;
28852927
st->txn_scheduled = thread->throttle_trigger;
28862928
}
@@ -3423,7 +3465,7 @@ doLog(TState *thread, CState *st,
34233465
* to the random sample.
34243466
*/
34253467
if (sample_rate != 0.0 &&
3426-
pg_erand48(thread->random_state) > sample_rate)
3468+
pg_erand48(thread->ts_sample_rs.xseed) > sample_rate)
34273469
return;
34283470

34293471
/* should we aggregate the results or not? */
@@ -4851,7 +4893,6 @@ set_random_seed(const char *seed)
48514893
return true;
48524894
}
48534895

4854-
48554896
int
48564897
main(int argc, char **argv)
48574898
{
@@ -5465,6 +5506,7 @@ main(int argc, char **argv)
54655506
for (i = 0; i < nclients; i++)
54665507
{
54675508
state[i].cstack = conditional_stack_create();
5509+
initRandomState(&state[i].cs_func_rs);
54685510
}
54695511

54705512
if (debug)
@@ -5598,9 +5640,9 @@ main(int argc, char **argv)
55985640
thread->state = &state[nclients_dealt];
55995641
thread->nstate =
56005642
(nclients - nclients_dealt + nthreads - i - 1) / (nthreads - i);
5601-
thread->random_state[0] = random();
5602-
thread->random_state[1] = random();
5603-
thread->random_state[2] = random();
5643+
initRandomState(&thread->ts_choose_rs);
5644+
initRandomState(&thread->ts_throttle_rs);
5645+
initRandomState(&thread->ts_sample_rs);
56045646
thread->logfile = NULL; /* filled in later */
56055647
thread->latency_late = 0;
56065648
thread->zipf_cache.nb_cells = 0;

0 commit comments

Comments
 (0)