|
- /*!
- * \file
- * \brief Distributed sort implementation header
- *
- * \author
- * Christos Choutouridis AEM:8997
- * <cchoutou@ece.auth.gr>
- */
-
- #ifndef DISTBITONIC_H_
- #define DISTBITONIC_H_
-
- #include <vector>
- #include <algorithm>
- #include <cmath>
- #include <cstdint>
- #if !defined DEBUG
- #define NDEBUG
- #endif
- #include <cassert>
-
- #include "utils.hpp"
- #include "config.h"
-
-
- /*!
- * Enumerator for the different versions of the sorting method
- */
- enum class SortMode {
- Bubbletonic, //!< The v0.5 of the algorithm where we use a bubble-sort like approach
- Bitonic //!< The v1.0 of the algorithm where we use the bitonic data-exchange approach
- };
-
- /*
- * ============================== Sort utilities ==============================
- */
-
- /*!
- * The primary function template of ascending(). It is DISABLED since , it is explicitly specialized
- * for each of the \c SortMode
- */
- template <SortMode Mode> inline bool ascending(mpi_id_t, [[maybe_unused]] size_t) noexcept = delete;
-
- /*!
- * Returns the ascending or descending configuration of the node's sequence based on
- * the current node (MPI process) and the depth of the sorting network
- *
- * @param node The current node (MPI process)
- * @return True if we need ascending configuration, false otherwise
- */
- template <> inline
- bool ascending<SortMode::Bubbletonic>(mpi_id_t node, [[maybe_unused]] size_t depth) noexcept {
- return (node % 2) == 0;
- }
-
- /*!
- * Returns the ascending or descending configuration of the node's sequence based on
- * the current node (MPI process) and the depth of the sorting network
- *
- * @param node The current node (MPI process)
- * @param depth The total depth of the sorting network (same for each step for a given network)
- *
- * @return True if we need ascending configuration, false otherwise
- */
- template <> inline
- bool ascending<SortMode::Bitonic>(mpi_id_t node, size_t depth) noexcept {
- return !(node & (1 << depth));
- }
-
- /*!
- * The primary function template of partner(). It is DISABLED since , it is explicitly specialized
- * for each of the \c SortMode
- */
- template <SortMode Mode> inline mpi_id_t partner(mpi_id_t, size_t) noexcept = delete;
-
- /*!
- * Returns the node's partner for data exchange during the sorting network iterations
- * of Bubbletonic
- *
- * @param node The current node
- * @param step The step of the sorting network
- * @return The node id of the partner for data exchange
- */
- template <> inline
- mpi_id_t partner<SortMode::Bubbletonic>(mpi_id_t node, size_t step) noexcept {
- //return (node % 2 == step % 2) ? node + 1 : node - 1;
- return (((node+step) % 2) == 0) ? node + 1 : node - 1;
- }
-
- /*!
- * Returns the node's partner for data exchange during the sorting network iterations
- * of Bitonic
- *
- * @param node The current node
- * @param step The step of the sorting network
- * @return The node id of the partner for data exchange
- */
- template <> inline
- mpi_id_t partner<SortMode::Bitonic>(mpi_id_t node, size_t step) noexcept {
- return (node ^ (1 << step));
- }
-
-
- /*!
- * The primary function template of keepSmall(). It is DISABLED since , it is explicitly specialized
- * for each of the \c SortMode
- */
- template<SortMode Mode> inline bool keepSmall(mpi_id_t, mpi_id_t, [[maybe_unused]] size_t) noexcept = delete;
-
- /*!
- * Predicate to check if a node keeps the small numbers during the bubbletonic sort network exchange.
- *
- * @param node The node for which we check
- * @param partner The partner of the data exchange
- * @return True if the node should keep the small values, false otherwise
- */
- template <> inline
- bool keepSmall<SortMode::Bubbletonic>(mpi_id_t node, mpi_id_t partner, [[maybe_unused]] size_t depth) noexcept {
- assert(node != partner);
- return (node < partner);
- }
-
- /*!
- * Predicate to check if a node keeps the small numbers during the bitonic sort network exchange.
- *
- * @param node The node for which we check
- * @param partner The partner of the data exchange
- * @param depth The total depth of the sorting network (same for each step for a given network)
- * @return True if the node should keep the small values, false otherwise
- */
- template <> inline
- bool keepSmall<SortMode::Bitonic>(mpi_id_t node, mpi_id_t partner, size_t depth) noexcept {
- assert(node != partner);
- return ascending<SortMode::Bitonic>(node, depth) == (node < partner);
- }
-
- /*!
- * Predicate to check if the node is active in the current iteration of the bubbletonic
- * sort exchange.
- *
- * @param node The node to check
- * @param nodes The total number of nodes
- * @return True if the node is active, false otherwise
- */
- bool isActive(mpi_id_t node, size_t nodes) noexcept;
-
- /*
- * ============================== Data utilities ==============================
- */
-
- /*!
- *
- * @tparam RangeT
- * @param data
- * @param ascending
- */
- template<typename RangeT>
- void fullSort(RangeT& data, bool ascending) {
- // Use introsort from stdlib++ here, unless ...
- if (ascending)
- std::sort(data.begin(), data.end(), std::less<>());
- else
- std::sort(data.begin(), data.end(), std::greater<>());
- }
-
- /*!
- *
- * @tparam ShadowedT
- * @tparam CompT
- * @param data
- * @param comp
- */
- template<typename ShadowedT, typename CompT>
- void elbowSortCore(ShadowedT& data, CompT comp) {
- size_t N = data.size();
- auto active = data.getActive();
- auto shadow = data.getShadow();
- size_t left = std::distance(
- active.begin(),
- std::min_element(active.begin(), active.end())
- );
- size_t right = (left == N-1) ? 0 : left + 1;
-
- for (size_t i = 0 ; i<N ; ++i) {
- if (comp(active[left], active[right])) {
- shadow[i] = active[left];
- left = (left == 0) ? N-1 : left -1;
- }
- else {
- shadow[i] = active[right];
- right = (right + 1) % N;
- }
- }
- data.switch_active();
- }
-
- /*!
- *
- * @tparam ShadowedT
- * @param data
- * @param ascending
- */
- template<typename ShadowedT>
- void elbowSort(ShadowedT& data, bool ascending) {
- if (ascending)
- elbowSortCore(data, std::less<>());
- else
- elbowSortCore(data, std::greater<>());
- }
-
- /*!
- *
- * @tparam RangeT
- * @param local
- * @param remote
- * @param keepsmall
- */
- template<typename RangeT>
- void minmax(RangeT& local, RangeT& remote, bool keepsmall) {
- using value_t = typename RangeT::value_type;
- std::transform(
- local.begin(), local.end(),
- remote.begin(),
- local.begin(),
- [keepsmall](const value_t& a, const value_t& b){
- return (keepsmall) ? std::min(a, b) : std::max(a, b);
- });
- }
-
- /*
- * ============================== Sort algorithms ==============================
- */
-
- /*!
- *
- * @tparam ShadowedT
- * @param data
- * @param Processes
- */
- template<typename ShadowedT>
- void distBubbletonic(ShadowedT& data, mpi_id_t Processes) {
- // Initially sort to create a half part of a bitonic sequence
- fullSort(data, ascending<SortMode::Bubbletonic>(mpi.rank(), 0));
-
- // Sort network
- for (size_t step = 0; step < Processes-1; ++step) {
- auto part = partner<SortMode::Bubbletonic>(mpi.rank(), step);
- auto ks = keepSmall<SortMode::Bubbletonic>(mpi.rank(), part, Processes);
- if (isActive(mpi.rank(), Processes)) {
- mpi.exchange(part, data.getActive(), data.getShadow(), step);
- minmax(data.getActive(), data.getShadow(), ks);
- elbowSort(data, ascending<SortMode::Bubbletonic>(mpi.rank(), Processes));
- }
- }
-
- if (!ascending<SortMode::Bubbletonic>(mpi.rank(), 0))
- elbowSort(data, true);
-
- }
-
-
- /*!
- *
- * @tparam ShadowedT
- * @param data
- * @param Processes
- */
- template<typename ShadowedT>
- void distBitonic(ShadowedT& data, mpi_id_t Processes) {
- auto p = static_cast<uint32_t>(std::log2(Processes));
-
- // Initially sort to create a half part of a bitonic sequence
- fullSort(data, ascending<SortMode::Bitonic>(mpi.rank(), 0));
-
- // Run through sort network using elbow-sort
- for (size_t depth = 1; depth <= p; ++depth) {
- for (size_t step = depth; step > 0;) {
- --step;
- auto part = partner<SortMode::Bitonic>(mpi.rank(), step);
- auto ks = keepSmall<SortMode::Bitonic>(mpi.rank(), part, depth);
- mpi.exchange(part, data.getActive(), data.getShadow(), (depth << 8) | step);
- minmax(data.getActive(), data.getShadow(), ks);
- }
- elbowSort (data, ascending<SortMode::Bitonic>(mpi.rank(), depth));
- }
- }
-
- #endif //DISTBITONIC_H_
|