AUTH's THMMY "Parallel and distributed systems" course assignments.
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 
 

389 linhas
14 KiB

  1. /**
  2. * \file
  3. * \brief Utilities header
  4. *
  5. * \author
  6. * Christos Choutouridis AEM:8997
  7. * <cchoutou@ece.auth.gr>
  8. */
  9. #ifndef UTILS_HPP_
  10. #define UTILS_HPP_
  11. #include <vector>
  12. #include <iostream>
  13. #include <chrono>
  14. #include <unistd.h>
  15. #include <mpi.h>
  16. //#include <functional>
  17. #include "config.h"
  18. /*
  19. * MPI_<type> dispatcher mechanism
  20. */
  21. template <typename T> struct MPI_TypeMapper { };
  22. template <> struct MPI_TypeMapper<char> { static MPI_Datatype getType() { return MPI_CHAR; } };
  23. template <> struct MPI_TypeMapper<short> { static MPI_Datatype getType() { return MPI_SHORT; } };
  24. template <> struct MPI_TypeMapper<int> { static MPI_Datatype getType() { return MPI_INT; } };
  25. template <> struct MPI_TypeMapper<long> { static MPI_Datatype getType() { return MPI_LONG; } };
  26. template <> struct MPI_TypeMapper<long long> { static MPI_Datatype getType() { return MPI_LONG_LONG; } };
  27. template <> struct MPI_TypeMapper<unsigned char> { static MPI_Datatype getType() { return MPI_UNSIGNED_CHAR; } };
  28. template <> struct MPI_TypeMapper<unsigned short>{ static MPI_Datatype getType() { return MPI_UNSIGNED_SHORT; } };
  29. template <> struct MPI_TypeMapper<unsigned int> { static MPI_Datatype getType() { return MPI_UNSIGNED; } };
  30. template <> struct MPI_TypeMapper<unsigned long> { static MPI_Datatype getType() { return MPI_UNSIGNED_LONG; } };
  31. template <> struct MPI_TypeMapper<unsigned long long> { static MPI_Datatype getType() { return MPI_UNSIGNED_LONG_LONG; } };
  32. template <> struct MPI_TypeMapper<float> { static MPI_Datatype getType() { return MPI_FLOAT; } };
  33. template <> struct MPI_TypeMapper<double> { static MPI_Datatype getType() { return MPI_DOUBLE; } };
  34. /*!
  35. * MPI wrapper type to provide MPI functionality and RAII to MPI as a resource
  36. *
  37. * @tparam TID The MPI type for process id [default: int]
  38. */
  39. template<typename TID = int>
  40. struct MPI_t {
  41. using ID_t = TID; // Export TID type (currently int defined by the standard)
  42. /*!
  43. * Initializes the MPI environment, must called from each process
  44. *
  45. * @param argc [int*] POINTER to main's argc argument
  46. * @param argv [char***] POINTER to main's argv argument
  47. */
  48. void init(int* argc, char*** argv) {
  49. // Initialize the MPI environment
  50. int err;
  51. if ((err = MPI_Init(argc, argv)) != MPI_SUCCESS)
  52. mpi_throw(err, "(MPI) MPI_Init() - ");
  53. initialized_ = true;
  54. // Get the number of processes
  55. int size_value, rank_value;
  56. if ((err = MPI_Comm_size(MPI_COMM_WORLD, &size_value)) != MPI_SUCCESS)
  57. mpi_throw(err, "(MPI) MPI_Comm_size() - ");
  58. if ((err = MPI_Comm_rank(MPI_COMM_WORLD, &rank_value)) != MPI_SUCCESS)
  59. mpi_throw(err, "(MPI) MPI_Comm_rank() - ");
  60. size_ = static_cast<ID_t>(size_value);
  61. rank_ = static_cast<ID_t>(rank_value);
  62. // Get the name of the processor
  63. char processor_name[MPI_MAX_PROCESSOR_NAME];
  64. int name_len;
  65. if ((err = MPI_Get_processor_name(processor_name, &name_len)) != MPI_SUCCESS)
  66. mpi_throw(err, "(MPI) MPI_Get_processor_name() - ");
  67. name_ = std::string (processor_name, name_len);
  68. }
  69. /*!
  70. * Exchange data with partner as part of the sorting network of both bubbletonic or bitonic
  71. * sorting algorithms.
  72. *
  73. * This function matches a transmit and a receive in order for fully exchanged data between
  74. * current node and partner.
  75. *
  76. * @tparam T The inner valur type used in buffer
  77. *
  78. * @param send_data [std::vector<T>] Reference to local data to send
  79. * @param recv_data [std::vector<T>] Reference to buffer to receive data from partner
  80. * @param partner [mpi_id_t] The partner for the exchange
  81. * @param tag [int] The tag to use for the MPI communication
  82. */
  83. template<typename T>
  84. void exchange(const std::vector<T>& send_data, std::vector<T>& recv_data, ID_t partner, int tag) {
  85. using namespace std::string_literals;
  86. MPI_Datatype datatype = MPI_TypeMapper<T>::getType();
  87. int send_count = static_cast<int>(send_data.size());
  88. MPI_Status status;
  89. int err;
  90. if ((err = MPI_Sendrecv(
  91. send_data.data(), send_count, datatype, partner, tag,
  92. recv_data.data(), send_count, datatype, partner, tag,
  93. MPI_COMM_WORLD, &status
  94. )) != MPI_SUCCESS)
  95. mpi_throw(err, "(MPI) MPI_Sendrecv() - ");
  96. }
  97. // Accessors
  98. [[nodiscard]] ID_t rank() const noexcept { return rank_; }
  99. [[nodiscard]] ID_t size() const noexcept { return size_; }
  100. [[nodiscard]] const std::string& name() const noexcept { return name_; }
  101. // Mutators
  102. ID_t rank(ID_t rank) noexcept { return rank_ = rank; }
  103. ID_t size(ID_t size) noexcept { return size_ = size; }
  104. std::string& name(const std::string& name) noexcept { return name_ = name; }
  105. /*!
  106. * Finalized the MPI
  107. */
  108. void finalize() {
  109. // Finalize the MPI environment
  110. initialized_ = false;
  111. MPI_Finalize();
  112. }
  113. //! RAII MPI finalization
  114. ~MPI_t() {
  115. // Finalize the MPI environment even on unexpected errors
  116. if (initialized_)
  117. MPI_Finalize();
  118. }
  119. // Local functionality
  120. private:
  121. /*!
  122. * Throw exception helper. It bundles the prefix msg with the MPI error string retrieved by
  123. * MPI API.
  124. *
  125. * @param err The MPI error code
  126. * @param prefixMsg The prefix text for the exception error message
  127. */
  128. void mpi_throw(int err, const char* prefixMsg) {
  129. char err_msg[MPI_MAX_ERROR_STRING];
  130. int msg_len;
  131. MPI_Error_string(err, err_msg, &msg_len);
  132. throw std::runtime_error(prefixMsg + std::string (err_msg) + '\n');
  133. }
  134. private:
  135. ID_t rank_{}; //!< MPI rank of the process
  136. ID_t size_{}; //!< MPI total size of the execution
  137. std::string name_{}; //!< The name of the local machine
  138. bool initialized_{}; //!< RAII helper flag
  139. };
  140. /*
  141. * Exported data types
  142. */
  143. extern MPI_t<> mpi;
  144. using mpi_id_t = MPI_t<>::ID_t;
  145. /*!
  146. * A std::vector wrapper with 2 vectors, an active and a shadow. This type exposes the standard vector
  147. * functionality of the active vector. The shadow can be used when we need to use the vector as mutable
  148. * data in algorithms that can not support "in-place" editing (like elbow-sort for example)
  149. *
  150. * @tparam Value_t the inner data type of the vectors
  151. */
  152. template <typename Value_t>
  153. struct ShadowedVec_t {
  154. // STL requirements
  155. using value_type = Value_t;
  156. using iterator = typename std::vector<Value_t>::iterator;
  157. using const_iterator = typename std::vector<Value_t>::const_iterator;
  158. using size_type = typename std::vector<Value_t>::size_type;
  159. // Default constructor
  160. ShadowedVec_t() = default;
  161. // Constructor from an std::vector
  162. explicit ShadowedVec_t(const std::vector<Value_t>& vec)
  163. : North(vec), South(), active(north) {
  164. South.resize(North.size());
  165. }
  166. explicit ShadowedVec_t(std::vector<Value_t>&& vec)
  167. : North(std::move(vec)), South(), active(north) {
  168. South.resize(North.size());
  169. }
  170. // Copy assignment operator
  171. ShadowedVec_t& operator=(const ShadowedVec_t& other) {
  172. if (this != &other) { // Avoid self-assignment
  173. North = other.North;
  174. South = other.South;
  175. active = other.active;
  176. }
  177. return *this;
  178. }
  179. // Move assignment operator
  180. ShadowedVec_t& operator=(ShadowedVec_t&& other) noexcept {
  181. if (this != &other) { // Avoid self-assignment
  182. North = std::move(other.North);
  183. South = std::move(other.South);
  184. active = other.active;
  185. // There is no need to zero out other since it is valid but in a non-defined state
  186. }
  187. return *this;
  188. }
  189. // Type accessors
  190. std::vector<Value_t>& getActive() { return (active == north) ? North : South; }
  191. std::vector<Value_t>& getShadow() { return (active == north) ? South : North; }
  192. const std::vector<Value_t>& getActive() const { return (active == north) ? North : South; }
  193. const std::vector<Value_t>& getShadow() const { return (active == north) ? South : North; }
  194. // Swap vectors
  195. void switch_active() { active = (active == north) ? south : north; }
  196. // Dispatch vector functionality to active vector
  197. Value_t& operator[](size_type index) { return getActive()[index]; }
  198. const Value_t& operator[](size_type index) const { return getActive()[index]; }
  199. Value_t& at(size_type index) { return getActive().at(index); }
  200. const Value_t& at(size_type index) const { return getActive().at(index); }
  201. void push_back(const Value_t& value) { getActive().push_back(value); }
  202. void push_back(Value_t&& value) { getActive().push_back(std::move(value)); }
  203. void pop_back() { getActive().pop_back(); }
  204. Value_t& front() { return getActive().front(); }
  205. Value_t& back() { return getActive().back(); }
  206. const Value_t& front() const { return getActive().front(); }
  207. const Value_t& back() const { return getActive().back(); }
  208. iterator begin() { return getActive().begin(); }
  209. const_iterator begin() const { return getActive().begin(); }
  210. iterator end() { return getActive().end(); }
  211. const_iterator end() const { return getActive().end(); }
  212. size_type size() const { return getActive().size(); }
  213. void resize(size_t new_size) {
  214. North.resize(new_size);
  215. South.resize(new_size);
  216. }
  217. void reserve(size_t new_capacity) {
  218. North.reserve(new_capacity);
  219. South.reserve(new_capacity);
  220. }
  221. [[nodiscard]] size_t capacity() const { return getActive().capacity(); }
  222. [[nodiscard]] bool empty() const { return getActive().empty(); }
  223. void clear() { getActive().clear(); }
  224. void swap(std::vector<Value_t>& other) { getActive().swap(other); }
  225. // Comparisons
  226. bool operator== (const ShadowedVec_t& other) { return getActive() == other.getActive(); }
  227. bool operator!= (const ShadowedVec_t& other) { return getActive() != other.getActive(); }
  228. bool operator== (const std::vector<value_type>& other) { return getActive() == other; }
  229. bool operator!= (const std::vector<value_type>& other) { return getActive() != other; }
  230. private:
  231. std::vector<Value_t> North{}; //!< Actual buffer to be used either as active or shadow
  232. std::vector<Value_t> South{}; //!< Actual buffer to be used either as active or shadow
  233. enum {
  234. north, south
  235. } active{north}; //!< Flag to select between North and South buffer
  236. };
  237. using distBuffer_t = ShadowedVec_t<distValue_t>;
  238. extern distBuffer_t Data;
  239. /*!
  240. * A Logger for entire program.
  241. */
  242. struct Log {
  243. struct Endl {} endl; //!< a tag object to to use it as a new line request.
  244. //! We provide logging via << operator
  245. template<typename T>
  246. Log &operator<<(T &&t) {
  247. if (config.verbose) {
  248. if (line_) {
  249. std::cout << "[Log]: " << t;
  250. line_ = false;
  251. } else
  252. std::cout << t;
  253. }
  254. return *this;
  255. }
  256. // overload for special end line handling
  257. Log &operator<<(Endl e) {
  258. (void) e;
  259. if (config.verbose) {
  260. std::cout << '\n';
  261. line_ = true;
  262. }
  263. return *this;
  264. }
  265. private:
  266. bool line_{true};
  267. };
  268. extern Log logger;
  269. /*!
  270. * A small timing utility based on chrono.
  271. */
  272. struct Timing {
  273. using Tpoint = std::chrono::steady_clock::time_point;
  274. using Tduration = std::chrono::microseconds;
  275. using microseconds = std::chrono::microseconds;
  276. using milliseconds = std::chrono::milliseconds;
  277. using seconds = std::chrono::seconds;
  278. //! tool to mark the starting point
  279. Tpoint start() noexcept { return mark_ = std::chrono::steady_clock::now(); }
  280. //! tool to mark the ending point
  281. Tpoint stop() noexcept {
  282. Tpoint now = std::chrono::steady_clock::now();
  283. duration_ += dt(now, mark_);
  284. return now;
  285. }
  286. Tduration dt(Tpoint t2, Tpoint t1) noexcept {
  287. return std::chrono::duration_cast<Tduration>(t2 - t1);
  288. }
  289. //! tool to print the time interval
  290. void print_duration(const char *what, mpi_id_t rank) noexcept {
  291. if (std::chrono::duration_cast<microseconds>(duration_).count() < 10000)
  292. std::cout << "[Timing] (Rank " << rank << ") " << what << ": "
  293. << std::to_string(std::chrono::duration_cast<microseconds>(duration_).count()) << " [usec]\n";
  294. else if (std::chrono::duration_cast<milliseconds>(duration_).count() < 10000)
  295. std::cout << "[Timing] (Rank " << rank << ") " << what << ": "
  296. << std::to_string(std::chrono::duration_cast<milliseconds>(duration_).count()) << " [msec]\n";
  297. else
  298. std::cout << "[Timing] (Rank " << rank << ") " << what << ": "
  299. << std::to_string(std::chrono::duration_cast<seconds>(duration_).count()) << " [sec]\n";
  300. }
  301. private:
  302. Tpoint mark_{};
  303. Tduration duration_{};
  304. };
  305. /*!
  306. * Utility high level function to forward a function call to std::invoke and measure
  307. * the excecution time
  308. *
  309. * @tparam Func The function type
  310. * @tparam Args The argument
  311. * @param func
  312. * @param args
  313. * @return
  314. */
  315. #define timeCall(Tim, Func, ...) \
  316. Tim.start(); \
  317. Func(__VA_ARGS__); \
  318. Tim.stop(); \
  319. //template <typename Ret, typename Func, typename... Args>
  320. //auto timeCall_r(Ret& ret, Func&& func, Args&&... args) {
  321. // Timing timer;
  322. //
  323. // timer.start();
  324. // ret = std::invoke(std::forward<Func>(func), std::forward<Args>(args)...);
  325. // timer.stop();
  326. //
  327. // return timer.dt();
  328. //}
  329. #endif /* UTILS_HPP_ */