0

I have been trying to get this to pass valgrind leak check and also pass in 2 billion random numbers and divide them between the threads. I keep getting a seg fault once I get to 1 billion random numbers. Where am I allocating wrong or what am I doing wrong?

struct thread
{
    long long int threadID; //The thread id
    long long int operations; //The number of threads
};

void *generateThreads(void *ptr)
{
    struct thread *args = ptr;
    struct random_data *rdata = (struct random_data *) calloc(args->operations*64,sizeof(struct     random_data));
    char *statebuf = (char*) calloc(args->operations*64,BUFSIZE);
    long long int i;
    int32_t value;

    for(i = 0; i < args->operations; i++)
    {       
        initstate_r(args->threadID,&statebuf[i],BUFSIZE,&rdata[i]);
        random_r(rdata,&value);
    }

    if(DEBUG > 1)
        printf("I am thread %lld with thread id %X\n", args->threadID, (unsigned int) pthread_self());

    free(rdata);
    free(statebuf);
    pthread_exit(NULL);
}

int main(int argc, char **argv)
{
    long long int numRandoms;
    long long int numThreads;
    double timeStart = 0;
    double timeElapsed = 0;
    pthread_t *tid;
    struct thread args;

    if (argc != 3)
    {
         fprintf(stderr, "Usage: %s <Number of Randoms> <Number of Threads>\n" ,argv[0]);
         exit(1);
    }

   /* Assign the arg values to appropriate variables */
   sscanf(argv[1],"%lld",&numRandoms); /* lld for long long int */
   sscanf(argv[2],"%lld",&numThreads); /* lld for long long int */

   /* Number of threads must be less than or equal to the number of random numbers */
   if(numRandoms < numThreads)
   {
       fprintf(stderr,"Number of threads must be less than or equal to the number of random numers.\n");
       exit(1);
   }

   /*Start*/
   long long int i;
   args.operations = numRandoms/numThreads;
   timeStart = getMilliSeconds();
   tid = (pthread_t *) calloc(numThreads,sizeof(pthread_t));

   /* value is the thread id, creating threads */
   for(i = 0; i < numThreads; i++)
   {
       args.threadID = i;
       pthread_create(&tid[i],NULL,generateThreads,(void *) &args);
   }

   /* Joining the threads */
   for(i = 0; i < numThreads; i++)
   {
       pthread_join(tid[i],NULL);
   }

   /*Output*/
   timeElapsed = getMilliSeconds() - timeStart;
   printf("%lf\n",(double)(timeElapsed/1000.0));
   free(tid);
   fflush(stdout);
   exit(0);
}
b4hand
  • 9,550
  • 4
  • 44
  • 49
  • 1
    You haven't shown us what `struct random_data` is, so we don't know how big it is. But, most likely the argument to `calloc()` exceeds the size that you can allocate as a single block on your computer. Another important question: Are you building this as a 32-bit or 64-bit program? – Greg Hewgill Dec 05 '14 at 00:45
  • Can't you generate random numbers on the fly? `rand()` returns a 15-bit number but you can easily get a bigger one by, for example, `r = rand() + (rand()<<15);` – Weather Vane Dec 05 '14 at 00:58
  • (off-topic) isn't a long long overkill for number of threads ? I understand that some people want to make their code future proof. – dvhh Dec 05 '14 at 02:11
  • Why the `*64` in the `calloc` calls? – JS1 Dec 05 '14 at 03:13
  • @GregHewgill local machine is 64 using OSX but I'm using ssh to run the program through my school's nodes... struct random_data was listed on the man page (see link) [man page](http://man7.org/linux/man-pages/man3/random_r.3.html) – Hilary Johnson Dec 05 '14 at 05:54
  • @JS1 I was trying to follow examples that I found online that had a similar issue. – Hilary Johnson Dec 05 '14 at 05:58
  • @WeatherVane We had to create our own thread id and use that as the seed for the random number and use a thread safe random number generator. – Hilary Johnson Dec 05 '14 at 05:59
  • @dvhh You are right, it is overkill. I was just trying different things to see if there was something I didn't understand correctly and that was one of them. – Hilary Johnson Dec 05 '14 at 06:00
  • I think you might be running out of memory. Try without the `*64` because you don't appear to need it. – JS1 Dec 05 '14 at 06:01
  • @JS1 still running into the same issue without the *64. Any other thoughts? – Hilary Johnson Dec 05 '14 at 06:07
  • 1
    Without knowing what `initstate_r` or `random_r` are, I don't know. You're going to have to debug your program and try to figure out where it is failing. Make sure to check the return values of all functions such as `calloc` and make sure none of them are returning NULL or an error value. – JS1 Dec 05 '14 at 06:15
  • @JS1 I have added return statements for debugging and everything is returning as expected until 1 billion numbers and it gives me a seg fault. The debugger tool has not helped me yet but I'm still working on it. The initstate_r and random_r information can be found at [man page](http://man7.org/linux/man-pages/man3/random_r.3.html) fyi – Hilary Johnson Dec 05 '14 at 17:12

2 Answers2

1

OK I figured out what you were trying to do. The problem was that whatever code you copied from used initstate_r in main to set up the states for all threads. It called initstate_r once per thread to set up the rng for that thread. But you copied that loop into each thread, so you were calling initstate_r many times per thread which is useless. The *64 was there originally to make each state occupy 64 bytes in order to keep them on separate cache lines. You probably were referring to this stackoverflow question.

Here is your function rewritten to make much more sense:

void *generateThreads(void *ptr)
{
    struct thread *args = ptr;
    struct random_data *rdata = calloc(1,sizeof(struct random_data));
    char statebuf[BUFSIZE];
    long long int i;
    int32_t value;

    initstate_r((int) pthread_self(),statebuf,BUFSIZE,rdata);
    for(i = 0; i < args->operations; i++)
    {
        random_r(rdata,&value);
        if(DEBUG > 1)
            printf("%d\n", value);
    }

    if(DEBUG > 1)
        printf("I am thread %lld with thread id %X\n", args->threadID, (unsigned int) pthread_self());

    free(rdata);
    pthread_exit(NULL);
}

By the way, the way you pass your arguments to your threads is wrong. You pass the same args to each thread, which means they are sharing the same args structure, which means they each share the same args->threadID. You should instead pass each thread its own args structure.

Community
  • 1
  • 1
JS1
  • 4,745
  • 1
  • 14
  • 19
0

My answer to question link provides thread safe pseudo-random number generator designed for __uint64/__uint128 integers using xorshift algorithm.

Additional properties:

  • shared-reentrant
  • lock-free
  • thread-safe
  • ultrafast
  • seeded from two variant sources of enthropy
Community
  • 1
  • 1
Dawid Szymański
  • 775
  • 6
  • 15