AUTH's THMMY "Parallel and distributed systems" course assignments.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

289 lines
8.4 KiB

  1. /*!
  2. * \file
  3. * \brief Distributed sort implementation header
  4. *
  5. * \author
  6. * Christos Choutouridis AEM:8997
  7. * <cchoutou@ece.auth.gr>
  8. */
  9. #ifndef DISTBITONIC_H_
  10. #define DISTBITONIC_H_
  11. #include <vector>
  12. #include <algorithm>
  13. #include <cmath>
  14. #include <cstdint>
  15. #if !defined DEBUG
  16. #define NDEBUG
  17. #endif
  18. #include <cassert>
  19. #include "utils.hpp"
  20. #include "config.h"
  21. /*!
  22. * Enumerator for the different versions of the sorting method
  23. */
  24. enum class SortMode {
  25. Bubbletonic, //!< The v0.5 of the algorithm where we use a bubble-sort like approach
  26. Bitonic //!< The v1.0 of the algorithm where we use the bitonic data-exchange approach
  27. };
  28. /*
  29. * ============================== Sort utilities ==============================
  30. */
  31. /*!
  32. * The primary function template of ascending(). It is DISABLED since , it is explicitly specialized
  33. * for each of the \c SortMode
  34. */
  35. template <SortMode Mode> inline bool ascending(mpi_id_t, [[maybe_unused]] size_t) noexcept = delete;
  36. /*!
  37. * Returns the ascending or descending configuration of the node's sequence based on
  38. * the current node (MPI process) and the depth of the sorting network
  39. *
  40. * @param node The current node (MPI process)
  41. * @return True if we need ascending configuration, false otherwise
  42. */
  43. template <> inline
  44. bool ascending<SortMode::Bubbletonic>(mpi_id_t node, [[maybe_unused]] size_t depth) noexcept {
  45. return (node % 2) == 0;
  46. }
  47. /*!
  48. * Returns the ascending or descending configuration of the node's sequence based on
  49. * the current node (MPI process) and the depth of the sorting network
  50. *
  51. * @param node The current node (MPI process)
  52. * @param depth The total depth of the sorting network (same for each step for a given network)
  53. *
  54. * @return True if we need ascending configuration, false otherwise
  55. */
  56. template <> inline
  57. bool ascending<SortMode::Bitonic>(mpi_id_t node, size_t depth) noexcept {
  58. return !(node & (1 << depth));
  59. }
  60. /*!
  61. * The primary function template of partner(). It is DISABLED since , it is explicitly specialized
  62. * for each of the \c SortMode
  63. */
  64. template <SortMode Mode> inline mpi_id_t partner(mpi_id_t, size_t) noexcept = delete;
  65. /*!
  66. * Returns the node's partner for data exchange during the sorting network iterations
  67. * of Bubbletonic
  68. *
  69. * @param node The current node
  70. * @param step The step of the sorting network
  71. * @return The node id of the partner for data exchange
  72. */
  73. template <> inline
  74. mpi_id_t partner<SortMode::Bubbletonic>(mpi_id_t node, size_t step) noexcept {
  75. //return (node % 2 == step % 2) ? node + 1 : node - 1;
  76. return (((node+step) % 2) == 0) ? node + 1 : node - 1;
  77. }
  78. /*!
  79. * Returns the node's partner for data exchange during the sorting network iterations
  80. * of Bitonic
  81. *
  82. * @param node The current node
  83. * @param step The step of the sorting network
  84. * @return The node id of the partner for data exchange
  85. */
  86. template <> inline
  87. mpi_id_t partner<SortMode::Bitonic>(mpi_id_t node, size_t step) noexcept {
  88. return (node ^ (1 << step));
  89. }
  90. /*!
  91. * The primary function template of keepSmall(). It is DISABLED since , it is explicitly specialized
  92. * for each of the \c SortMode
  93. */
  94. template<SortMode Mode> inline bool keepSmall(mpi_id_t, mpi_id_t, [[maybe_unused]] size_t) noexcept = delete;
  95. /*!
  96. * Predicate to check if a node keeps the small numbers during the bubbletonic sort network exchange.
  97. *
  98. * @param node The node for which we check
  99. * @param partner The partner of the data exchange
  100. * @return True if the node should keep the small values, false otherwise
  101. */
  102. template <> inline
  103. bool keepSmall<SortMode::Bubbletonic>(mpi_id_t node, mpi_id_t partner, [[maybe_unused]] size_t depth) noexcept {
  104. assert(node != partner);
  105. return (node < partner);
  106. }
  107. /*!
  108. * Predicate to check if a node keeps the small numbers during the bitonic sort network exchange.
  109. *
  110. * @param node The node for which we check
  111. * @param partner The partner of the data exchange
  112. * @param depth The total depth of the sorting network (same for each step for a given network)
  113. * @return True if the node should keep the small values, false otherwise
  114. */
  115. template <> inline
  116. bool keepSmall<SortMode::Bitonic>(mpi_id_t node, mpi_id_t partner, size_t depth) noexcept {
  117. assert(node != partner);
  118. return ascending<SortMode::Bitonic>(node, depth) == (node < partner);
  119. }
  120. /*!
  121. * Predicate to check if the node is active in the current iteration of the bubbletonic
  122. * sort exchange.
  123. *
  124. * @param node The node to check
  125. * @param nodes The total number of nodes
  126. * @return True if the node is active, false otherwise
  127. */
  128. bool isActive(mpi_id_t node, size_t nodes) noexcept;
  129. /*
  130. * ============================== Data utilities ==============================
  131. */
  132. /*!
  133. *
  134. * @tparam RangeT
  135. * @param data
  136. * @param ascending
  137. */
  138. template<typename RangeT>
  139. void fullSort(RangeT& data, bool ascending) {
  140. // Use introsort from stdlib++ here, unless ...
  141. if (ascending)
  142. std::sort(data.begin(), data.end(), std::less<>());
  143. else
  144. std::sort(data.begin(), data.end(), std::greater<>());
  145. }
  146. /*!
  147. *
  148. * @tparam ShadowedT
  149. * @tparam CompT
  150. * @param data
  151. * @param comp
  152. */
  153. template<typename ShadowedT, typename CompT>
  154. void elbowSortCore(ShadowedT& data, CompT comp) {
  155. size_t N = data.size();
  156. auto active = data.getActive();
  157. auto shadow = data.getShadow();
  158. size_t left = std::distance(
  159. active.begin(),
  160. std::min_element(active.begin(), active.end())
  161. );
  162. size_t right = (left == N-1) ? 0 : left + 1;
  163. for (size_t i = 0 ; i<N ; ++i) {
  164. if (comp(active[left], active[right])) {
  165. shadow[i] = active[left];
  166. left = (left == 0) ? N-1 : left -1;
  167. }
  168. else {
  169. shadow[i] = active[right];
  170. right = (right + 1) % N;
  171. }
  172. }
  173. data.switch_active();
  174. }
  175. /*!
  176. *
  177. * @tparam ShadowedT
  178. * @param data
  179. * @param ascending
  180. */
  181. template<typename ShadowedT>
  182. void elbowSort(ShadowedT& data, bool ascending) {
  183. if (ascending)
  184. elbowSortCore(data, std::less<>());
  185. else
  186. elbowSortCore(data, std::greater<>());
  187. }
  188. /*!
  189. *
  190. * @tparam RangeT
  191. * @param local
  192. * @param remote
  193. * @param keepsmall
  194. */
  195. template<typename RangeT>
  196. void minmax(RangeT& local, RangeT& remote, bool keepsmall) {
  197. using value_t = typename RangeT::value_type;
  198. std::transform(
  199. local.begin(), local.end(),
  200. remote.begin(),
  201. local.begin(),
  202. [keepsmall](const value_t& a, const value_t& b){
  203. return (keepsmall) ? std::min(a, b) : std::max(a, b);
  204. });
  205. }
  206. /*
  207. * ============================== Sort algorithms ==============================
  208. */
  209. /*!
  210. *
  211. * @tparam ShadowedT
  212. * @param data
  213. * @param Processes
  214. */
  215. template<typename ShadowedT>
  216. void distBubbletonic(ShadowedT& data, mpi_id_t Processes) {
  217. // Initially sort to create a half part of a bitonic sequence
  218. fullSort(data, ascending<SortMode::Bubbletonic>(mpi.rank(), 0));
  219. // Sort network
  220. for (size_t step = 0; step < Processes-1; ++step) {
  221. auto part = partner<SortMode::Bubbletonic>(mpi.rank(), step);
  222. auto ks = keepSmall<SortMode::Bubbletonic>(mpi.rank(), part, Processes);
  223. if (isActive(mpi.rank(), Processes)) {
  224. mpi.exchange(part, data.getActive(), data.getShadow(), step);
  225. minmax(data.getActive(), data.getShadow(), ks);
  226. elbowSort(data, ascending<SortMode::Bubbletonic>(mpi.rank(), Processes));
  227. }
  228. }
  229. if (!ascending<SortMode::Bubbletonic>(mpi.rank(), 0))
  230. elbowSort(data, true);
  231. }
  232. /*!
  233. *
  234. * @tparam ShadowedT
  235. * @param data
  236. * @param Processes
  237. */
  238. template<typename ShadowedT>
  239. void distBitonic(ShadowedT& data, mpi_id_t Processes) {
  240. auto p = static_cast<uint32_t>(std::log2(Processes));
  241. // Initially sort to create a half part of a bitonic sequence
  242. fullSort(data, ascending<SortMode::Bitonic>(mpi.rank(), 0));
  243. // Run through sort network using elbow-sort
  244. for (size_t depth = 1; depth <= p; ++depth) {
  245. for (size_t step = depth; step > 0;) {
  246. --step;
  247. auto part = partner<SortMode::Bitonic>(mpi.rank(), step);
  248. auto ks = keepSmall<SortMode::Bitonic>(mpi.rank(), part, depth);
  249. mpi.exchange(part, data.getActive(), data.getShadow(), (depth << 8) | step);
  250. minmax(data.getActive(), data.getShadow(), ks);
  251. }
  252. elbowSort (data, ascending<SortMode::Bitonic>(mpi.rank(), depth));
  253. }
  254. }
  255. #endif //DISTBITONIC_H_