AUTH's THMMY "Parallel and distributed systems" course assignments.
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 
 
 
 

307 рядки
10 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 "matrix.hpp"
  17. #include "config.h"
  18. template <typename T> struct MPI_TypeMapper;
  19. // Specializations for supported types
  20. template <> struct MPI_TypeMapper<char> { static MPI_Datatype getType() { return MPI_CHAR; } };
  21. template <> struct MPI_TypeMapper<unsigned char> { static MPI_Datatype getType() { return MPI_UNSIGNED_CHAR; } };
  22. template <> struct MPI_TypeMapper<short> { static MPI_Datatype getType() { return MPI_SHORT; } };
  23. template <> struct MPI_TypeMapper<int> { static MPI_Datatype getType() { return MPI_INT; } };
  24. template <> struct MPI_TypeMapper<long> { static MPI_Datatype getType() { return MPI_LONG; } };
  25. template <> struct MPI_TypeMapper<long long> { static MPI_Datatype getType() { return MPI_LONG_LONG; } };
  26. template <> struct MPI_TypeMapper<unsigned short>{ static MPI_Datatype getType() { return MPI_UNSIGNED_SHORT; } };
  27. template <> struct MPI_TypeMapper<unsigned int> { static MPI_Datatype getType() { return MPI_UNSIGNED; } };
  28. template <> struct MPI_TypeMapper<unsigned long> { static MPI_Datatype getType() { return MPI_UNSIGNED_LONG; } };
  29. template <> struct MPI_TypeMapper<unsigned long long> { static MPI_Datatype getType() { return MPI_UNSIGNED_LONG_LONG; } };
  30. template <> struct MPI_TypeMapper<float> { static MPI_Datatype getType() { return MPI_FLOAT; } };
  31. template <> struct MPI_TypeMapper<double> { static MPI_Datatype getType() { return MPI_DOUBLE; } };
  32. template<typename TID = int>
  33. struct MPI_t {
  34. using ID_t = TID; // Export TID type (currently int defined by the standard)
  35. void init(int *argc, char ***argv) {
  36. // Initialize the MPI environment
  37. MPI_Init(argc, argv);
  38. initialized_ = true;
  39. // Get the number of processes
  40. int size_value, rank_value;
  41. MPI_Comm_size(MPI_COMM_WORLD, &size_value);
  42. MPI_Comm_rank(MPI_COMM_WORLD, &rank_value);
  43. size_ = static_cast<ID_t>(size_value);
  44. rank_ = static_cast<ID_t>(rank_value);
  45. // Get the name of the processor
  46. char processor_name[MPI_MAX_PROCESSOR_NAME];
  47. int name_len;
  48. MPI_Get_processor_name(processor_name, &name_len);
  49. name_ = std::string (processor_name, name_len);
  50. }
  51. template<typename T>
  52. void exchange(ID_t partner, const std::vector<T>& send_data, std::vector<T>& recv_data, int tag) {
  53. using namespace std::string_literals;
  54. MPI_Status status;
  55. MPI_Datatype datatype = MPI_TypeMapper<T>::getType();
  56. int send_count = static_cast<int>(send_data.size());
  57. int err = MPI_Sendrecv(
  58. send_data.data(), send_count, datatype, partner, tag,
  59. recv_data.data(), send_count, datatype, partner, tag,
  60. MPI_COMM_WORLD, &status
  61. );
  62. if (err != MPI_SUCCESS) {
  63. char err_msg[MPI_MAX_ERROR_STRING];
  64. int msg_len;
  65. MPI_Error_string(err, err_msg, &msg_len);
  66. throw std::runtime_error("(MPI) MPI_Sendrecv() - " + std::string (err_msg) + '\n');
  67. }
  68. }
  69. // Accessors
  70. [[nodiscard]] ID_t rank() const noexcept { return rank_; }
  71. [[nodiscard]] ID_t size() const noexcept { return size_; }
  72. [[nodiscard]] const std::string& name() const noexcept { return name_; }
  73. void finalize() {
  74. // Finalize the MPI environment
  75. initialized_ = false;
  76. MPI_Finalize();
  77. }
  78. ~MPI_t() {
  79. // Finalize the MPI environment even on unexpected errors
  80. if (initialized_)
  81. MPI_Finalize();
  82. }
  83. private:
  84. ID_t rank_{};
  85. ID_t size_{};
  86. std::string name_{};
  87. bool initialized_{};
  88. };
  89. extern MPI_t<> mpi;
  90. using mpi_id_t = MPI_t<>::ID_t;
  91. template <typename Value_t>
  92. struct ShadowedVec_t {
  93. // STL requirements
  94. using value_type = Value_t;
  95. using iterator = typename std::vector<Value_t>::iterator;
  96. using const_iterator = typename std::vector<Value_t>::const_iterator;
  97. using size_type = typename std::vector<Value_t>::size_type;
  98. // Default constructor
  99. ShadowedVec_t() = default;
  100. // Constructor from an std::vector
  101. explicit ShadowedVec_t(const std::vector<Value_t>& vec)
  102. : North(vec), South(), active(north) {
  103. South.resize(North.size());
  104. }
  105. explicit ShadowedVec_t(std::vector<Value_t>&& vec)
  106. : North(std::move(vec)), South(), active(north) {
  107. South.resize(North.size());
  108. }
  109. // Copy assignment operator
  110. ShadowedVec_t& operator=(const ShadowedVec_t& other) {
  111. if (this != &other) { // Avoid self-assignment
  112. North = other.North;
  113. South = other.South;
  114. active = other.active;
  115. }
  116. return *this;
  117. }
  118. // Move assignment operator
  119. ShadowedVec_t& operator=(ShadowedVec_t&& other) noexcept {
  120. if (this != &other) { // Avoid self-assignment
  121. North = std::move(other.North);
  122. South = std::move(other.South);
  123. active = other.active;
  124. // There is no need to zero out other since it is valid but in a non-defined state
  125. }
  126. return *this;
  127. }
  128. // Dispatch to active vector
  129. Value_t& operator[](size_type index) { return getActive()[index]; }
  130. const Value_t& operator[](size_type index) const { return getActive()[index]; }
  131. Value_t& at(size_type index) { return getActive().at(index); }
  132. const Value_t& at(size_type index) const { return getActive().at(index); }
  133. void push_back(const Value_t& value) { getActive().push_back(value); }
  134. void push_back(Value_t&& value) { getActive().push_back(std::move(value)); }
  135. void pop_back() { getActive().pop_back(); }
  136. Value_t& front() { return getActive().front(); }
  137. const Value_t& front() const { return getActive().front(); }
  138. Value_t& back() { return getActive().back(); }
  139. const Value_t& back() const { return getActive().back(); }
  140. iterator begin() { return getActive().begin(); }
  141. const_iterator begin() const { return getActive().begin(); }
  142. iterator end() { return getActive().end(); }
  143. const_iterator end() const { return getActive().end(); }
  144. size_type size() const { return getActive().size(); }
  145. void resize(size_t new_size) {
  146. North.resize(new_size);
  147. South.resize(new_size);
  148. }
  149. void reserve(size_t new_capacity) {
  150. North.reserve(new_capacity);
  151. South.reserve(new_capacity);
  152. }
  153. [[nodiscard]] size_t capacity() const { return getActive().capacity(); }
  154. [[nodiscard]] bool empty() const { return getActive().empty(); }
  155. void clear() { getActive().clear(); }
  156. void swap(std::vector<Value_t>& other) { getActive().swap(other); }
  157. // Switching vectors
  158. void switch_active() { active = (active == north) ? south : north; }
  159. // Accessors
  160. const std::vector<Value_t>& getNorth() const { return North; }
  161. const std::vector<Value_t>& getSouth() const { return South; }
  162. std::vector<Value_t>& getActive() {
  163. return (active == north) ? North : South;
  164. }
  165. const std::vector<Value_t>& getActive() const {
  166. return (active == north) ? North : South;
  167. }
  168. std::vector<Value_t>& getShadow() {
  169. return (active == north) ? South : North;
  170. }
  171. const std::vector<Value_t>& getShadow() const {
  172. return (active == north) ? South : North;
  173. }
  174. // Comparisons
  175. bool operator== (const ShadowedVec_t& other) {
  176. return getActive() == other.getActive();
  177. }
  178. bool operator!= (const ShadowedVec_t& other) {
  179. return getActive() != other.getActive();
  180. }
  181. bool operator== (const std::vector<value_type>& other) {
  182. return getActive() == other;
  183. }
  184. bool operator!= (const std::vector<value_type>& other) {
  185. return getActive() != other;
  186. }
  187. private:
  188. std::vector<Value_t> North{};
  189. std::vector<Value_t> South{};
  190. enum { north, south } active{north};
  191. };
  192. using distBuffer_t = ShadowedVec_t<distValue_t>;
  193. extern distBuffer_t Data;
  194. /*!
  195. * A Logger for entire program.
  196. */
  197. struct Log {
  198. struct Endl {} endl; //!< a tag object to to use it as a new line request.
  199. //! We provide logging via << operator
  200. template<typename T>
  201. Log &operator<<(T &&t) {
  202. if (session.verbose) {
  203. if (line_) {
  204. std::cout << "[Log]: " << t;
  205. line_ = false;
  206. } else
  207. std::cout << t;
  208. }
  209. return *this;
  210. }
  211. // overload for special end line handling
  212. Log &operator<<(Endl e) {
  213. (void) e;
  214. if (session.verbose) {
  215. std::cout << '\n';
  216. line_ = true;
  217. }
  218. return *this;
  219. }
  220. private:
  221. bool line_{true};
  222. };
  223. extern Log logger;
  224. /*!
  225. * A small timing utility based on chrono.
  226. */
  227. struct Timing {
  228. using Tpoint = std::chrono::steady_clock::time_point;
  229. using microseconds = std::chrono::microseconds;
  230. using milliseconds = std::chrono::milliseconds;
  231. using seconds = std::chrono::seconds;
  232. //! tool to mark the starting point
  233. Tpoint start() noexcept { return start_ = std::chrono::steady_clock::now(); }
  234. //! tool to mark the ending point
  235. Tpoint stop() noexcept { return stop_ = std::chrono::steady_clock::now(); }
  236. auto dt() noexcept {
  237. return std::chrono::duration_cast<std::chrono::microseconds>(stop_ - start_).count();
  238. }
  239. //! tool to print the time interval
  240. void print_dt(const char *what) noexcept {
  241. if (session.timing) {
  242. auto t = stop_ - start_;
  243. if (std::chrono::duration_cast<microseconds>(t).count() < 10000)
  244. std::cout << "[Timing]: " << what << ": "
  245. << std::to_string(std::chrono::duration_cast<microseconds>(t).count()) << " [usec]\n";
  246. else if (std::chrono::duration_cast<milliseconds>(t).count() < 10000)
  247. std::cout << "[Timing]: " << what << ": "
  248. << std::to_string(std::chrono::duration_cast<milliseconds>(t).count()) << " [msec]\n";
  249. else
  250. std::cout << "[Timing]: " << what << ": "
  251. << std::to_string(std::chrono::duration_cast<seconds>(t).count()) << " [sec]\n";
  252. }
  253. }
  254. private:
  255. Tpoint start_;
  256. Tpoint stop_;
  257. };
  258. #endif /* UTILS_HPP_ */