Merge branch 'master' of mussel.ucc.asn.au:matches/honours
authorSam Moore <[email protected]>
Thu, 27 Sep 2012 11:07:18 +0000 (19:07 +0800)
committerSam Moore <[email protected]>
Thu, 27 Sep 2012 11:07:18 +0000 (19:07 +0800)
12 files changed:
course/semester2/pprog/assignment1/Makefile [new file with mode: 0644]
course/semester2/pprog/assignment1/get_data.py
course/semester2/pprog/assignment1/mthread/nbody-slow [new file with mode: 0755]
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/graphics.h
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..c66787e
--- /dev/null
@@ -0,0 +1,18 @@
+# Makefile to build all programs
+
+all : single-thread/nbody mthread/nbody openmp/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 8ef8241..04ad2be 100755 (executable)
@@ -12,10 +12,13 @@ import Gnuplot, Gnuplot.funcutils
 plot = Gnuplot.Gnuplot(persist=0)
 
 programs = {
-       "single-thread" : "./single-thread/nbody -g -v 5"
+       "single-thread" : "./single-thread/nbody -v 5"
 }
-for n in range(1, 5):
-       programs.update({"mthread"+str(n) : "./mthread/nbody -g -v 5 -n "+str(n)})
+for n in range(2, 6):
+       programs.update({"mthread"+str(n) : "./mthread/nbody -v 5 --pedantic-graphics -n "+str(n)})
+       programs.update({"mthread"+str(n) : "./mthread/nbody -v 5 -n "+str(n)})
+       #programs.update({"slow-mthread"+str(n) : "./mthread/nbody-slow --pedantic-graphics -v 5 -n "+str(n)})
+       #programs.update({"openmp"+str(n) : "./openmp/nbody -v 5 --pedantic-graphics -n " +str(n)})
 
 
 
@@ -29,8 +32,8 @@ def RunProgram(string):
 
 def RunForSteps(program, field, steps):
        results = []
-       #print(str(program) + " -s "+str(steps) + " " + str(field))
-       results.append(RunProgram(str(program) + " -s "+str(steps) + " " + str(field)))
+       print(str(program) + " -s "+str(steps) + " " + str(field))
+       results = RunProgram(str(program) + " -s "+str(steps) + " " + str(field))
        return results
 
 def VaryField(program, steps, fields):
@@ -56,12 +59,18 @@ def main(argv):
        fields = map(int, os.listdir("fields"))
        fields.sort(key = lambda e : e)
        
+       ignore = [10, 100]
        for n in fields:
+               if n in ignore:
+                       continue
                for p in data.keys():
-                       data[p] = [RunForSteps(programs[p], "fields/"+str(n), 200), n]
+                       data[p] = RunForSteps(programs[p], "fields/"+str(n), 200)
                        #print(str(data[p]))
-                       if (len(data[p]) > 0):
-                               plot.replot(Gnuplot.Data(data[p][0], title=str(p)+":"+str(n), with_="lp linecolor "+str(fields.index(n))))
+
+               #print(str(data.items()));
+               for d in sorted(data.items(), key = lambda e : e[1][len(e[1])-1][1]):
+                       if (len(d[1]) > 0):
+                               plot.replot(Gnuplot.Data(d[1], title=str(d[0])+":"+str(n), with_="lp")) #linecolor "+str(fields.index(n))))
 
        #print(str(data.items()))
        # Score the programs
diff --git a/course/semester2/pprog/assignment1/mthread/nbody-slow b/course/semester2/pprog/assignment1/mthread/nbody-slow
new file mode 100755 (executable)
index 0000000..9680b55
Binary files /dev/null and b/course/semester2/pprog/assignment1/mthread/nbody-slow differ
index 4acf28f..de9b8aa 100644 (file)
@@ -15,14 +15,21 @@ 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
+System * nested_sub_system = NULL; //Array of Systems for division of "universe" for the nested worker threads
 
 pthread_mutex_t mutex_runstate; // Mutex around the runstate
 
 
 
