From 19d5190c55a836926f2bd646e043fcd6c61ab919 Mon Sep 17 00:00:00 2001 From: Sam Moore Date: Tue, 11 Sep 2012 20:26:15 +0800 Subject: [PATCH 1/1] Parallel Programming - pthreads version Implemented multithreaded barrier to save typing the same thing a billion more times --- .../pprog/assignment1/mthread/nbody.c | 122 +++++++++--------- .../pprog/assignment1/mthread/nbody.h | 25 ++-- 2 files changed, 73 insertions(+), 74 deletions(-) diff --git a/course/semester2/pprog/assignment1/mthread/nbody.c b/course/semester2/pprog/assignment1/mthread/nbody.c index f59b4512..75049286 100644 --- a/course/semester2/pprog/assignment1/mthread/nbody.c +++ b/course/semester2/pprog/assignment1/mthread/nbody.c @@ -15,15 +15,12 @@ pthread_t compute_thread; // The thread responsible for computations; it spawns pthread_t * worker_thread = NULL; //Array of worker threads responsible for Force and Position updates System * sub_system = NULL; //Array of Systems used to divide up the main "universe" System for worker threads -pthread_mutex_t mutex_workers; // Mutex used for the barrier between Force and Position updates -pthread_cond_t workers_done_cv; // Conditional used for the barrier between Force and Position updates -unsigned workers_busy; // Number of workers currently doing something pthread_mutex_t mutex_runstate; // Mutex around the runstate -pthread_mutex_t mutex_graphics; //Mutex between graphics and computation -pthread_cond_t graphics_cv; //Condition used to start graphics or computation thread from the other -bool graphics_busy = false; // Indicate if graphics is currently drawing +Barrier worker_barrier; +Barrier graphics_barrier; + /** * @function Compute_Thread @@ -86,6 +83,9 @@ void * Compute_Thread(void * arg) pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); //Needs to be detached, so that memory can be reused. + + + // The main computation loop while (true) { @@ -110,11 +110,6 @@ void * Compute_Thread(void * arg) continue; } - - - - workers_busy = options.num_threads; //All threads working - //Compute forces for (unsigned i = 0; i < options.num_threads; ++i) { @@ -124,28 +119,19 @@ void * Compute_Thread(void * arg) QuitProgram(true); pthread_exit(NULL); } + } - - //Barrier - Wait for forces to be computed - pthread_mutex_lock(&mutex_workers); - while (workers_busy > 0) - pthread_cond_wait(&workers_done_cv, &mutex_workers); - pthread_mutex_unlock(&mutex_workers); + Barrier_Wait(&worker_barrier); //All the forces are now computed - // Wait for graphics to finish drawing the last step - if (options.pedantic_graphics && options.draw_graphics) + if (options.draw_graphics && options.pedantic_graphics) { - pthread_mutex_lock(&mutex_graphics); - while (graphics_busy) - pthread_cond_wait(&graphics_cv, &mutex_graphics); - pthread_mutex_unlock(&mutex_graphics); + Barrier_Wait(&graphics_barrier); + Barrier_Enter(&graphics_barrier); } - - workers_busy = options.num_threads; //All threads working //Compute positions for (unsigned i = 0; i < options.num_threads; ++i) @@ -159,23 +145,15 @@ void * Compute_Thread(void * arg) } //Wait for positions to be computed - pthread_mutex_lock(&mutex_workers); - while (workers_busy > 0) - pthread_cond_wait(&workers_done_cv, &mutex_workers); - pthread_mutex_unlock(&mutex_workers); + Barrier_Wait(&worker_barrier); + //Update number of steps computed universe.steps += 1; + if (options.draw_graphics && options.pedantic_graphics) + Barrier_Leave(&graphics_barrier); - // Signal graphics thread to draw bodies - if (options.pedantic_graphics && options.draw_graphics) - { - pthread_mutex_lock(&mutex_graphics); - graphics_busy = true; - pthread_cond_signal(&graphics_cv); - pthread_mutex_unlock(&mutex_graphics); - } } } @@ -190,10 +168,8 @@ void BeforeDraw() { if (!options.pedantic_graphics) return; - pthread_mutex_lock(&mutex_graphics); - while (!graphics_busy) - pthread_cond_wait(&graphics_cv, &mutex_graphics); - pthread_mutex_unlock(&mutex_graphics); + Barrier_Wait(&graphics_barrier); + Barrier_Enter(&graphics_barrier); } /** @@ -207,10 +183,7 @@ void AfterDraw() { if (!options.pedantic_graphics) return; - pthread_mutex_lock(&mutex_graphics); - graphics_busy = false; - pthread_cond_signal(&graphics_cv); - pthread_mutex_unlock(&mutex_graphics); + Barrier_Leave(&graphics_barrier); } /** @@ -220,16 +193,12 @@ void AfterDraw() */ void * Force_Thread(void * s) { - + Barrier_Enter(&worker_barrier); + System_Forces((System*)s, &universe); //Simple wrapper - pthread_mutex_lock(&mutex_workers); - workers_busy -= 1; // Count this thread as done - if (workers_busy == 0) - { - pthread_cond_signal(&workers_done_cv); // All threads done; wake up the compute_thread - } - pthread_mutex_unlock(&mutex_workers); + Barrier_Leave(&worker_barrier); + return NULL; } @@ -240,16 +209,9 @@ void * Force_Thread(void * s) */ void * Position_Thread(void * s) { - + Barrier_Enter(&worker_barrier); System_Positions((System*)s); // Simple wrapper - - pthread_mutex_lock(&mutex_workers); - workers_busy -= 1; // Count this thread as done - if (workers_busy == 0) - { - pthread_cond_signal(&workers_done_cv); //All threads done; wake up the compute_thread - } - pthread_mutex_unlock(&mutex_workers); + Barrier_Leave(&worker_barrier); return NULL; } @@ -300,6 +262,9 @@ void Simulation_Run(int argc, char ** argv) { atexit(Thread_Cleanup); + Barrier_Init(&worker_barrier); + Barrier_Init(&graphics_barrier); + if (options.draw_graphics) { // The graphics are enabled, so create a thread to do computations @@ -314,3 +279,36 @@ void Simulation_Run(int argc, char ** argv) else Compute_Thread((void*)(&universe)); // Graphics are disabled, so do computations in the main thread } + + + +void Barrier_Init(Barrier * b) +{ + b->threads_busy = 0; +} + +void Barrier_Enter(Barrier * b) +{ + pthread_mutex_lock(&(b->mutex)); + b->threads_busy += 1; + pthread_mutex_unlock(&(b->mutex)); +} + +void Barrier_Leave(Barrier * b) +{ + pthread_mutex_lock(&(b->mutex)); + b->threads_busy -= 1; + if (b->threads_busy == 0) + { + pthread_cond_signal(&(b->threads_done_cv)); + } + pthread_mutex_unlock(&(b->mutex)); +} + +void Barrier_Wait(Barrier * b) +{ + pthread_mutex_lock(&(b->mutex)); + while (b->threads_busy > 0) + pthread_cond_wait(&(b->threads_done_cv), &(b->mutex)); + pthread_mutex_unlock(&(b->mutex)); +} diff --git a/course/semester2/pprog/assignment1/mthread/nbody.h b/course/semester2/pprog/assignment1/mthread/nbody.h index f2eec0db..59501263 100644 --- a/course/semester2/pprog/assignment1/mthread/nbody.h +++ b/course/semester2/pprog/assignment1/mthread/nbody.h @@ -10,6 +10,8 @@ #define DEFAULT_WORKING_THREADS 2 +#define PERSISTENT_THREADS //If defined, threads will not be continually destroyed and then respawned + //Undefine default macros, replace with functions #undef Simulation_Run void Simulation_Run(int argc, char ** argv); @@ -31,17 +33,16 @@ void * Position_Thread(void * system); //Thread - Compute positions for all obje void Thread_Cleanup(void); //Called at program exit to safely join computation thread -extern pthread_t compute_thread; // ID of the thread that runs Compute_Thread. -extern pthread_mutex_t mutex_runstate; //Mutex around the "runstate" variable - -extern pthread_t * worker_thread; //Array of worker threads responsible for Force and Position updates -extern System * sub_system; //Array of Systems used to divide up the main "universe" System for worker threads -extern pthread_mutex_t mutex_workers; -extern pthread_cond_t workers_done_cv; -extern unsigned workers_busy; - -extern pthread_mutex_t mutex_graphics; // Mutex for graphics -extern pthread_cond_t graphics_cv; //Condition used to start graphics or computation thread from the other -extern bool graphics_busy; +typedef struct +{ + pthread_mutex_t mutex; + unsigned threads_busy; // Counter of threads which are busy + pthread_cond_t threads_done_cv; +} Barrier; + +void Barrier_Init(Barrier * b); +void Barrier_Enter(Barrier * b); +void Barrier_Leave(Barrier * b); +void Barrier_Wait(Barrier * b); #endif //_NBODY_MTHREAD_H -- 2.20.1