AUTH's THMMY "Parallel and distributed systems" course assignments.
 
 
 
 
 
 

256 line
8.3 KiB

  1. /*!
  2. * \file
  3. * \brief Main application file for PDS HW2 (MPI)
  4. *
  5. * \author
  6. * Christos Choutouridis AEM:8997
  7. * <cchoutou@ece.auth.gr>
  8. */
  9. #include <exception>
  10. #include <iostream>
  11. #include <algorithm>
  12. #include <random>
  13. #include "utils.hpp"
  14. #include "config.h"
  15. #include "distsort.hpp"
  16. // Global session data
  17. config_t config;
  18. MPI_t<> mpi;
  19. distBuffer_t Data;
  20. Log logger;
  21. Timing Ttotal;
  22. /*!
  23. * A small command line argument parser
  24. * \return The status of the operation
  25. */
  26. bool get_options(int argc, char* argv[]){
  27. bool status =true;
  28. // iterate over the passed arguments
  29. for (int i=1 ; i<argc ; ++i) {
  30. std::string arg(argv[i]); // get current argument
  31. if (arg == "-q" || arg == "--array-size") {
  32. if (i+1 < argc) {
  33. config.arraySize = 1 << atoi(argv[++i]);
  34. }
  35. else {
  36. status = false;
  37. }
  38. }
  39. else if (arg == "--pipeline") {
  40. if (i+1 < argc) {
  41. auto stages = atoi(argv[++i]);
  42. if (isPowerOfTwo(stages) && stages <= static_cast<int>(MAX_PIPELINE_SIZE))
  43. config.pipeline = stages;
  44. else
  45. status = false;
  46. }
  47. else {
  48. status = false;
  49. }
  50. }
  51. else if (arg == "--validation") {
  52. config.validation = true;
  53. }
  54. else if (arg == "--perf") {
  55. config.perf = true;
  56. }
  57. else if (arg == "--ndebug") {
  58. config.ndebug = true;
  59. }
  60. else if (arg == "-v" || arg == "--verbose") {
  61. config.verbose = true;
  62. }
  63. else if (arg == "-h" || arg == "--help") {
  64. std::cout << "distbitonic/distbubbletonic - A distributed bitonic sort\n\n";
  65. std::cout << "distbitonic -q <N> [--pipeline N] [--validation] [--ndebug] [-v]\n";
  66. std::cout << "distbitonic -h\n";
  67. std::cout << "distbubbletonic -q <N> [--pipeline N] [--validation] [--ndebug] [-v]\n";
  68. std::cout << "distbubbletonic -h\n";
  69. std::cout << '\n';
  70. std::cout << "Options:\n\n";
  71. std::cout << " -q | --array-size <N>\n";
  72. std::cout << " Selects the array size according to size = 2^N\n\n";
  73. std::cout << " --pipeline <N>\n";
  74. std::cout << " Request a pipeline of <N> stages for exchange-minmax\n";
  75. std::cout << " N must be power of 2 up to " << MAX_PIPELINE_SIZE << "\n\n";
  76. std::cout << " --validation\n";
  77. std::cout << " Request a full validation at the end, performed by process rank 0\n\n";
  78. std::cout << " --perf\n";
  79. std::cout << " Request performance timing measurements to stdout.\n\n";
  80. std::cout << " --ndebug\n";
  81. std::cout << " Skip debug breakpoint when on debug build.\n\n";
  82. std::cout << " -v | --verbose\n";
  83. std::cout << " Request a more verbose output to stdout.\n\n";
  84. std::cout << " -h | --help\n";
  85. std::cout << " Prints this and exit.\n\n";
  86. std::cout << "Examples:\n\n";
  87. std::cout << " mpirun -np 4 distbitonic -q 24\n";
  88. std::cout << " Runs distbitonic in 4 MPI processes with 2^24 array points each\n\n";
  89. std::cout << " mpirun -np 16 distbubbletonic -q 20\n";
  90. std::cout << " Runs distbubbletonic in 16 MPI processes with 2^20 array points each\n\n";
  91. exit(0);
  92. }
  93. else { // parse error
  94. std::cout << "Invocation error. Try -h for details.\n";
  95. status = false;
  96. }
  97. }
  98. return status;
  99. }
  100. /*!
  101. * A simple validator for the entire distributed process
  102. *
  103. * @tparam ShadowedDataT A Shadowed buffer type with random access iterator.
  104. *
  105. * @param data [ShadowedDataT] The local to MPI process
  106. * @param Processes [mpi_id_t] The total number of MPI processes
  107. * @param rank [mpi_id_t] The current process id
  108. *
  109. * @return [bool] True if all are sorted and in total ascending order
  110. */
  111. template<typename ShadowedDataT>
  112. bool validator(ShadowedDataT& data, mpi_id_t Processes, mpi_id_t rank) {
  113. using value_t = typename ShadowedDataT::value_type;
  114. bool ret = true; // Have faith!
  115. // Local results
  116. value_t lmin = data.front();
  117. value_t lmax = data.back();
  118. value_t lsort = static_cast<value_t>(std::is_sorted(data.begin(), data.end()));
  119. // Gather min/max/sort to rank 0
  120. std::vector<value_t> mins(Processes);
  121. std::vector<value_t> maxes(Processes);
  122. std::vector<value_t> sorts(Processes);
  123. MPI_Datatype datatype = MPI_TypeMapper<value_t>::getType();
  124. MPI_Gather(&lmin, 1, datatype, mins.data(), 1, datatype, 0, MPI_COMM_WORLD);
  125. MPI_Gather(&lmax, 1, datatype, maxes.data(), 1, datatype, 0, MPI_COMM_WORLD);
  126. MPI_Gather(&lsort, 1, datatype, sorts.data(), 1, datatype, 0, MPI_COMM_WORLD);
  127. // Check all results
  128. if (rank == 0) {
  129. for (mpi_id_t r = 1; r < Processes; ++r) {
  130. if (sorts[r] == 0)
  131. ret = false;
  132. if (maxes[r - 1] > mins[r])
  133. ret = false;
  134. }
  135. }
  136. return ret;
  137. }
  138. #if !defined TESTING
  139. /*!
  140. * @return Returns 0, but.... we may throw or exit(1)
  141. */
  142. int main(int argc, char* argv[]) try {
  143. // Initialize MPI environment
  144. mpi.init(&argc, &argv);
  145. // try to read command line (after MPI parsing)
  146. if (!get_options(argc, argv))
  147. exit(1);
  148. logger << "MPI environment initialized." <<
  149. " Rank: " << mpi.rank() <<
  150. " Size: " << mpi.size() <<
  151. logger.endl;
  152. #if defined DEBUG
  153. #if defined TESTING
  154. /*
  155. * In case of a debug build we will wait here until sleep_wait
  156. * will reset via debugger. In order to do that the user must attach
  157. * debugger to all processes. For example:
  158. * $> mpirun -np 2 ./<program path>
  159. * $> ps aux | grep <program>
  160. * $> gdb <program> <PID1>
  161. * $> gdb <program> <PID2>
  162. */
  163. volatile bool sleep_wait = false;
  164. #else
  165. volatile bool sleep_wait = true;
  166. #endif
  167. while (sleep_wait && !config.ndebug)
  168. sleep(1);
  169. #endif
  170. // Initialize local data
  171. logger << "Initialize local array of " << config.arraySize << " elements" << logger.endl;
  172. std::random_device rd; // Mersenne seeded from hw if possible. range: [type_min, type_max]
  173. std::mt19937 gen(rd());
  174. std::uniform_int_distribution<distValue_t > dis(
  175. std::numeric_limits<distValue_t>::min(),
  176. std::numeric_limits<distValue_t>::max()
  177. );
  178. // Fill vector
  179. Data.resize(config.arraySize);
  180. std::generate(Data.begin(), Data.end(), [&]() { return dis(gen); });
  181. // Run distributed sort
  182. if (mpi.rank() == 0)
  183. logger << "Starting distributed sorting ... ";
  184. Ttotal.start();
  185. #if CODE_VERSION == BUBBLETONIC
  186. distBubbletonic(Data, mpi.size(), mpi.rank());
  187. #else
  188. distBitonic (Data, mpi.size(), mpi.rank());
  189. #endif
  190. Ttotal.stop();
  191. if (mpi.rank() == 0)
  192. logger << " Done." << logger.endl;
  193. // Print-outs and validation
  194. if (config.perf) {
  195. Ttotal.print_duration("Total ", mpi.rank());
  196. TfullSort.print_duration("Full-Sort ", mpi.rank());
  197. Texchange.print_duration("Exchange ", mpi.rank());
  198. Tminmax.print_duration("Min-Max ", mpi.rank());
  199. TelbowSort.print_duration("Elbow-Sort", mpi.rank());
  200. }
  201. if (config.validation) {
  202. // If requested, we have the chance to fail!
  203. if (mpi.rank() == 0)
  204. std::cout << "Results validation ...";
  205. bool val = validator(Data, mpi.size(), mpi.rank());
  206. if (mpi.rank() == 0)
  207. std::cout << ((val) ? "\x1B[32m [PASS] \x1B[0m\n" : " \x1B[32m [FAIL] \x1B[0m\n");
  208. }
  209. mpi.finalize();
  210. return 0;
  211. }
  212. catch (std::exception& e) {
  213. //we probably pollute the user's screen. Comment `cerr << ...` if you don't like it.
  214. std::cerr << "Error: " << e.what() << '\n';
  215. exit(1);
  216. }
  217. #else
  218. #include <gtest/gtest.h>
  219. #include <exception>
  220. /*!
  221. * The testing version of our program
  222. */
  223. GTEST_API_ int main(int argc, char **argv) try {
  224. testing::InitGoogleTest(&argc, argv);
  225. return RUN_ALL_TESTS();
  226. }
  227. catch (std::exception& e) {
  228. std::cout << "Exception: " << e.what() << '\n';
  229. }
  230. #endif