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.
--- /dev/null
+# 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 *~
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
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();
}
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
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
#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.
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
#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
omp_set_num_threads(1);
-
+ omp_init_lock(&graphics_lock);
#pragma omp parallel sections
{
break;
}
-
- if (options.draw_graphics == false && options.verbosity != 0
- && universe.steps % options.verbosity == 1)
+ if (options.verbosity != 0 && universe.steps % options.verbosity == 1)
{
DisplayStatistics();
}
}
}
+
+ omp_destroy_lock(&graphics_lock);
}
/**
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)
{
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);
}
//#undef QuitProgram
//void QuitProgram(bool error);
+#undef BeforeDraw
+void BeforeDraw();
+#undef AfterDraw
+void AfterDraw();
+
void Compute(); //Compute a single step
#endif //_NBODY_OPENMP_H
{
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);
*/
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);
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;
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
+
}
/**
#include <stdio.h>
#include <time.h>
#include <signal.h>
+#include <string.h> // For parsing arguments
#include "nbody.h"
#include "graphics.h"
// --- 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
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;
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);
* @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
* @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)
{
#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
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