From 19cb61f75ddda901224f7ce2bae0d3934f67d9ee Mon Sep 17 00:00:00 2001 From: Sam Moore Date: Thu, 6 Sep 2012 23:32:11 +0800 Subject: [PATCH] Parallel Programming - Finished(?) pthread version Seems to work. Hasn't crashed yet. Seems faster than single-threaded version, but have only looked at side by side graphics drawing. Need to analyse performance better. Sam --- .../pprog/assignment1/mthread/graphics.c | 20 ++- .../pprog/assignment1/mthread/main.c | 31 ++-- .../pprog/assignment1/mthread/nbody.c | 166 ++++++++++++++++-- .../pprog/assignment1/mthread/nbody.h | 21 ++- 4 files changed, 200 insertions(+), 38 deletions(-) diff --git a/course/semester2/pprog/assignment1/mthread/graphics.c b/course/semester2/pprog/assignment1/mthread/graphics.c index 9fe66555..6f043ad8 100644 --- a/course/semester2/pprog/assignment1/mthread/graphics.c +++ b/course/semester2/pprog/assignment1/mthread/graphics.c @@ -87,6 +87,25 @@ void Graphics_Run(int argc, char ** argv) */ void Graphics_Display() { + //Check whether the runstate has been set to quit the program + switch (runstate) + { + case RUN: + break; + case QUIT: + exit(EXIT_SUCCESS); + break; + case QUIT_ERROR: + exit(EXIT_FAILURE); + break; + } + + //Check if window exists, quit if it doesn't + if (glutGetWindow() == 0) + { + exit(EXIT_SUCCESS); + } + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); @@ -113,7 +132,6 @@ void Graphics_Keyboard(unsigned char theKey, int mouseX, int mouseY) { if (theKey == 'x' || theKey == 'X') { - printf("Kill recieved\n"); exit(EXIT_SUCCESS); } diff --git a/course/semester2/pprog/assignment1/mthread/main.c b/course/semester2/pprog/assignment1/mthread/main.c index 2d6b228b..53162461 100644 --- a/course/semester2/pprog/assignment1/mthread/main.c +++ b/course/semester2/pprog/assignment1/mthread/main.c @@ -10,10 +10,15 @@ unsigned numberOfProcessors; System universe; pthread_t compute_thread; -pthread_mutex_t mutex_terminate; -bool terminate = false; +pthread_mutex_t mutex_runstate; +RUNSTATE runstate = RUN; -void Cleanup_Threads(void); //Cleanup for threads +void Thread_Cleanup(void) +{ + if (runstate == RUN) + QuitProgram(false); + pthread_join(compute_thread, NULL); +} // This is main function. Do not change it. int main(int argc, char** argv) @@ -33,7 +38,7 @@ int main(int argc, char** argv) numberOfProcessors = atoi(argv[2]); } - atexit(Cleanup_Threads); + atexit(Thread_Cleanup); // Create a thread to handle computation loop @@ -45,20 +50,10 @@ int main(int argc, char** argv) Graphics_Run(argc, argv); // Run graphics loop in the main thread + printf("Close!\n"); + //We get to this point when the "close" button is clicked in the graphics window + QuitProgram(false); + pthread_join(compute_thread, NULL); } -/** - * Function will be called when exit() is called. - * Will set condition for child threads to terminate, and then join with them. - * The main thread is responsible for calling exit(). - */ -void Cleanup_Threads() -{ - printf("Set terminate\n"); - pthread_mutex_lock(&mutex_terminate); - terminate = true; - pthread_mutex_unlock(&mutex_terminate); - pthread_join(compute_thread, NULL); - pthread_exit(NULL); -} diff --git a/course/semester2/pprog/assignment1/mthread/nbody.c b/course/semester2/pprog/assignment1/mthread/nbody.c index 290eeb61..cd0656af 100644 --- a/course/semester2/pprog/assignment1/mthread/nbody.c +++ b/course/semester2/pprog/assignment1/mthread/nbody.c @@ -10,9 +10,23 @@ #include #include #include +#include +#include #include "nbody.h" +//Stuff to do with worker threads +// The worker threads (if used) are spawned by the computation thread, not the main thread +// The main thread (which handles initialisation, graphics, user input, cleanup and exit) does not need to know about these. +#if NUM_THREADS > 1 + + pthread_t worker_thread[NUM_THREADS]; //Worker threads responsible for Force and Position updates + System sub_system[NUM_THREADS]; //Array of System's used to divide up the main "universe" System for worker threads + pthread_mutex_t mutex_workers; + pthread_cond_t workers_done_cv; + unsigned worker_count; +#endif + /** * Prints the body on screen */ @@ -70,21 +84,28 @@ void Body_Position(Body * a) } /** - * Main compute function + * Compute forces on each object in System s1 due to all bodies in System s2 */ -void System_Compute(System * s) +void System_Forces(System * s1, System * s2) { -// clock_t start, finish; - -// start = clock(); - - for (unsigned i = 0; i < s->N; ++i) + //printf("Compute forces for %p - %d bodies...\n", (void*)s1, s1->N); + for (unsigned i = 0; i < s1->N; ++i) { - Body_Force(s->body+i, s); - Body_Velocity(s->body+i); - Body_Position(s->body+i); + Body_Force(s1->body+i, s2); + Body_Velocity(s1->body+i); } + //printf("Compute positions for %p - Done!\n", (void*)s1); +} +/** + * Compute positions of all objects in System + */ +void System_Positions(System * s) +{ + //printf("Compute positions for %p - %d bodies...\n", (void*)s, s->N); + for (unsigned i = 0; i < s->N; ++i) + Body_Position(s->body+i); + //printf("Compute positions for %p - Done!\n", (void*)s); } @@ -140,20 +161,137 @@ void System_Init(System * s, char *fileName) void Universe_Cleanup() { free(universe.body); - pthread_exit(NULL); + } /** * Thread - Continuously computes steps for a system of bodies */ -void * Compute_Thread(void * s) +void * Compute_Thread(void * arg) { + + System * s = (System*)(arg); //cast argument to a System* + // First set up the "sub_system" array + #if NUM_THREADS > 1 + unsigned bodies_per_system = (s->N) / NUM_THREADS; + unsigned remainder = (s->N) % NUM_THREADS; + for (unsigned i = 0; i < NUM_THREADS; ++i) + { + sub_system[i].body = (s->body)+(i*bodies_per_system); + sub_system[i].N = bodies_per_system; + if (i == NUM_THREADS - 1) + sub_system[i].N += remainder; + } + + pthread_attr_t attr; //thread attribute for the workers + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + #endif while (true) { - if (terminate) pthread_exit(NULL); - System_Compute((System*)s); + + if (runstate != RUN) pthread_exit(NULL); //Check whether the main thread wants to exit + + + + #if NUM_THREADS <= 1 + //No worker threads; do everything in this thread + System_Forces(s, s); + System_Positions(s); + #else + + + + + worker_count = NUM_THREADS; //All threads working + + //Compute forces + for (unsigned i = 0; i < NUM_THREADS; ++i) + { + if (pthread_create(worker_thread+i, &attr, Force_Thread, (void*)(sub_system+i)) != 0) + { + perror("In compute thread, couldn't create worker thread (force)"); + QuitProgram(true); + pthread_exit(NULL); + } + } + + + + //Wait for forces to be computed + pthread_mutex_lock(&mutex_workers); + while (worker_count > 0) + pthread_cond_wait(&workers_done_cv, &mutex_workers); + pthread_mutex_unlock(&mutex_workers); + + worker_count = NUM_THREADS; //All threads working + + //Compute positions + for (unsigned i = 0; i < NUM_THREADS; ++i) + { + if (pthread_create(worker_thread+i, &attr, Position_Thread, (void*)(sub_system+i)) != 0) + { + perror("In compute thread, couldn't create worker thread (position)"); + QuitProgram(true); + pthread_exit(NULL); + } + } + + //Wait for positions to be computed + pthread_mutex_lock(&mutex_workers); + while (worker_count > 0) + pthread_cond_wait(&workers_done_cv, &mutex_workers); + pthread_mutex_unlock(&mutex_workers); + + + #endif + } } +void * Force_Thread(void * s) +{ + + System_Forces((System*)s, &universe); + pthread_mutex_lock(&mutex_workers); + worker_count -= 1; + if (worker_count == 0) + { + pthread_cond_signal(&workers_done_cv); + } + pthread_mutex_unlock(&mutex_workers); + return NULL; +} + +void * Position_Thread(void * s) +{ + + System_Positions((System*)s); + pthread_mutex_lock(&mutex_workers); + worker_count -= 1; + if (worker_count == 0) + { + pthread_cond_signal(&workers_done_cv); + } + pthread_mutex_unlock(&mutex_workers); + return NULL; +} + +/** + * Child threads can call this to signal the main thread to quit the program + * The main thread also uses this to tell child threads that the program is quitting + * Note that the function doesn't call exit(), that is done by the main thread + */ +void QuitProgram(bool error) +{ + + pthread_mutex_lock(&mutex_runstate); + if (error) + runstate = QUIT_ERROR; + else + runstate = QUIT; + pthread_mutex_unlock(&mutex_runstate); +} diff --git a/course/semester2/pprog/assignment1/mthread/nbody.h b/course/semester2/pprog/assignment1/mthread/nbody.h index 80bfb825..b9cac6fa 100644 --- a/course/semester2/pprog/assignment1/mthread/nbody.h +++ b/course/semester2/pprog/assignment1/mthread/nbody.h @@ -7,7 +7,10 @@ * @purpose N-Body simulator: declarations of simulation related parameters */ -#define NUM_THREADS 4 + +#define NUM_THREADS 5 //The number of *worker* threads (not including the computation thread) to use. + // If this is 1, the computation thread will not spawn any workers (faster to just do the work itself), + // otherwise it will spawn NUM_THREADS workers. #include #include @@ -56,7 +59,8 @@ void Body_Position(Body * a); //Compute position of body a void System_Init(System * s, char * fileName); //Initialise System (array of bodies) from a text file -void System_Compute(System * system); //Perform a single computation step for a System of bodies +void System_Forces(System * s1, System * s2); //Compute forces for bodies in s1 due to bodies in s2 (also updates velocities) +void System_Positions(System * s); //Update positions for bodies in s1 void Universe_Cleanup(); extern System universe; // The main array of bodies; global variable. @@ -67,10 +71,17 @@ extern System universe; // The main array of bodies; global variable. */ extern pthread_t compute_thread; // ID of the thread that runs Compute_Thread. Set in main() -void * Compute_Thread(void * system); //Thread - Continuously perform computations for a System of bodies +void * Compute_Thread(void * system); //Thread - Continuously perform computations for a System of bodies. May spawn additional worker threads. + +void * Force_Thread(void * system); //Thread - Compute forces for all objects in a system +void * Position_Thread(void * system); //Thread - Compute positions for all objects in a system + +typedef enum {RUN, QUIT, QUIT_ERROR} RUNSTATE; +extern RUNSTATE runstate; +extern pthread_mutex_t mutex_runstate; //Mutex around the "runstate" variable + +void QuitProgram(bool error); -extern bool terminate; //Will be set to true by main thread to signal children to terminate. Child threads are responsible for checking. -extern pthread_mutex_t mutex_terminate; //Mutex around the "terminate" variable #endif //_NBODY_H -- 2.20.1