I have two implementations of counting pi with Monte-Carlo method: with and without threads. Implementation without threads working just fine, but method with threads have problems with accuracy and perfomance. Here is code:
Without threads:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
srand(time(NULL));
unsigned long N = 0, Nin = 0;
float x,y;
while(N < 2E+9)
{
x = rand()/((float)RAND_MAX + 1.0)*10.0 - 5.0;
y = rand()/((float)RAND_MAX + 1.0)*10.0 - 5.0;
if(x*x + y*y < 25.0) Nin += 1;
N++;
}
long double pi = 4.0 * (long double)Nin / (long double)N;
printf("\tPi1: %.20Lf\n\t%lu %lu\n", pi, Nin, N);
return 0;
}
And with threads:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
typedef struct
{
unsigned long Nin;
unsigned long N;
} nums;
void pi_counter(nums* a)
{
float x,y;
unsigned int N = 0, Nin = 0;
while(N < 1E+9)
{
x = rand()/((float)RAND_MAX + 1.0)*10.0 - 5.0;
y = rand()/((float)RAND_MAX + 1.0)*10.0 - 5.0;
if(x*x + y*y < 25.0) Nin++;
N++;
}
a -> Nin += Nin;
a -> N += N;
}
int main()
{
pthread_t thread1, thread2, thread3;
nums a;
srand(time(NULL));
pthread_create( &thread1, NULL, pi_counter, &a );
pthread_create( &thread2, NULL, pi_counter, &a );
pthread_join( thread1, NULL );
pthread_join( thread2, NULL );
long double pi = 4.0 * (long double)a.Nin / (long double)a.N;
printf("\tPi2: %.20Lf\n\t%lu %lu\n", pi, a.Nin, a.N);
return 0;
}
Results:
$ time ./pi2
Pi2: 3.14147154999999999995
1570735775 2000000000
real 1m1.927s
user 1m23.624s
sys 0m0.139s
$ time ./pi
Pi1: 3.14158868600000000006
1570794343 2000000000
real 0m49.956s
user 0m49.887s
sys 0m0.022s
Where is my mistake?
rand
is not thread-safe; simultaneously using it in multiple threads will result in undefined behavior. You can either wrap it with a function that acquires and holds a mutex while calling rand
, or you can use rand_r
or (better yet) write a decent PRNG to use in its place.
Besides the other answers, in the following code
a -> Nin += Nin;
a -> N += N;
a is shared but not guarded by mutex, resulting in wrong addition. Though you may have not encountered this problem, but you will eventually.
Your rand()
concurrently in threads will result same numbers sequence, because you get other results but algorithm is fine (is probabilistic, nothing is guaranteed). Why same sequence? Because seed rand instance is per process, thread is process to but lightweight.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With