+pthread_attr_t attr; //thread attribute for the workers. 
+
+Barrier force_barrier; // I laughed at this variable name. A bit sad really.
+Barrier position_barrier;
+
+
+Barrier graphics_barrier;
+
+
 /**
  * @function Compute_Thread
  * @purpose Thread - Continuously computes steps for a system of bodies. Seperate from graphics functions.
@@ -39,6 +46,9 @@ void * Compute_Thread(void * arg)
        if (options.num_threads <= 0)
                options.num_threads = (DEFAULT_WORKING_THREADS > 1) ? DEFAULT_WORKING_THREADS : 1;
 
+       if (options.nested_threads <= 0)
+               options.nested_threads = 1;
+
        // Do a sanity check; there is no point spawning more threads than bodies.
        if (options.num_threads > s->N)
        {
@@ -48,58 +58,53 @@ void * Compute_Thread(void * arg)
                options.num_threads = s->N;
        }
        
+       pthread_attr_init(&attr);
+       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); //Needs to be detached, so that memory can be reused.
+       
        if (options.num_threads > 1) // Allocate worker threads and sub systems, as long as there would be more than 1
        {
-               worker_thread = (pthread_t*)(calloc(options.num_threads, sizeof(pthread_t)));
-               if (worker_thread == NULL)
-               {
-                       perror("Couldn't allocate array of worker threads");
-                       QuitProgram(true);
-                       pthread_exit(NULL);
-               }
-               sub_system = (System*)(calloc(options.num_threads, sizeof(System)));
-               if (sub_system == NULL)
+               worker_thread = Allocate_Threads(options.num_threads);
+               sub_system = Split_System(&universe, options.num_threads);
+
+               if (options.nested_threads > 1)
+                       nested_sub_system = Split_System(&universe, options.nested_threads);
+
+               #ifdef PERSISTENT_THREADS
+               for (unsigned i = 0; i < options.num_threads; ++i)
                {
-                       perror("Couldn't allocate array of systems for worker threads to use");
-                       QuitProgram(true);
-                       pthread_exit(NULL);
+                       if (pthread_create(worker_thread+i, & attr, Worker_Thread, (void*)(sub_system+i)) != 0)
+                       {
+                               perror("In compute thread, couldn't create worker thread");
+                               QuitProgram(true);
+                               pthread_exit(NULL);
+                       }
                }
+               #endif //PERSISTENT_THREADS
 
-               // Divide up the Body array owned by s into options.num_threads arrays, one for each worker thread
-               unsigned bodies_per_system = (s->N) / options.num_threads;
-               unsigned remainder = (s->N) % options.num_threads;
-               for (unsigned i = 0; i < options.num_threads; ++i)
+               
+       }
+       #ifdef PERSISTENT_THREADS
+       else
+       {
+               while (!ExitCondition())
                {
-                       sub_system[i].body = (s->body)+(i*bodies_per_system);
-                       sub_system[i].N = bodies_per_system;
-                       sub_system[i].steps = 0;
-                       if (i == options.num_threads - 1)
-                               sub_system[i].N += remainder; // The last thread gets the remainder
+                       if (options.verbosity != 0 && universe.steps % options.verbosity == 1)
+                               DisplayStatistics();
 
+                       // Just do everything in this thread 
+                       System_Forces(s, s);
+                       System_Positions(s);
                }
+               QuitProgram(false);
+               pthread_exit(NULL);
        }
 
-
-       pthread_attr_t attr; //thread attribute for the workers. 
-       pthread_attr_init(&attr);
-       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); //Needs to be detached, so that memory can be reused.
-
+       #else
        // The main computation loop
-       while (true)
+       while (!ExitCondition())
        {
-               //Check whether the program should quit due to steps being computed, or a timeout
-               if (ExitCondition())
-               {
-                       QuitProgram(false);
-                       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();
-               }
-
 
                if (options.num_threads <= 1)
                {
@@ -111,9 +116,6 @@ void * Compute_Thread(void * arg)
 
                
 
-               
-               workers_busy = options.num_threads; //All threads working
-
                //Compute forces
                for (unsigned i = 0; i < options.num_threads; ++i)
                {
@@ -123,19 +125,18 @@ 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(&force_barrier);
 
                //All the forces are now computed
-               
-               workers_busy = options.num_threads; //All threads working
+
+               if (options.draw_graphics && options.pedantic_graphics)
+               {
+                       Barrier_Wait(&graphics_barrier);
+               }
 
                //Compute positions
                for (unsigned i = 0; i < options.num_threads; ++i)
@@ -149,19 +150,144 @@ 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(&position_barrier);
+
 
                //Update number of steps computed
                universe.steps += 1;
 
+               
+
+               
+
+
+       }
+       QuitProgram(false);
+       #endif //PERSISTENT_THREADS
+       return NULL;
+}
+
+/**
+ * @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.verbosity != 0 && universe.steps % options.verbosity == 0)
+               DisplayStatistics();
+       //printf("BEFORE DRAW\n");
+       if (!options.pedantic_graphics)
+               return;
+       
+       Barrier_Wait(&position_barrier);
+
+
+       Barrier_Enter(&graphics_barrier);       
+}
+
+/**
+ * @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()
+{
+       universe.steps += 1;
+       if (!options.pedantic_graphics)
+               return;
+       Barrier_Leave(&graphics_barrier);
+       
+}
+
+#ifdef PERSISTENT_THREADS
+
+/**
+ * @function Worker_Thread
+ * @purpose Thread - Calculate stuff
+ */
+void * Worker_Thread(void * arg)
+{
+       System * s = (System*)(arg);
+
+
+       pthread_t * nested_workers = NULL;
+       System_ForcePair * system_pairs = NULL;
+       System * nested_position = NULL;
+       
+       Barrier nested_barrier; 
+       Barrier_Init(&nested_barrier);
+
+       printf("options.nested_threads == %d\n", (int)(options.nested_threads));
 
+       if (options.nested_threads != 1)        
+       {
 
+               system_pairs = (System_ForcePair*)(calloc(options.nested_threads, sizeof(System_ForcePair)));
+               if (system_pairs == NULL) // Handle tedious error cases
+               {
+                       perror("Couldn't allocate array of system pairs");
+                       QuitProgram(true);
+                       pthread_exit(NULL);
+               }
+               nested_workers = Allocate_Threads(options.nested_threads);
+               nested_position = 
+
+               for (unsigned i = 0; i < options.nested_threads; ++i)
+               {
+                       system_pairs[i].A = s;
+                       system_pairs[i].B = nested_sub_system+i;
+               }
        }
+
+       while (!ExitCondition())
+       {
+               
+               if (options.nested_threads == 1)
+               {
+                       Barrier_Enter(&force_barrier);
+                       System_Forces(s, &universe);
+                       Barrier_Leave(&force_barrier);
+               }
+               else
+               {
+                       for (unsigned i = 0; i < options.nested_threads; ++i)
+                       {
+                               if (pthread_create(nested_workers+i, &attr, Force_Thread, (void*)(system_pairs+i)) != 0)
+                               {
+                                       perror("In worker thread, couldn't create nested worker thread (force)");
+                                       QuitProgram(true);
+                                       free(nested_workers);
+                                       pthread_exit(NULL);                                     
+                               }       
+                       }
+               }
+               //printf("Computed forces for %p\n", arg);
+               Barrier_Wait(&force_barrier);
+               //printf("Computed ALL forces\n");
+
+               if (options.draw_graphics && options.pedantic_graphics)
+                       Barrier_Wait(&graphics_barrier);
+
+               Barrier_Enter(&position_barrier);
+               
+               System_Positions(s);
+
+               Barrier_Leave(&position_barrier);
+               //printf("Computed positions for %p\n", arg);
+               Barrier_Wait(&position_barrier);
+               //printf("Computed ALL positions\n");
+       }
+       printf("Worker thread exits\n");
+       QuitProgram(false);
+       pthread_exit(NULL);
 }
 
