Parallel Programming - Finished(?) pthread version
authorSam Moore <sam@daedalus.(none)>
Thu, 6 Sep 2012 15:32:11 +0000 (23:32 +0800)
committerSam Moore <sam@daedalus.(none)>
Thu, 6 Sep 2012 15:32:11 +0000 (23:32 +0800)
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

course/semester2/pprog/assignment1/mthread/graphics.c
course/semester2/pprog/assignment1/mthread/main.c
course/semester2/pprog/assignment1/mthread/nbody.c
course/semester2/pprog/assignment1/mthread/nbody.h

index 9fe6655..6f043ad 100644 (file)
@@ -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);
        }
 
index 2d6b228..5316246 100644 (file)
@@ -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);
-}
index 290eeb6..cd0656a 100644 (file)
 #include <time.h>
 #include <string.h>
 #include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
 
 #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);
+}
 
 
index 80bfb82..b9cac6f 100644 (file)
@@ -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 <pthread.h>
 #include <stdbool.h>
@@ -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

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