X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=course%2Fsemester2%2Fpprog%2Fassignment1%2Fopenmp%2Fnbody.c;h=bc2d1732dc4022141d1a8b9aae7d4120f3f703d1;hb=0217abbb1034188ecbfdf034678cf6867381e561;hp=6eb9ed4a66e191579fa4c1df2d2e61b0fa5168c6;hpb=19e590cc4f7dbc186cad9418bf5a2321bfa3fe1a;p=matches%2Fhonours.git diff --git a/course/semester2/pprog/assignment1/openmp/nbody.c b/course/semester2/pprog/assignment1/openmp/nbody.c index 6eb9ed4a..bc2d1732 100644 --- a/course/semester2/pprog/assignment1/openmp/nbody.c +++ b/course/semester2/pprog/assignment1/openmp/nbody.c @@ -8,6 +8,13 @@ #include "../single-thread/nbody.c" // Include original code #include "graphics.h" // For Graphics_Run function only + +#ifdef OVER_ENGINEERED +System * sub_system = NULL; +#endif //OVER_ENGINEERED + +omp_lock_t runstate_lock; + /** * @function Simulation_Run * @purpose Start the simulation @@ -15,94 +22,135 @@ */ void Simulation_Run(int argc, char ** argv) { - omp_set_nested(1); - if (omp_get_nested() != 1) - { - fprintf(stderr, "Couldn't set nested parallelism, I kind of need that\n"); - exit(EXIT_FAILURE); - } - - + omp_init_lock(&runstate_lock); if (options.num_threads == 0) options.num_threads = omp_get_max_threads(); if (options.draw_graphics) - omp_set_num_threads(2); - else - omp_set_num_threads(1); - - - - - #pragma omp parallel sections - { - - #pragma omp section + { + Graphics_Run(argc, argv); + if (options.pedantic_graphics == false) { - omp_set_num_threads(options.num_threads); - while (true) + options.num_threads += 1; + omp_set_nested(1); + if (omp_get_nested() == 0) { - - if (runstate != RUN) break; //Check whether the main thread wants to exit - - if (ExitCondition()) - { - break; - } + fprintf(stderr, "Couldn't enable nested parallelism\n."); + exit(EXIT_FAILURE); + } - if (options.draw_graphics == false && options.verbosity != 0 - && universe.steps % options.verbosity == 1) + #pragma omp parallel num_threads(2) + { + // This can't be done with sections! + // Because glut is useless, and can only be dealt with in the main thread + // I just hope that thread "0" is always the main thread! + if (omp_get_thread_num() == 0) // #pragma omp section { - DisplayStatistics(); + //printf("Thread %d gets graphics\n", omp_get_thread_num()); + glutMainLoop(); + QuitProgram(false); // Program gets to here if the window is quit + } + else // #pragma omp section + { + //printf("Thread %d gets computations\n", omp_get_thread_num()); + Compute(); } - - - Compute(); } - //printf("Left compute loop\n"); + return; } + } + Compute(); - #pragma omp section - { - if (options.draw_graphics) - Graphics_Run(argc, argv); - //printf("Got to bit after Graphics_Run()\n"); - } - } + omp_destroy_lock(&runstate_lock); } -/** - * @function Compute - * @purpose Compute a single step, multithreaded using OpenMP - */ -void Compute(void) + +void Compute(void) { - //printf("There are %d threads working and %d available\n", omp_get_num_threads(), omp_get_max_threads()); - //return; - unsigned n_per_thread = (universe.N) / options.num_threads; - unsigned remainder = (universe.N) % options.num_threads; - #pragma omp parallel for - for (unsigned i = 0; i < options.num_threads; ++i) + omp_set_num_threads(options.num_threads); + if (omp_get_max_threads() != options.num_threads) { - //printf("Thread %d executes iteration %u\n", omp_get_thread_num(), i); - System s; - s.body = universe.body + (i * n_per_thread); - s.N = (i == options.num_threads - 1) ? n_per_thread : n_per_thread + remainder; - System_Forces(&s, &universe); + fprintf(stderr, "Couldn't use %d threads!\n", options.num_threads); + exit(EXIT_FAILURE); } - #pragma omp parallel for - for (unsigned i = 0; i < options.num_threads; ++i) + #ifdef OVER_ENGINEERED + sub_system = Split_System(&universe, options.num_threads); // I could use omp for loops instead, but I like this way + #endif //OVER_ENGINEERED + + #pragma omp parallel { - //printf("Thread %d executes iteration %u\n", omp_get_thread_num(), i); - System s; - s.body = universe.body + (i * n_per_thread); - s.N = (i == options.num_threads - 1) ? n_per_thread : n_per_thread + remainder; - System_Positions(&s); - } + #ifdef OVER_ENGINEERED + int index = omp_get_thread_num(); + #endif //OVER_ENGINEERED + while (!ExitCondition()) + { - universe.steps += 1; + #ifdef OVER_ENGINEERED + System_Forces(sub_system+index, &universe); + #pragma omp barrier // Look! An explicit barrier! + System_Positions(sub_system+index); + #pragma omp barrier + #else + #pragma omp for + for (unsigned a = 0; a < universe.N; ++a) + { + for (unsigned i = 0; i < DIMENSIONS; ++i) + (universe.body+a)->F[i] = 0; + //printf("Thread %d computes force for body %d\n", omp_get_thread_num(), a); + omp_set_num_threads(options.nested_threads); + #pragma omp parallel for + for (unsigned b = 0; b < universe.N; ++b) + { + if (b != a) + Body_Force(universe.body+a, universe.body+b); + } + } + + #pragma omp for + for (unsigned a = 0; a < universe.N; ++a) + { + //printf("Thread %d computes position for body %d\n", omp_get_thread_num(), a); + Body_Velocity(universe.body+a); + Body_Position(universe.body+a); + } + + #endif //OVER_ENGINEERED + #pragma omp master + { + // The if statement has a cost, but if can be removed easily in an optimised version. + // Only the main thread is allowed to mess with glut, because glut is evil (not thread safe). + // Although this function may be nested, that will only happen if the if would be false. + // ("if the if would be false"; I am awesome at explaining code) + if (options.draw_graphics && options.pedantic_graphics) + { + glutMainLoopEvent(); + Graphics_Display(); + } + } + #pragma omp single + { + // Only one thread should execute this, and we don't care which one. + // So much easier than the pthread version! + universe.steps += 1; + if (options.verbosity != 0 && universe.steps % options.verbosity == 0) + DisplayStatistics(); + } + + } + } + QuitProgram(false); +} + +void QuitProgram(bool error) +{ + if (runstate == QUIT || runstate == QUIT_ERROR) + return; + omp_set_lock(&runstate_lock); + runstate = (error) ? QUIT_ERROR : QUIT; + omp_unset_lock(&runstate_lock); } +