|
- /**
- * \file
- * \brief Utilities header
- *
- * \author
- * Christos Choutouridis AEM:8997
- * <cchoutou@ece.auth.gr>
- */
- #ifndef UTILS_HPP_
- #define UTILS_HPP_
-
- #include <vector>
- #include <iostream>
- #include <chrono>
- #include <unistd.h>
- #include <mpi.h>
-
- //#include "matrix.hpp"
- #include "config.h"
-
- template <typename T> struct MPI_TypeMapper;
-
- // Specializations for supported types
- template <> struct MPI_TypeMapper<char> { static MPI_Datatype getType() { return MPI_CHAR; } };
- template <> struct MPI_TypeMapper<unsigned char> { static MPI_Datatype getType() { return MPI_UNSIGNED_CHAR; } };
-
- template <> struct MPI_TypeMapper<short> { static MPI_Datatype getType() { return MPI_SHORT; } };
- template <> struct MPI_TypeMapper<int> { static MPI_Datatype getType() { return MPI_INT; } };
- template <> struct MPI_TypeMapper<long> { static MPI_Datatype getType() { return MPI_LONG; } };
- template <> struct MPI_TypeMapper<long long> { static MPI_Datatype getType() { return MPI_LONG_LONG; } };
- template <> struct MPI_TypeMapper<unsigned short>{ static MPI_Datatype getType() { return MPI_UNSIGNED_SHORT; } };
- template <> struct MPI_TypeMapper<unsigned int> { static MPI_Datatype getType() { return MPI_UNSIGNED; } };
- template <> struct MPI_TypeMapper<unsigned long> { static MPI_Datatype getType() { return MPI_UNSIGNED_LONG; } };
- template <> struct MPI_TypeMapper<unsigned long long> { static MPI_Datatype getType() { return MPI_UNSIGNED_LONG_LONG; } };
-
- template <> struct MPI_TypeMapper<float> { static MPI_Datatype getType() { return MPI_FLOAT; } };
- template <> struct MPI_TypeMapper<double> { static MPI_Datatype getType() { return MPI_DOUBLE; } };
-
- template<typename TID = int>
- struct MPI_t {
- using ID_t = TID; // Export TID type (currently int defined by the standard)
-
- void init(int *argc, char ***argv) {
- // Initialize the MPI environment
- MPI_Init(argc, argv);
- initialized_ = true;
-
- // Get the number of processes
- int size_value, rank_value;
- MPI_Comm_size(MPI_COMM_WORLD, &size_value);
- MPI_Comm_rank(MPI_COMM_WORLD, &rank_value);
- size_ = static_cast<ID_t>(size_value);
- rank_ = static_cast<ID_t>(rank_value);
-
- // Get the name of the processor
- char processor_name[MPI_MAX_PROCESSOR_NAME];
- int name_len;
- MPI_Get_processor_name(processor_name, &name_len);
- name_ = std::string (processor_name, name_len);
- }
-
- template<typename T>
- void exchange(ID_t partner, const std::vector<T>& send_data, std::vector<T>& recv_data, int tag) {
- using namespace std::string_literals;
-
- MPI_Status status;
- MPI_Datatype datatype = MPI_TypeMapper<T>::getType();
- int send_count = static_cast<int>(send_data.size());
- int err = MPI_Sendrecv(
- send_data.data(), send_count, datatype, partner, tag,
- recv_data.data(), send_count, datatype, partner, tag,
- MPI_COMM_WORLD, &status
- );
- if (err != MPI_SUCCESS) {
- char err_msg[MPI_MAX_ERROR_STRING];
- int msg_len;
- MPI_Error_string(err, err_msg, &msg_len);
- throw std::runtime_error("(MPI) MPI_Sendrecv() - " + std::string (err_msg) + '\n');
- }
- }
-
- // Accessors
- [[nodiscard]] ID_t rank() const noexcept { return rank_; }
- [[nodiscard]] ID_t size() const noexcept { return size_; }
- [[nodiscard]] const std::string& name() const noexcept { return name_; }
-
- void finalize() {
- // Finalize the MPI environment
- initialized_ = false;
- MPI_Finalize();
- }
- ~MPI_t() {
- // Finalize the MPI environment even on unexpected errors
- if (initialized_)
- MPI_Finalize();
- }
- private:
- ID_t rank_{};
- ID_t size_{};
- std::string name_{};
- bool initialized_{};
- };
-
- extern MPI_t<> mpi;
- using mpi_id_t = MPI_t<>::ID_t;
-
- template <typename Value_t>
- struct ShadowedVec_t {
- // STL requirements
- using value_type = Value_t;
- using iterator = typename std::vector<Value_t>::iterator;
- using const_iterator = typename std::vector<Value_t>::const_iterator;
- using size_type = typename std::vector<Value_t>::size_type;
-
- // Default constructor
- ShadowedVec_t() = default;
-
- // Constructor from an std::vector
- explicit ShadowedVec_t(const std::vector<Value_t>& vec)
- : North(vec), South(), active(north) {
- South.resize(North.size());
- }
-
- explicit ShadowedVec_t(std::vector<Value_t>&& vec)
- : North(std::move(vec)), South(), active(north) {
- South.resize(North.size());
- }
-
- // Copy assignment operator
- ShadowedVec_t& operator=(const ShadowedVec_t& other) {
- if (this != &other) { // Avoid self-assignment
- North = other.North;
- South = other.South;
- active = other.active;
- }
- return *this;
- }
-
- // Move assignment operator
- ShadowedVec_t& operator=(ShadowedVec_t&& other) noexcept {
- if (this != &other) { // Avoid self-assignment
- North = std::move(other.North);
- South = std::move(other.South);
- active = other.active;
-
- // There is no need to zero out other since it is valid but in a non-defined state
- }
- return *this;
- }
-
- // Dispatch to active vector
- Value_t& operator[](size_type index) { return getActive()[index]; }
- const Value_t& operator[](size_type index) const { return getActive()[index]; }
-
- Value_t& at(size_type index) { return getActive().at(index); }
- const Value_t& at(size_type index) const { return getActive().at(index); }
-
- void push_back(const Value_t& value) { getActive().push_back(value); }
- void push_back(Value_t&& value) { getActive().push_back(std::move(value)); }
- void pop_back() { getActive().pop_back(); }
- Value_t& front() { return getActive().front(); }
- const Value_t& front() const { return getActive().front(); }
- Value_t& back() { return getActive().back(); }
- const Value_t& back() const { return getActive().back(); }
-
- iterator begin() { return getActive().begin(); }
- const_iterator begin() const { return getActive().begin(); }
- iterator end() { return getActive().end(); }
- const_iterator end() const { return getActive().end(); }
-
- size_type size() const { return getActive().size(); }
- void resize(size_t new_size) {
- North.resize(new_size);
- South.resize(new_size);
- }
-
- void reserve(size_t new_capacity) {
- North.reserve(new_capacity);
- South.reserve(new_capacity);
- }
- [[nodiscard]] size_t capacity() const { return getActive().capacity(); }
- [[nodiscard]] bool empty() const { return getActive().empty(); }
-
- void clear() { getActive().clear(); }
-
- void swap(std::vector<Value_t>& other) { getActive().swap(other); }
-
- // Switching vectors
- void switch_active() { active = (active == north) ? south : north; }
-
- // Accessors
- const std::vector<Value_t>& getNorth() const { return North; }
- const std::vector<Value_t>& getSouth() const { return South; }
- std::vector<Value_t>& getActive() {
- return (active == north) ? North : South;
- }
- const std::vector<Value_t>& getActive() const {
- return (active == north) ? North : South;
- }
- std::vector<Value_t>& getShadow() {
- return (active == north) ? South : North;
- }
- const std::vector<Value_t>& getShadow() const {
- return (active == north) ? South : North;
- }
-
- // Comparisons
- bool operator== (const ShadowedVec_t& other) {
- return getActive() == other.getActive();
- }
- bool operator!= (const ShadowedVec_t& other) {
- return getActive() != other.getActive();
- }
- bool operator== (const std::vector<value_type>& other) {
- return getActive() == other;
- }
- bool operator!= (const std::vector<value_type>& other) {
- return getActive() != other;
- }
-
- private:
- std::vector<Value_t> North{};
- std::vector<Value_t> South{};
- enum { north, south } active{north};
- };
-
- using distBuffer_t = ShadowedVec_t<distValue_t>;
-
- extern distBuffer_t Data;
-
- /*!
- * A Logger for entire program.
- */
- struct Log {
- struct Endl {} endl; //!< a tag object to to use it as a new line request.
-
- //! We provide logging via << operator
- template<typename T>
- Log &operator<<(T &&t) {
- if (session.verbose) {
- if (line_) {
- std::cout << "[Log]: " << t;
- line_ = false;
- } else
- std::cout << t;
- }
- return *this;
- }
-
- // overload for special end line handling
- Log &operator<<(Endl e) {
- (void) e;
- if (session.verbose) {
- std::cout << '\n';
- line_ = true;
- }
- return *this;
- }
-
- private:
- bool line_{true};
- };
-
- extern Log logger;
-
- /*!
- * A small timing utility based on chrono.
- */
- struct Timing {
- using Tpoint = std::chrono::steady_clock::time_point;
- using microseconds = std::chrono::microseconds;
- using milliseconds = std::chrono::milliseconds;
- using seconds = std::chrono::seconds;
-
- //! tool to mark the starting point
- Tpoint start() noexcept { return start_ = std::chrono::steady_clock::now(); }
-
- //! tool to mark the ending point
- Tpoint stop() noexcept { return stop_ = std::chrono::steady_clock::now(); }
-
- auto dt() noexcept {
- return std::chrono::duration_cast<std::chrono::microseconds>(stop_ - start_).count();
- }
-
- //! tool to print the time interval
- void print_dt(const char *what) noexcept {
- if (session.timing) {
- auto t = stop_ - start_;
- if (std::chrono::duration_cast<microseconds>(t).count() < 10000)
- std::cout << "[Timing]: " << what << ": "
- << std::to_string(std::chrono::duration_cast<microseconds>(t).count()) << " [usec]\n";
- else if (std::chrono::duration_cast<milliseconds>(t).count() < 10000)
- std::cout << "[Timing]: " << what << ": "
- << std::to_string(std::chrono::duration_cast<milliseconds>(t).count()) << " [msec]\n";
- else
- std::cout << "[Timing]: " << what << ": "
- << std::to_string(std::chrono::duration_cast<seconds>(t).count()) << " [sec]\n";
- }
- }
-
- private:
- Tpoint start_;
- Tpoint stop_;
- };
-
- #endif /* UTILS_HPP_ */
|