+#endif //PERSISTENT_THREADS
+
 /**
  * @function Force_Thread
  * @purpose Thread - Calculates the forces on objects in a System
@@ -169,19 +295,17 @@ void * Compute_Thread(void * arg)
  */
 void * Force_Thread(void * s)
 {
-       
-       System_Forces((System*)s, &universe); //Simple wrapper
+       System_ForcePair * pair = (System_ForcePair*)s;
+       Barrier_Enter(&force_barrier);
+
+       System_Forces(pair->A, pair->B); //Simple wrapper
+
+       Barrier_Leave(&force_barrier);
 
-       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);
        return NULL;
 }
 
+
 /**
  * @function Position_Thread
  * @purpose Thread - Calculates the positions of objects in a System 
@@ -189,16 +313,9 @@ void * Force_Thread(void * s)
  */
 void * Position_Thread(void * s)
 {
-       
+       Barrier_Enter(&position_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(&position_barrier);
        return NULL;
 }      
 
@@ -249,17 +366,115 @@ void Simulation_Run(int argc, char ** argv)
 {
        atexit(Thread_Cleanup);
 
+       Barrier_Init(&force_barrier);
+       Barrier_Init(&position_barrier);
+       Barrier_Init(&graphics_barrier);
+
+       
        if (options.draw_graphics)
        {
                // The graphics are enabled, so create a thread to do computations
                // Graphics are done in the main loop
+               //printf("Graphics are enabled\n");
+               #ifdef PERSISTENT_THREADS
+               Compute_Thread((void*)(&universe));
+               #else
                if (pthread_create(&compute_thread, NULL, Compute_Thread, (void*)&universe) != 0)
                {
                        perror("Error creating compute thread");
                        exit(EXIT_FAILURE);
                }
+               #endif //PERSISTENT_THREADS
+               //printf("Run compute thread\n");
                Graphics_Run(argc, 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));
+}
+
+/**
+ * @function Split_System
+ * @purpose Helper to divide one system into an array of systems
+ *     Each sub system will have N = (s->N / n) bodies in it
+ * @param s - The original system (typically &universe)
+ * @param n - The number of sub systems in the array
+ *
+ * WARNING: It is the caller's responsibility to free() the returned array
+ */
+System * Split_System(System * s, unsigned n)
+{
+       System * result = (System*)(calloc(n, sizeof(System)));
+       if (result == NULL)
+       {
+               perror("Couldn't create array of sub systems");
+               QuitProgram(true);
+               pthread_exit(NULL);
+       }
+
+       unsigned n_per_system = (s->N) / n;
+       unsigned remainder = (s->N) % n;
+
+       for (unsigned i = 0; i < n; ++i)        
+       {
+               result[i].N = n_per_system;
+               if (i == n-1)
+                       result[i].N += remainder;
+               result[i].body = (s->body) + (n_per_system * i);
+               result[i].steps = 0;
+       }
+       return result;
+}
+
+/**
+ * @function Allocate_Threads
+ * @purpose Helper function to allocate an array of pthread_t objects
+ *     Handles all the pointless, er, "important" error checking that should be done
+ * @param n - Number of threads in the array
+ * 
+ * WARNING: Remember to free() the array!!!
+ */
+pthread_t * Allocate_Threads(unsigned n)
+{
+       pthread_t * result = (pthread_t*)(calloc(n, sizeof(pthread_t)));
+       if (result == NULL)
+       {
+               perror("Unable to allocate memory for threads");
+               QuitProgram(true);
+               pthread_exit(NULL);
+       }
+       return result;
+}
index 0b3208a..f2de022 100644 (file)
 
 #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);
 #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.
 
-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
 
+System * Split_System(System * s, unsigned n); // Splits one system into a number of other systems, returns an array of size n
+pthread_t * Allocate_Threads(unsigned n); // Allocates space for threads - handles errors
 
+#ifdef PERSISTENT_THREADS
+void * Worker_Thread(void * arg);
+#endif //PERSISTENT_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
 
 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
+/**
+ * Structure to represent a barrier for multiple threads
+ * @param mutex - Mutex around the counter
+ * @param busy - Counter of threads within the barrier
+ * @param threads_done_cv - Condition to wake up threads waiting on barrier once all working threads have left it
+ */
+typedef struct
+{
+       pthread_mutex_t mutex;
+       unsigned threads_busy; // Counter of threads which are busy
+       pthread_cond_t threads_done_cv;
+} Barrier;
+
+/**
+ * Structure to represent a pair of Systems; passed to Force_Thread
+ * @param A - System to calculate forces for
+ * @param B - System causing forces on System A
+ */
+typedef struct
+{
+       System * A;
+       System * B;
+} System_ForcePair;
 
-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;
+void Barrier_Init(Barrier * b);
+void Barrier_Enter(Barrier * b);
+void Barrier_Leave(Barrier * b);
+void Barrier_Wait(Barrier * b);
 
 #endif //_NBODY_MTHREAD_H
index 6eb9ed4..1d2f369 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,41 @@ 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.verbosity != 0 && universe.steps % options.verbosity == 0)
+               DisplayStatistics();
+       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..32ca080 100644 (file)
@@ -22,6 +22,9 @@ float look[3];
 int windowWidth, windowHeight, upY;
 double scale = 1.0;
 
