diff options
| author | Robert Haas <rhaas@postgresql.org> | 2011-08-03 16:26:40 -0400 |
|---|---|---|
| committer | Robert Haas <rhaas@postgresql.org> | 2011-08-03 16:26:40 -0400 |
| commit | 4af43ee3f165c8e4b332a7e680a44f4b7ba2d3c1 (patch) | |
| tree | c75980425aaaae3144a458871139cdec9f5537ab /contrib/pgbench | |
| parent | ac36e6f71f197540b8ee83c97f338ae5e5163f30 (diff) | |
| download | postgresql-4af43ee3f165c8e4b332a7e680a44f4b7ba2d3c1.tar.gz | |
Make pgbench use erand48() rather than random().
glibc renders random() thread-safe by wrapping a futex lock around it;
testing reveals that this limits the performance of pgbench on machines
with many CPU cores. Rather than switching to random_r(), which is
only available on GNU systems and crashes unless you use undocumented
alchemy to initialize the random state properly, switch to our built-in
implementation of erand48(), which is both thread-safe and concurrent.
Since the list of reasons not to use the operating system's erand48()
is getting rather long, rename ours to pg_erand48() (and similarly
for our implementations of lrand48() and srand48()) and just always
use those. We were already doing this on Cygwin anyway, and the
glibc implementation is not quite thread-safe, so pgbench wouldn't
be able to use that either.
Per discussion with Tom Lane.
Diffstat (limited to 'contrib/pgbench')
| -rw-r--r-- | contrib/pgbench/pgbench.c | 32 |
1 files changed, 15 insertions, 17 deletions
diff --git a/contrib/pgbench/pgbench.c b/contrib/pgbench/pgbench.c index 9d57436aeb..56dab6192d 100644 --- a/contrib/pgbench/pgbench.c +++ b/contrib/pgbench/pgbench.c @@ -198,6 +198,7 @@ typedef struct instr_time start_time; /* thread start time */ instr_time *exec_elapsed; /* time spent executing cmds (per Command) */ int *exec_count; /* number of cmd executions (per Command) */ + unsigned short random_state[3]; /* separate randomness for each thread */ } TState; #define INVALID_THREAD ((pthread_t) 0) @@ -380,13 +381,18 @@ usage(const char *progname) /* random number generator: uniform distribution from min to max inclusive */ static int -getrand(int min, int max) +getrand(TState *thread, int min, int 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 + (int) (((max - min + 1) * (double) random()) / (MAX_RANDOM_VALUE + 1.0)); + return min + (int) ((max - min + 1) * pg_erand48(thread->random_state)); } /* call PQexec() and exit() on failure */ @@ -901,7 +907,7 @@ top: if (commands[st->state] == NULL) { st->state = 0; - st->use_file = getrand(0, num_files - 1); + st->use_file = getrand(thread, 0, num_files - 1); commands = sql_files[st->use_file]; } } @@ -1068,9 +1074,9 @@ top: } #ifdef DEBUG - printf("min: %d max: %d random: %d\n", min, max, getrand(min, max)); + printf("min: %d max: %d random: %d\n", min, max, getrand(thread, min, max)); #endif - snprintf(res, sizeof(res), "%d", getrand(min, max)); + snprintf(res, sizeof(res), "%d", getrand(thread, min, max)); if (!putVariable(st, argv[0], argv[1], res)) { @@ -2242,6 +2248,9 @@ main(int argc, char **argv) thread->tid = i; thread->state = &state[nclients / nthreads * i]; thread->nstate = nclients / nthreads; + thread->random_state[0] = random(); + thread->random_state[1] = random(); + thread->random_state[2] = random(); if (is_latencies) { @@ -2384,7 +2393,7 @@ threadRun(void *arg) Command **commands = sql_files[st->use_file]; int prev_ecnt = st->ecnt; - st->use_file = getrand(0, num_files - 1); + st->use_file = getrand(thread, 0, num_files - 1); if (!doCustom(thread, st, &result->conn_time, logfile)) remains--; /* I've aborted */ @@ -2583,17 +2592,6 @@ pthread_create(pthread_t *thread, if (duration > 0) setalarm(duration); - /* - * Set a different random seed in each child process. Otherwise they all - * inherit the parent's state and generate the same "random" sequence. (In - * the threaded case, the different threads will obtain subsets of the - * output of a single random() sequence, which should be okay for our - * purposes.) - */ - INSTR_TIME_SET_CURRENT(start_time); - srandom(((unsigned int) INSTR_TIME_GET_MICROSEC(start_time)) + - ((unsigned int) getpid())); - ret = start_routine(arg); write(th->pipes[1], ret, sizeof(TResult)); close(th->pipes[1]); |
