Parallel Programming - Commit before I break things
authorSam Moore <sam@daedalus.(none)>
Tue, 11 Sep 2012 12:01:43 +0000 (20:01 +0800)
committerSam Moore <sam@daedalus.(none)>
Tue, 11 Sep 2012 12:01:43 +0000 (20:01 +0800)
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 [new file with mode: 0644]
course/semester2/pprog/assignment1/mthread/nbody.c
course/semester2/pprog/assignment1/mthread/nbody.h
course/semester2/pprog/assignment1/openmp/nbody.c
course/semester2/pprog/assignment1/openmp/nbody.h
course/semester2/pprog/assignment1/single-thread/graphics.c
course/semester2/pprog/assignment1/single-thread/main.c
course/semester2/pprog/assignment1/single-thread/nbody.c
course/semester2/pprog/assignment1/single-thread/nbody.h

diff --git a/course/semester2/pprog/assignment1/Makefile b/course/semester2/pprog/assignment1/Makefile
new file mode 100644 (file)
index 0000000..e93e59d
--- /dev/null
@@ -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 *~
index 4acf28f..f59b451 100644 (file)
@@ -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
index 0b3208a..f2eec0d 100644 (file)
@@ -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
index 6eb9ed4..c487201 100644 (file)
@@ -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);
 }
index b7d0b41..91c4bf4 100644 (file)
@@ -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
index 7a7d213..864ff91 100644 (file)
@@ -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
+
 }
 
 /**
index 4b0c48a..392c27d 100644 (file)
@@ -9,6 +9,7 @@
 #include <stdio.h>
 #include <time.h>
 #include <signal.h>
+#include <string.h> // 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
index c54661f..67afd0f 100644 (file)
@@ -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) 
 {
index fc19d05..a041574 100644 (file)
 
 #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

UCC git Repository :: git.ucc.asn.au