+#ifdef FLYING_CAMERA
+Camera eye;
+#endif //FLYING_CAMERA
 
 /**
  * @function Graphics_Run
@@ -33,10 +36,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);  
@@ -96,6 +98,12 @@ void Graphics_Run(int argc, char ** argv)
        look[2] = 0;
        gluPerspective(VIEW_ANGLE, (double)WIDTH/(double)HEIGHT, WORLD_NEAR, WORLD_FAR);  
 
+       #ifdef FLYING_CAMERA
+       eye.x[0] = 1; eye.x[1] = 0; eye.x[2] = 0;
+       eye.y[0] = 0; eye.y[1] = 1; eye.y[2] = 0;
+       eye.z[0] = 0; eye.z[1] = 0; eye.z[2] = 1;
+       #endif //FLYING_CAMERA
+
        glutMainLoop();   
 }
 
@@ -107,40 +115,55 @@ 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);
        glLoadIdentity();
+
+       #ifdef FLYING_CAMERA
+       gluLookAt(eye.p[0], eye.p[1], eye.p[2], 
+               eye.p[0] + eye.x[0], eye.p[1] + eye.x[1], eye.p[2] + eye.x[2],
+               eye.z[0], eye.z[1], eye.z[2]);
+       #else
        gluLookAt(eyeRho * sin(eyePhi) * sin(eyeTheta), eyeRho * cos(eyePhi),
                eyeRho * sin(eyePhi) * cos(eyeTheta),look[0], look[1], look[2], 0, upY, 0);
+       #endif //FLYING_CAMERA
+
+       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;
                glColor3f(0.0f, b->mass/1e11*100, 0.0f);
+               //glColor3f(1.0f, 0.0f, 0.0f);
                glPushMatrix(); // to save the current matrix
                glTranslated(scale*b->x[0], scale*b->x[1], scale*b->x[2]);
                glutSolidSphere (BALL_SIZE, 10, 10);
                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
+
 }
 
 /**
@@ -157,6 +180,149 @@ void Graphics_Keyboard(unsigned char theKey, int mouseX, int mouseY)
                return;
        }
 
+       #ifdef FLYING_CAMERA
+       switch (theKey)
+       {
+               case 'W':
+               case 'w':
+                       for (unsigned i = 0; i < DIMENSIONS; ++i)
+                               eye.p[i] += eye.x[i];
+                       break;
+               case 'S':
+               case 's':
+                       for (unsigned i = 0; i < DIMENSIONS; ++i)
+                               eye.p[i] -= eye.x[i];
+                       break;
+               case 'A':
+               case 'a':
+                       for (unsigned i = 0; i < DIMENSIONS; ++i)
+                               eye.p[i] += eye.y[i];
+                       break;
+               case 'D':
+               case 'd':
+                       for (unsigned i = 0; i < DIMENSIONS; ++i)
+                               eye.p[i] -= eye.y[i];
+                       break;
+               case 'Z':
+               case 'z':
+                       for (unsigned i = 0; i < DIMENSIONS; ++i)
+                               eye.p[i] += eye.z[i];
+                       break;
+               case 'C':
+               case 'c':
+                       for (unsigned i = 0; i < DIMENSIONS; ++i)
+                               eye.p[i] -= eye.z[i];
+                       break;
+
+               case 'I':
+               case 'i':
+               {
+                       float theta = M_PI/80.0;
+                       Camera old = eye;
+
+                       for (unsigned i = 0; i < DIMENSIONS; ++i)
+                       {
+                               eye.z[i] = old.z[i] * cos(theta) + old.x[i] * sin(theta);
+                               eye.x[i] = old.x[i] * cos(theta) - old.z[i] * sin(theta);
+                       }
+                       break;
+               }
+               case 'K':
+               case 'k':
+               {
+                       float theta = -M_PI/80.0;
+                       Camera old = eye;
+
+                       for (unsigned i = 0; i < DIMENSIONS; ++i)
+                       {
+                               eye.z[i] = old.z[i] * cos(theta) + old.x[i] * sin(theta);
+                               eye.x[i] = old.x[i] * cos(theta) - old.z[i] * sin(theta);
+                       }
+                       break;
+               }
+               
+               case 'J':
+               case 'j':
+               {
+                       float theta = -M_PI/80.0;
+                       Camera old = eye;
+
+                       for (unsigned i = 0; i < DIMENSIONS; ++i)
+                       {
+                               eye.y[i] = old.y[i] * cos(theta) + old.x[i] * sin(theta);
+                               eye.x[i] = old.x[i] * cos(theta) - old.y[i] * sin(theta);
+                       }
+                       break;
+               }
+
+               case 'L':
+               case 'l':
+               {
+                       float theta = M_PI/80.0;
+                       Camera old = eye;
+
+                       for (unsigned i = 0; i < DIMENSIONS; ++i)
+                       {
+                               eye.y[i] = old.y[i] * cos(theta) + old.x[i] * sin(theta);
+                               eye.x[i] = old.x[i] * cos(theta) - old.y[i] * sin(theta);
+                       }
+                       break;
+               }
+
+               case 'Q':
+               case 'q':
+               {
+                       float theta = M_PI/80.0;
+                       Camera old = eye;
+
+                       for (unsigned i = 0; i < DIMENSIONS; ++i)
+                       {
+                               eye.z[i] = old.z[i] * cos(theta) + old.y[i] * sin(theta);
+                               eye.y[i] = old.y[i] * cos(theta) - old.z[i] * sin(theta);
+                       }
+                       break;
+               }
+               case 'E':
+               case 'e':
+               {
+                       float theta = -M_PI/80.0;
+                       Camera old = eye;
+
+                       for (unsigned i = 0; i < DIMENSIONS; ++i)
+                       {
+                               eye.z[i] = old.z[i] * cos(theta) + old.y[i] * sin(theta);
+                               eye.y[i] = old.y[i] * cos(theta) - old.z[i] * sin(theta);
+                       }
+                       break;
+               }       
+       }
+
+       /*
+       float x = 0; float y = 0; float z = 0;
+       for (unsigned i = 0; i < DIMENSIONS; ++i)
+       {
+               x += eye.x[i] * eye.x[i];
+               y += eye.y[i] * eye.y[i];
+               z += eye.z[i] * eye.z[i];
+       }
+       for (unsigned i = 0; i < DIMENSIONS; ++i)
+       {
+               eye.x[i] /= sqrt(x);
+               eye.y[i] /= sqrt(y);
+               eye.z[i] /= sqrt(z);
+       }
+       */
+       system("clear");
+       printf("Camera status:\n");
+       printf("Position: %f %f %f\n", eye.p[0], eye.p[1], eye.p[2]);
+       printf("x: %f %f %f (%f)\n", eye.x[0], eye.x[1], eye.x[2], sqrt(eye.x[0]*eye.x[0] + eye.x[1]*eye.x[1] + eye.x[2]*eye.x[2]));
+       printf("y: %f %f %f (%f)\n", eye.y[0], eye.y[1], eye.y[2], sqrt(eye.y[0]*eye.y[0] + eye.y[1]*eye.y[1] + eye.y[2]*eye.y[2]));
+       printf("z: %f %f %f (%f)\n", eye.z[0], eye.z[1], eye.z[2], sqrt(eye.z[0]*eye.z[0] + eye.z[1]*eye.z[1] + eye.z[2]*eye.z[2]));
+
+       #else
+       
+       
+
                if (theKey == 'i' || theKey == 'I') {
                        eyePhi -= M_PI / 20;
                if (eyePhi == 0)
@@ -186,6 +352,7 @@ void Graphics_Keyboard(unsigned char theKey, int mouseX, int mouseY)
                }
                if (sin(eyePhi) > 0) upY = 1;
                else upY = 1;
