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:
@ -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]);
|
||||
|
Reference in New Issue
Block a user