Permalink
Browse files

Created a multithreading version of the code

  • Loading branch information...
5N44P committed Mar 14, 2017
1 parent f6ccf10 commit 61233671a8487342045df1335eea762043460db5
Showing with 91 additions and 0 deletions.
  1. +91 −0 multithreading.c
@@ -0,0 +1,91 @@
/* based on the work of https://randu.org/tutorials/threads/ */
/* pthread implementation of montecarlo method to calculate pi */
/* for windows, non portable */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <windows.h>
#define NUM_THREADS 8 // number of threads running
#define THREAD_POINTS 12500000 // points per thread
#define RAND_MAX_EN (((RAND_MAX+1.0)*(RAND_MAX+1.0))-1.0)
#define RAND_MAX_P1 RAND_MAX+1.0
// data passed to the thread5
typedef struct _thread_data_t {
int tid; // thread id given by the order of creation
int belongs; // stores here the number of points that belong the circle
} thread_data_t;
// thread function
void *thr_func(void *arg) {
thread_data_t *data = (thread_data_t *)arg;
// making sure that every thread gets a different set of ra
srand(time(NULL)*data->tid);

This comment has been minimized.

@carmelopellegrino

carmelopellegrino Mar 14, 2017

Contributor

rand is neither thread-safe nor reentrant, because it stores an internal state that is not thread-local. This state is, partially, set by srand. The net effect to use srand in a thread is that the same seed is set for all threads. In principle, calling srand from multiple threads results in an undefined behaviour, but provided the atomicity of integers on x86 CPUs, I'd say that the last value is the one effectively used. Then, all subsequent calls to rand will interleave, simulating the effect of a different seed for each thread (also here, undefined behaviour is the answer, but we are talking about random numbers :P ). The correct pattern here should be to set the seed only once in the main thread and to use the rand_r function in place of all multi-threaded rand calls.

ref:
http://stackoverflow.com/questions/6161322/using-stdlibs-rand-from-multiple-threads

printf("Thread %d is running\n", data->tid); // just to say hi
static const unsigned long int randmaxen=(((RAND_MAX+1.0)*(RAND_MAX+1.0))-1.0);
double x,y,x1,x2,y1,y2;
unsigned int executed=0; // stores the number of executed points
data->belongs=0; //initalizes to 0 the counter of matched points
// go through each point
for(executed;executed<THREAD_POINTS;executed++){

This comment has been minimized.

@carmelopellegrino

carmelopellegrino Mar 14, 2017

Contributor

Just a funny style comment: Here you can use the "operator tends to" :P

int executing = THREAD_POINTS;
while (executing --> 0) {...}
x1=rand();
x2=rand();
y1=rand();
y2=rand();
//the enhanced random formula
x=x1*RAND_MAX_P1+x2;
x=x/randmaxen;
y=y1*RAND_MAX_P1+y2;
y=y/randmaxen;
//chech if it belongs to the circle or not
if(x*x+y*y<1){
data->belongs++;
}
}
// say goodbye. the datas are stored in the thr_data array
pthread_exit(NULL);
}
int main(int argc, char **argv) {
pthread_t thr[NUM_THREADS];
int i, rc;
// for calculating the computational time
clock_t time = clock();

This comment has been minimized.

@carmelopellegrino

carmelopellegrino Mar 14, 2017

Contributor

Even if clock is a standard C function and it is present in both Win and Linux, its implementation can change from one architecture to another as well as on an OS basis. Indeed, in the Visual Studio implementation the clock function provides the wall clock time, in unit of clock cycles, elapsed from the start of the process. In Linux, it provides an estimate of the CPU time used, which may comprehend CPU time spent in threads and child processes, and not even starting from 0 at the process start. It is not strictly true that on Linux the program performs worse.

A funny experiment can be to run the following code on Win to see the output:

#include <stdio.h>
#include <time.h>

int main()
{

  clock_t const start = clock();

  sleep(10); // or whatever corresponding function on Win that does nothing but sleeping

  clock_t const stop = clock();

  printf("(stop - start) = %lf\n", (stop - start) / (1.*CLOCKS_PER_SEC));
}

ref:

This comment has been minimized.

@5N44P

5N44P Mar 14, 2017

Owner

I've already pointed out that in the last commit (here 6d04873)
Nevertheless when i say that the code on linux seems to perform worse, i'm not referring to the machine counted time. Instead even with as low as 125000 points (per thread, per 8 threads) the program seems to never end (i've tried to let it run for tens of minutes, despite on windows only takes a few seconds to execute, on the same machine).

This comment has been minimized.

@carmelopellegrino

carmelopellegrino Mar 14, 2017

Contributor

Ah sorry, I didn't see the comment in the commit. 125000 points should be generated in few seconds on any OS... It may be a bug due to some difference between the two OSes. I'll investigate on it!

This comment has been minimized.

@carmelopellegrino

carmelopellegrino Mar 14, 2017

Contributor

Indeed, if running 8 threads the program blocks for minutes, while when running on one thread it does the same job in less than one second! I'd say that the problem is in the rand() calls as they are the only points where the threads can interact together.

This comment has been minimized.

@5N44P

5N44P Mar 14, 2017

Owner

I'll modify the code and try with the rand_r() funcion. I'll be here with the results in minutes :)

This comment has been minimized.

@5N44P

5N44P Mar 15, 2017

Owner

In the meantime i had some issues with my internet connection.
Anyway i've tried the with rand_r() modification and it works fine on linux. In the next days i'll update the code.
Thank you for your help!

This comment has been minimized.

@carmelopellegrino

carmelopellegrino Mar 15, 2017

Contributor

You are welcome :)

This comment has been minimized.

@5N44P

5N44P Mar 16, 2017

Owner

Repository updated! (here af902d3)

// creating the array of thr_data that will be passed to threads
thread_data_t thr_data[NUM_THREADS];
// creating the threads and handling some errors
for (i = 0; i < NUM_THREADS; ++i) {
thr_data[i].tid = i;
if ((rc = pthread_create(&thr[i], NULL, thr_func, &thr_data[i]))) {
fprintf(stderr, "error: pthread_create, rc: %d\n", rc);
return EXIT_FAILURE;
}
}
// wait each thread to be over and print the number of matching points for each thread
for (i = 0; i < NUM_THREADS; ++i) {
pthread_join(thr[i], NULL);
printf("%u\n",thr_data[i].belongs);
}
// calculating and printing the computational time
time = clock() - time;
printf("Total CPU time: %lf seconds\n", (double)time/(CLOCKS_PER_SEC));
Beep(3000,500);
return 0;
}

0 comments on commit 6123367

Please sign in to comment.