+       #endif //FLYING_CAMERA
 }
 
 /**
index c2b99f3..97ccf47 100644 (file)
@@ -14,7 +14,7 @@
 #include "nbody.h"
 
 #define WIDTH 800
-#define HEIGHT 800
+#define HEIGHT 640
 #define POINT_SIZE 1
 #define POSITION_X 112
 #define POSITION_Y 20
@@ -24,7 +24,7 @@
 #define WORLD_TOP 10000
 #define VIEW_ANGLE 45
 #define RHO 100
-#define WORLD_NEAR 0.1
+#define WORLD_NEAR 0.0001
 #define WORLD_FAR 1000000
 #define BALL_SIZE 0.5
 #define REFRESH_RATE 0.001
@@ -36,6 +36,20 @@ void Graphics_Keyboard(unsigned char key, int mouse_x, int mouse_y);
 void Graphics_Reshape(int width, int height);
 
 
+#define FLYING_CAMERA
+
+#ifdef FLYING_CAMERA
+typedef struct
+{
+       float p[DIMENSIONS]; // Translation position of the camera
+       
+       float x[DIMENSIONS];
+       float y[DIMENSIONS];
+       float z[DIMENSIONS];
+       
+} Camera;
+#endif //FLYING_CAMERA
+
 #endif //_GRAPHICS_H
 
 //EOF
index 4b0c48a..e9ccb41 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;
 
@@ -82,7 +85,7 @@ int main(int argc, char** argv)
        options.start_clock = clock(); //Get CPU cycles executed before simulation starts
        Simulation_Run(argc, argv); // Start the simulation
        
-       printf("Got here!\n");
+       //printf("Got here!\n");
        exit(EXIT_FAILURE); //Should never get to this line
 }
 
@@ -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..e4b4bc7 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) 
 {
@@ -234,6 +235,8 @@ void DisplayStatistics()
  */
 bool ExitCondition(void)
 {
-       return (runstate != RUN || (options.timeout > 0.00 && ((unsigned)(time(NULL) - options.start_time.tv_sec) >= options.timeout))
+       bool result = (runstate != RUN || (options.timeout > 0.00 && ((unsigned)(time(NULL) - options.start_time.tv_sec) >= options.timeout))
                || (options.num_steps > 0 && universe.steps > options.num_steps));
+       //printf("runstate %d\n timeout %d\n steps %d\n", (int)(runstate != RUN), (int)(options.timeout > 0.00 && ((unsigned)(time(NULL) - options.start_time.tv_sec) >= options.timeout)), (int)(options.num_steps > 0 && universe.steps > options.num_steps));
+       return result;
 }
index fc19d05..4d53f9d 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 == 0) \
+       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