ellipsix informatics
 
 
Trackback URI
2009
Mar
10

How to use random_r

Bookmark and Share

The random_r function, a GNU addition to the standard C library, allows you to use a pseudorandom number generator whose persistent state is stored in an area of memory managed by your program, rather than the system. It's useful when you want to generate pseudorandom numbers quickly, without taking the time to synchronize access to the PRNG state, among other uses.

This function is a little esoteric, judging by the fact that I couldn't find any clear example of how to use it in a multithreaded program when I was trying to fix up a simulation today. Here's an example (error checking omitted):

#include <pthread.h>
#include <stdlib.h>
#define NTHREADS 4
#define PRNG_BUFSZ 32

PRNG_BUFSZ stands for "pseudorandom number generator buffer size" which is exactly what it sounds like, the size (in bytes) of the state buffer allocated to each PRNG. Sure, I could have written BUFFER_SIZE but C programmers write in this abbreviated Hebrew-like code so much I'm surprised the language still includes vowels. Anyway, whatever you call it, this number must be at least 8. The larger this size, the more complex the pseudorandom number sequence will be.

void* thread_run(void* arg) {
    int r1;
    /* think happy thoughts */
    random_r((struct random_data*)arg, &r1);
    /* this runs in each thread, so many happy thoughts at the same time yay :-) */
}

int main(int argc, char** argv) {
    struct random_data* rand_states;
    char* rand_statebufs;
    pthread_t* thread_ids;
    int t = 0;
    /* allocate memory */
    rand_states = (struct random_data*)calloc(NTHREADS, sizeof(struct random_data));

The struct random_data structures must be zeroed out before you go trying to initialize states; thus I use calloc.

    rand_statebufs = (char*)calloc(NTHREADS, PRNG_BUFSZ);

The state buffer stores extra information about the PRNG state that isn't included in the struct random_data, at least not directly. Each struct random_data does include a pointer to its corresponding state buffer, though. This means that the state buffer for a given PRNG (that is, for a given struct random_data) must remain allocated for as long as the PRNG continues to be used. You could declare these on the stack as long as you are sure that the PRNG will never be used after the function returns — which can be hard to guarantee in a multithreaded program. So DON'T DO IT (well, if the one function runs for the entire length of the program, it's probably okay; or you could make them static local variables, which persist beyond the life of the function). Asynchronous execution + passing stack pointers == Skynet == DOOM.

    /* still in main() */
    thread_ids = (pthread_t*)calloc(NTHREADS, sizeof(pthread_t));
    /* create threads */
    for (t = 0; t < NTHREADS; t++) {
        /* for each thread, initialize a PRNG (the seed is the first argument) */
        initstate_r(random(), &rand_statebufs[t], PRNG_BUFSZ, &rand_states[t]);
        /* and create the thread to generate random numbers from that PRNG */
        pthread_create(&thread_ids[t], NULL, &thread_run, &rand_states[t]); /* last argument gets passed to thread_run */
    }
    for (t = 0; t < NTHREADS; t++) {
        pthread_join(thread_ids[t], NULL);
    }
    free(thread_ids);
    free(rand_states);
    free(rand_statebufs);
}
blog comments powered by Disqus