1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-28 23:42:10 +03:00

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.
This commit is contained in:
Robert Haas
2011-08-03 16:26:40 -04:00
parent ac36e6f71f
commit 4af43ee3f1
12 changed files with 40 additions and 62 deletions

View File

@ -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]);