From 83cf58f62ddc5ce9fe44de27327fde27f767ebb6 Mon Sep 17 00:00:00 2001 From: Sam Moore Date: Tue, 11 Sep 2012 20:01:43 +0800 Subject: [PATCH] Parallel Programming - Commit before I break things Going to try and alter pthreads version so that it doesn't keep respawning threads IE: The same threads are active for the whole program. See which is faster. --- course/semester2/pprog/assignment1/Makefile | 18 +++++ .../pprog/assignment1/mthread/nbody.c | 57 +++++++++++++++- .../pprog/assignment1/mthread/nbody.h | 9 +++ .../pprog/assignment1/openmp/nbody.c | 53 +++++++++++++-- .../pprog/assignment1/openmp/nbody.h | 5 ++ .../assignment1/single-thread/graphics.c | 40 ++++++----- .../pprog/assignment1/single-thread/main.c | 67 ++++++++++++++++--- .../pprog/assignment1/single-thread/nbody.c | 1 + .../pprog/assignment1/single-thread/nbody.h | 29 +++++++- 9 files changed, 245 insertions(+), 34 deletions(-) create mode 100644 course/semester2/pprog/assignment1/Makefile diff --git a/course/semester2/pprog/assignment1/Makefile b/course/semester2/pprog/assignment1/Makefile new file mode 100644 index 00000000..e93e59df --- /dev/null +++ b/course/semester2/pprog/assignment1/Makefile @@ -0,0 +1,18 @@ +# Makefile to build all programs + +all : single-thread/nbody mthread/nbody + +single-thread/nbody : + make -C single-thread + +mthread/nbody : + make -C mthread + +openmp/nbody : + make -C openmp + +clean : + make -C single-thread clean_full + make -C mthread clean_full + make -C openmp clean_full + rm *~ diff --git a/course/semester2/pprog/assignment1/mthread/nbody.c b/course/semester2/pprog/assignment1/mthread/nbody.c index 4acf28f2..f59b4512 100644 --- a/course/semester2/pprog/assignment1/mthread/nbody.c +++ b/course/semester2/pprog/assignment1/mthread/nbody.c @@ -21,7 +21,9 @@ 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 /** * @function Compute_Thread @@ -94,8 +96,7 @@ void * Compute_Thread(void * arg) pthread_exit(NULL); } - if (options.draw_graphics == false && options.verbosity != 0 - && universe.steps % options.verbosity == 1) + if (options.verbosity != 0 && universe.steps % options.verbosity == 1) { DisplayStatistics(); } @@ -134,6 +135,15 @@ void * Compute_Thread(void * arg) pthread_mutex_unlock(&mutex_workers); //All the forces are now computed + + // Wait for graphics to finish drawing the last step + if (options.pedantic_graphics && options.draw_graphics) + { + pthread_mutex_lock(&mutex_graphics); + while (graphics_busy) + pthread_cond_wait(&graphics_cv, &mutex_graphics); + pthread_mutex_unlock(&mutex_graphics); + } workers_busy = options.num_threads; //All threads working @@ -158,10 +168,51 @@ void * Compute_Thread(void * arg) universe.steps += 1; + // 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); + } } } +/** + * @function BeforeDraw + * @purpose Called in graphics thread before the draw loop + * When --pedantic-graphics enabled, will wait for position computations to finish before drawing + * Otherwise does nothing + */ +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); +} + +/** + * @function AfterDraw + * @purpose Called in graphics thread after the draw loop + * When --pedantic-graphics is supplied, will signal computation thread that drawing is finished + * So that positions can be safely altered + * Otherwise does nothing + */ +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); +} + /** * @function Force_Thread * @purpose Thread - Calculates the forces on objects in a System diff --git a/course/semester2/pprog/assignment1/mthread/nbody.h b/course/semester2/pprog/assignment1/mthread/nbody.h index 0b3208a9..f2eec0db 100644 --- a/course/semester2/pprog/assignment1/mthread/nbody.h +++ b/course/semester2/pprog/assignment1/mthread/nbody.h @@ -16,6 +16,11 @@ void Simulation_Run(int argc, char ** argv); #undef QuitProgram void QuitProgram(bool error); +#undef BeforeDraw +void BeforeDraw(); +#undef AfterDraw +void AfterDraw(); + void * Compute_Thread(void * system); //Thread - Continuously perform computations for a System of bodies. May spawn additional worker threads. @@ -35,4 +40,8 @@ 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; + #endif //_NBODY_MTHREAD_H diff --git a/course/semester2/pprog/assignment1/openmp/nbody.c b/course/semester2/pprog/assignment1/openmp/nbody.c index 6eb9ed4a..c4872010 100644 --- a/course/semester2/pprog/assignment1/openmp/nbody.c +++ b/course/semester2/pprog/assignment1/openmp/nbody.c @@ -8,6 +8,9 @@ #include "../single-thread/nbody.c" // Include original code #include "graphics.h" // For Graphics_Run function only +omp_lock_t graphics_lock; +bool graphics_busy = false; + /** * @function Simulation_Run * @purpose Start the simulation @@ -32,7 +35,7 @@ void Simulation_Run(int argc, char ** argv) omp_set_num_threads(1); - + omp_init_lock(&graphics_lock); #pragma omp parallel sections { @@ -50,9 +53,7 @@ void Simulation_Run(int argc, char ** argv) break; } - - if (options.draw_graphics == false && options.verbosity != 0 - && universe.steps % options.verbosity == 1) + if (options.verbosity != 0 && universe.steps % options.verbosity == 1) { DisplayStatistics(); } @@ -71,6 +72,8 @@ void Simulation_Run(int argc, char ** argv) } } + + omp_destroy_lock(&graphics_lock); } /** @@ -93,6 +96,13 @@ void Compute(void) System_Forces(&s, &universe); } + // Wait for graphics to finish drawing + + if (options.draw_graphics && options.pedantic_graphics) + { + while (graphics_busy); + } + #pragma omp parallel for for (unsigned i = 0; i < options.num_threads; ++i) { @@ -105,4 +115,39 @@ void Compute(void) universe.steps += 1; + // Tell graphics to continue + if (options.draw_graphics && options.pedantic_graphics) + { + omp_set_lock(&graphics_lock); + graphics_busy = true; + omp_unset_lock(&graphics_lock); + + } + +} + +/** + * @function BeforeDraw + * @purpose Called before the graphics thread draws bodies. + * If pedantic graphics are used, will wait until graphics_busy is set to true before drawing + */ +void BeforeDraw() +{ + if (!options.pedantic_graphics) + return; + while (graphics_busy == false); +} + +/** + * @function AfterDraw + * @purpose Called after the graphics thread draws bodies + * If pedantic graphics used, will unset graphics_busy when done drawing + */ +void AfterDraw() +{ + if (!options.pedantic_graphics) + return; + omp_set_lock(&graphics_lock); + graphics_busy = false; + omp_unset_lock(&graphics_lock); } diff --git a/course/semester2/pprog/assignment1/openmp/nbody.h b/course/semester2/pprog/assignment1/openmp/nbody.h index b7d0b417..91c4bf4f 100644 --- a/course/semester2/pprog/assignment1/openmp/nbody.h +++ b/course/semester2/pprog/assignment1/openmp/nbody.h @@ -20,6 +20,11 @@ void Simulation_Run(int argc, char ** argv); //#undef QuitProgram //void QuitProgram(bool error); +#undef BeforeDraw +void BeforeDraw(); +#undef AfterDraw +void AfterDraw(); + void Compute(); //Compute a single step #endif //_NBODY_OPENMP_H diff --git a/course/semester2/pprog/assignment1/single-thread/graphics.c b/course/semester2/pprog/assignment1/single-thread/graphics.c index 7a7d2134..864ff915 100644 --- a/course/semester2/pprog/assignment1/single-thread/graphics.c +++ b/course/semester2/pprog/assignment1/single-thread/graphics.c @@ -33,10 +33,9 @@ void Graphics_Run(int argc, char ** argv) { if (options.draw_graphics == false) { - while (true) - Graphics_Display(); - - return; + // This message is left here for when I inevitably accidentally call the function + fprintf(stderr, "Graphics_Run should not be called if graphics are disabled!\n"); + exit(EXIT_FAILURE); } glutInit(&argc, argv); @@ -107,23 +106,20 @@ void Graphics_Run(int argc, char ** argv) */ void Graphics_Display() { - //Check whether the program should quit due to steps being computed, or a timeout - if (ExitCondition()) + + if (options.draw_graphics == false) { - //printf("Leave graphics loop\n"); - glutLeaveMainLoop(); - return; + // This message is left here for when I inevitably accidentally call the function + fprintf(stderr, "Graphics_Display should not be called if graphics are disabled!\n"); + exit(EXIT_FAILURE); } - #ifdef SINGLE_THREADED - if (options.verbosity != 0 && universe.steps % options.verbosity == 1) - DisplayStatistics(); - System_Compute(&universe); - #endif - - if (options.draw_graphics == false) + //Check whether the graphics thread should exit for any reason + if (ExitCondition()) + { + glutLeaveMainLoop(); // original glut does not have this, which makes "nicely" exiting a bit annoying return; - + } glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); @@ -131,6 +127,11 @@ void Graphics_Display() gluLookAt(eyeRho * sin(eyePhi) * sin(eyeTheta), eyeRho * cos(eyePhi), eyeRho * sin(eyePhi) * cos(eyeTheta),look[0], look[1], look[2], 0, upY, 0); + BeforeDraw(); // Stuff to do before graphics is allowed to draw + // Single-thread - perform a computation step, obviously! It's not done anywhere else + // Multiple threads - ensure that all body positions are updated (ie: not halfway through step). + // (We don't care about the force and velocity variables though) + for (int i = 0; i < universe.N; ++i) { Body * b = universe.body+i; @@ -141,6 +142,11 @@ void Graphics_Display() glPopMatrix(); // restore the previous matrix } glFlush(); + + AfterDraw(); // Stuff to do after graphics is done drawing + // Single-thread - Nothing at all + // Multiple threads - signal threads it is safe to change position variables + } /** diff --git a/course/semester2/pprog/assignment1/single-thread/main.c b/course/semester2/pprog/assignment1/single-thread/main.c index 4b0c48ac..392c27d6 100644 --- a/course/semester2/pprog/assignment1/single-thread/main.c +++ b/course/semester2/pprog/assignment1/single-thread/main.c @@ -9,6 +9,7 @@ #include #include #include +#include // For parsing arguments #include "nbody.h" #include "graphics.h" @@ -20,7 +21,7 @@ Options options; // global variable declared in "nbody.h" - Options passed to pr // --- Function forward declarations --- // void HandleArguments(int argc, char ** argv); //Interprets program arguments and sets up the "options" variable -void UnsignedArgument(unsigned * i, int argc, char ** argv, unsigned * store); //Helper function to get switch value for unsigned +unsigned UnsignedArgument(unsigned * i, int argc, char ** argv, unsigned * store, unsigned * store2); //Helper function to get switch value for unsigned void FloatArgument(unsigned * i, int argc, char ** argv, float * store); //Helper function to get switch value for float void DisplayStatistics(); //Called on exit of program, displays information about computation time, steps computed, etc void Interrupt(int dummy); // Interrupt handler function, called when SIGINT (Ctrl-C) is sent to program @@ -41,9 +42,11 @@ int main(int argc, char** argv) options.input = NULL; options.output = NULL; options.num_threads = 0; + options.nested_threads = 0; options.num_steps = 0; options.timeout = 0; options.draw_graphics = true; + options.pedantic_graphics = false; options.print_positions = false; options.verbosity = 0; @@ -116,25 +119,49 @@ void HandleArguments(int argc, char ** argv) switch (argv[i][1]) //The argument is a switch if we get here { case 'n': //Number of threads switch - UnsignedArgument(&i, argc, argv, &(options.num_threads)); + UnsignedArgument(&i, argc, argv, &(options.num_threads), &(options.nested_threads)); #ifdef SINGLE_THREADED fprintf(stderr, "Warning: -%c switch has no effect in the single-threaded program.\n", argv[i][1]); - options.num_threads = 1; + #endif //SINGLE_THREADED break; case 's': //Number of steps switch - UnsignedArgument(&i, argc, argv, &(options.num_steps)); + UnsignedArgument(&i, argc, argv, &(options.num_steps), NULL); break; case 't': //Timeout switch (in seconds) - UnsignedArgument(&i, argc, argv, &(options.timeout)); + UnsignedArgument(&i, argc, argv, &(options.timeout), NULL); break; case 'g': //Graphics switch options.draw_graphics = !options.draw_graphics; break; case 'v': //Verbosity switch - UnsignedArgument(&i, argc, argv, &(options.verbosity)); + UnsignedArgument(&i, argc, argv, &(options.verbosity), NULL); break; + case '-': + if (strcmp(argv[i]+1, "pedantic-graphics")) + { + options.pedantic_graphics = true; + #ifdef SINGLE_THREADED + fprintf(stderr, "Warning: %s switch has no effect in the single threaded program.\n", argv[i]); + #endif //SINGLE_THREADED + } + else if (strcmp(argv[i]+1, "fast-graphics")) + { + options.pedantic_graphics = false; + #ifdef SINGLE_THREADED + fprintf(stderr, "Warning: %s switch has no effect in the single threaded program.\n", argv[i]); + #endif //SINGLE_THREADED + + } + else + { + fprintf(stderr, "Unrecognised switch -%s\n", argv[i]); + exit(EXIT_FAILURE); + } + break; + + default: fprintf(stderr, "Unrecognised switch -%c\n", argv[i][1]); exit(EXIT_FAILURE); @@ -151,22 +178,46 @@ void HandleArguments(int argc, char ** argv) * @param argc - number of arguments * @param argv - argument strings * @param store - pointer to unsigned to store result in + * @param store2 - pointer to second unsigned + * @returns 1 if store was filled, 2 if both store1 and store2 were filled */ -void UnsignedArgument(unsigned * i, int argc, char ** argv, unsigned * store) +unsigned UnsignedArgument(unsigned * i, int argc, char ** argv, unsigned * store, unsigned * store2) { if (*i >= argc-1) { fprintf(stderr,"Supply a positive integer for the -%c switch.\n", argv[*i][1]); exit(EXIT_FAILURE); } + + char * seperator = strstr(argv[*i+1], ":"); + if (seperator != NULL) + { + if (store2 == NULL) + { + fprintf(stderr,"Supply a positive integer for the -%c switch.\n", argv[*i][1]); + exit(EXIT_FAILURE); + } + int val = atoi(seperator+1); + if (val <= 0) + { + fprintf(stderr,"Supply a positive integer for the -%c switch.\n", argv[*i][1]); + exit(EXIT_FAILURE); + } + *store2 = (unsigned)val; + + *seperator = '\0'; + } + int val = atoi(argv[*i+1]); if (val <= 0) { fprintf(stderr,"Supply a positive integer for the -%c switch. %s is invalid.\n", argv[*i][1], argv[*i+1]); exit(EXIT_FAILURE); } - *store = val; + *store = (unsigned)val; *i += 1; + + return (seperator == NULL) ? 1 : 2; } /** * @function FloatArgument diff --git a/course/semester2/pprog/assignment1/single-thread/nbody.c b/course/semester2/pprog/assignment1/single-thread/nbody.c index c54661f8..67afd0fd 100644 --- a/course/semester2/pprog/assignment1/single-thread/nbody.c +++ b/course/semester2/pprog/assignment1/single-thread/nbody.c @@ -101,6 +101,7 @@ void System_Compute(System * s) * @param s1, s2 - The two systems * NOTE: In single threaded version, called with s1 == s2 == &universe * In multi threaded version, called with s2 == &universe, but s1 is constructed for each thread + * In nested multi-threaded version, s2 is constructed for the nested threads as well */ void System_Forces(System * s1, System * s2) { diff --git a/course/semester2/pprog/assignment1/single-thread/nbody.h b/course/semester2/pprog/assignment1/single-thread/nbody.h index fc19d059..a0415748 100644 --- a/course/semester2/pprog/assignment1/single-thread/nbody.h +++ b/course/semester2/pprog/assignment1/single-thread/nbody.h @@ -17,10 +17,33 @@ #define SINGLE_THREADED -//These macros will be undefined by the multithreaded versions -#define Simulation_Run(argc, argv) (Graphics_Run(argc, argv)) //Sets up the simulation; in multithreaded versions, will spawn threads +// --- The below macros will be undefined by the multithreaded versions, and replaced with functions --- // + +//Sets up the simulation; in multithreaded versions, will spawn threads +#define Simulation_Run(argc, argv) \ +if (options.draw_graphics) \ + Graphics_Run(argc, argv); \ +else \ +{ \ + while (ExitCondition() == false) \ + { BeforeDraw(); AfterDraw(); } \ +} + #define QuitProgram(error) (runstate = (error == true) ? QUIT : QUIT_ERROR) //Prepares to exit program, is thread safe in multithreaded versions +//Macro to be overwritten in multithreaded versions, called before the graphics is allowed to draw anything +#define BeforeDraw() \ +if (options.verbosity != 0 && universe.steps % options.verbosity == 1) \ + DisplayStatistics(); \ +System_Compute(&universe); + + + +//Macro to be overwritten in multithreaded versions, called after the graphics has finished drawing. +#define AfterDraw() + +// --- Constants and other Macros --- // + #define M_PI 3.14159265358979323846264338327950288 /* pi */ #define G 6.67428E-11 #define DELTA_T 0.05 @@ -71,9 +94,11 @@ typedef struct const char * output; // file to write final positions / velocities of bodies to const char * program; // program name unsigned num_threads; // number of worker threads to spawn (must be greater than 1 for any to be spawned) + unsigned nested_threads; // number of threads to nest computations with (must be greater than 1 for any to be spawned) unsigned num_steps; // number of steps to run before stopping (run indefinately if equal to zero) unsigned timeout; // number of seconds to run before stopping (run indefinately if equal to zero) bool draw_graphics; // whether or not to actually draw graphics + bool pedantic_graphics; // whether the graphics thread will synchronise with the computation thread (true) or just draw as fast as possible (false) bool print_positions; // print positions of bodies to stdout on every step unsigned verbosity; // print statistics every number of steps indicated by this variable clock_t start_clock; // clock cycles done when simulation starts -- 2.20.1