Advertisement
EWTD

ParallelCompLab#2

Nov 26th, 2022
805
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 12.19 KB | None | 0 0
  1. #include <iostream>
  2. #include <memory>
  3. #include <cstring>
  4. #include <vector>
  5. #include <algorithm>
  6. #include <cmath>
  7. #include <chrono>
  8. #include "mpi.h"
  9.  
  10. using namespace std;
  11.  
  12. #define N 4096
  13. #define eps 0.00001
  14. #define lambda (1.f/(10.f*N))
  15.  
  16.  
  17. class Process {
  18.     int32_t _process_id;
  19.  
  20. public:
  21.     Process() noexcept { MPI_Comm_rank(MPI_COMM_WORLD, (int*) &_process_id); }
  22.  
  23.     Process(const int32_t process_id) noexcept { _process_id = process_id; }
  24.  
  25.     virtual void initialize() = 0;
  26.  
  27.     virtual void run() = 0;
  28.  
  29.     virtual void finalize() = 0;
  30.  
  31. protected:
  32.     typedef std::vector<std::vector<double>> Matrix;
  33.     typedef std::vector<double> Vector;
  34.  
  35.     enum Command : unsigned char {
  36.         EVALUATE_METRIC = 0,
  37.         EVALUATE_ITERATION = 1,
  38.         EXIT = 2
  39.     };
  40.  
  41.     inline int32_t process_id() const noexcept { return _process_id; }
  42.  
  43.     template<typename T1>
  44.     static void send_value(T1& a, int receiver) {
  45.         constexpr std::uint32_t a_size = sizeof(T1);
  46.         char buffer[a_size];
  47.         memcpy(buffer, &a, a_size);
  48.         MPI_Send(buffer, a_size, MPI_BYTE, receiver, 0, MPI_COMM_WORLD);
  49.     }
  50.  
  51.     template<typename T1>
  52.     static MPI_Status recv_value(T1& a, int sender) {
  53.         MPI_Status result;
  54.         constexpr std::uint32_t a_size = sizeof(T1);
  55.         char buffer[a_size];
  56.         MPI_Recv(buffer, a_size, MPI_BYTE, sender, 0, MPI_COMM_WORLD, &result);
  57.         memcpy(&a, buffer, a_size);
  58.         return result;
  59.     }
  60.  
  61.     template<typename T1, typename T2>
  62.     static void send_pair(const T1& a, const T2& b, int receiver) {
  63.         constexpr std::uint32_t a_size = sizeof(T1), b_size = sizeof(T2);
  64.         char buffer[a_size + b_size];
  65.         memcpy(buffer, &a, a_size);
  66.         memcpy(buffer + a_size, &b, b_size);
  67.         MPI_Send(buffer, a_size + b_size, MPI_BYTE, receiver, 0, MPI_COMM_WORLD);
  68.     }
  69.  
  70.     template<typename T1, typename T2>
  71.     static MPI_Status recv_pair(T1& a, T2& b, int sender) {
  72.         MPI_Status result;
  73.         constexpr std::uint32_t a_size = sizeof(T1), b_size = sizeof(T2);
  74.         char buffer[a_size + b_size];
  75.         MPI_Recv(buffer, a_size + b_size, MPI_BYTE, sender, 0, MPI_COMM_WORLD, &result);
  76.         memcpy(&a, buffer, a_size);
  77.         memcpy(&b, buffer + a_size, b_size);
  78.         return result;
  79.     }
  80.  
  81.     template<typename ForwardIt>
  82.     static void send_vector(ForwardIt begin, ForwardIt end, int receiver) {
  83.         const uint32_t n = std::distance(begin, end);
  84.         MPI_Send(&n, 1, MPI_UINT32_T, receiver, 0, MPI_COMM_WORLD);
  85.         MPI_Send(begin.base(), n, MPI_DOUBLE, receiver, 0, MPI_COMM_WORLD);
  86.     }
  87.  
  88.     template<typename ForwardIt>
  89.     static MPI_Status recv_vector(ForwardIt begin, ForwardIt end, int sender) {
  90.         MPI_Status result;
  91.         const uint32_t n = std::distance(begin, end);
  92.         MPI_Recv(begin.base(), n, MPI_DOUBLE, sender, 0, MPI_COMM_WORLD, &result);
  93.         return result;
  94.     }
  95.  
  96.     template<typename ForwardIt>
  97.     static void send_matrix(ForwardIt begin, ForwardIt end, int receiver) {
  98.         const uint32_t n = std::distance(begin, end), m = begin->size();
  99.         send_pair(n, m, receiver);
  100.         for (ForwardIt iter = begin; iter != end; iter++)
  101.             MPI_Send(iter->data(), m, MPI_DOUBLE, receiver, 0, MPI_COMM_WORLD);
  102.     }
  103.  
  104.     template<typename ForwardIt>
  105.     static MPI_Status recv_matrix(ForwardIt begin, ForwardIt end, int sender) {
  106.         MPI_Status result;
  107.         const uint32_t n = std::distance(begin, end), m = begin->size();
  108.         for (ForwardIt iter = begin; iter != end; iter++)
  109.             MPI_Recv(iter->data(), m, MPI_DOUBLE, sender, 0, MPI_COMM_WORLD, &result);
  110.         return result;
  111.     }
  112.  
  113.     static Vector matrix_multiply(const Matrix& lhs, const Vector& rhs) {
  114.         if (lhs[0].size() != rhs.size()) throw std::invalid_argument("can't multiply matrix and vector");
  115.         Vector result(lhs.size(), 0);
  116.         for (std::uint32_t i = 0; i < result.size(); i++)
  117.             for (std::uint32_t j = 0; j < lhs[i].size(); j++)
  118.                 result[i] += lhs[i][j] * rhs[j];
  119.         return result;
  120.     }
  121.  
  122.     static Vector vector_subtraction(const Vector& lhs, const Vector& rhs) {
  123.         if (lhs.size() != rhs.size()) throw std::invalid_argument("can't subtract vector and vector");
  124.         Vector result(lhs.size());
  125.         for (uint32_t idx = 0; idx < lhs.size(); idx++)
  126.             result[idx] = lhs[idx] - rhs[idx];
  127.         return result;
  128.     }
  129. };
  130.  
  131. class RootProcess : public Process {
  132.     int _proccesses_count;
  133.     Matrix SLE_coef;
  134.     Vector SLE_rhs;
  135.     Vector SLE_solution;
  136.     long double __denumerator_squared_st_diviation = 0;
  137. public:
  138.     RootProcess() noexcept: Process(0), SLE_coef(N, Vector(N, 0)),
  139.                             SLE_rhs(N, 0), SLE_solution(N, 0) {
  140.         MPI_Comm_size(MPI_COMM_WORLD, (int*) &_proccesses_count);
  141.     }
  142.  
  143.     void initialize() final {
  144.         generate_SLE(SLE_coef, SLE_rhs);
  145.         std::for_each(SLE_rhs.begin(), SLE_rhs.end(),
  146.                       [this](const double value) { __denumerator_squared_st_diviation += value * value; });
  147.         int slice = N / (_proccesses_count - 1);
  148.         auto begin = SLE_coef.begin();
  149.         auto end = min(begin + slice, SLE_coef.end());
  150.         for (int32_t process_id = 1; process_id < _proccesses_count && begin != SLE_coef.end(); process_id++) {
  151.             send_matrix(begin, end, process_id);
  152.             std::uint32_t begin_idx = std::distance(SLE_coef.begin(), begin), end_idx = std::distance(SLE_coef.begin(), end);
  153.             send_vector(SLE_rhs.begin() + begin_idx,
  154.                         SLE_rhs.begin() + end_idx,
  155.                         process_id);
  156.             send_vector(SLE_solution.begin(), SLE_solution.end(), process_id);
  157.             send_pair(begin_idx, end_idx, process_id);
  158.             begin = end;
  159.             end = min(end + slice, SLE_coef.end());
  160.         }
  161.     }
  162.  
  163.     void run() final {
  164.         long double metric = count_metric();
  165.         //std::cout << "Iteration #0. metric: " << metric << ", duration: 0s\n";
  166.         uint32_t idx = 1;
  167.         auto start = std::chrono::system_clock::now();
  168.         while (metric > eps) {
  169.             auto cycle_start = std::chrono::system_clock::now();
  170.             evaluate_iteration();
  171.             metric = count_metric();
  172.             auto cycle_duration = std::chrono::duration_cast<std::chrono::milliseconds>(
  173.                                                    std::chrono::system_clock::now() - cycle_start);
  174.             //std::cout << "Iteration #" << (idx++) << ". metric: " << metric << ", duration: " << cycle_duration.count() << "ms\n";
  175.         }
  176.         auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - start);
  177.         std::cout << "Elapsed Time: " << duration.count() << "ms. Solution: {\n";
  178.         std::for_each(SLE_solution.begin(), SLE_solution.end(), [](const double value) { std::cout << value << ", "; });
  179.         std::cout << "\n}\n";
  180.     }
  181.  
  182.     void finalize() final {
  183.         for (int32_t process_id = 1; process_id < _proccesses_count; process_id++)
  184.             send_command(Command::EXIT, process_id);
  185.     }
  186.  
  187. private:
  188.     long double count_metric() const noexcept {
  189.         for (int32_t process_id = 1; process_id < _proccesses_count; process_id++){
  190.             send_command(Command::EVALUATE_METRIC, process_id);
  191.             send_vector(SLE_solution.begin(), SLE_solution.end(), process_id);
  192.         }
  193.         long double numerator = 0;
  194.         for (int32_t process_id = 1; process_id < _proccesses_count; process_id++) {
  195.             long double received_numerator;
  196.             recv_value(received_numerator, process_id);
  197.             numerator += received_numerator;
  198.         }
  199.         return std::sqrt(numerator) / std::sqrt(__denumerator_squared_st_diviation);
  200.     }
  201.  
  202.     void evaluate_iteration() {
  203.         for (int32_t process_id = 1; process_id < _proccesses_count; process_id++)
  204.             send_command(Command::EVALUATE_ITERATION, process_id);
  205.         std::uint32_t solution_idx = 0;
  206.         for (int32_t process_id = 1; process_id < _proccesses_count; process_id++) {
  207.             std::uint32_t n;
  208.             recv_value(n, process_id);
  209.             Vector received_solution(n, 0);
  210.             recv_vector(received_solution.begin(), received_solution.end(), process_id);
  211.             auto slice = N / (_proccesses_count - 1);
  212.             for (std::uint32_t idx = slice * (process_id-1); idx < slice*process_id; idx++, solution_idx++)
  213.                 SLE_solution[solution_idx] = received_solution[idx];
  214.         }
  215.     }
  216.  
  217.     static void generate_SLE(Matrix& mt, Vector& rhs) noexcept {
  218.         for (uint64_t i = 0; i < mt.size(); i++)
  219.             for (uint64_t j = 0; j < mt[i].size(); j++)
  220.                 mt[i][j] = (i == j ? 2 : 1);
  221.         for (auto& elem: rhs)
  222.             elem = N + 1;
  223.     }
  224.  
  225.     static void send_command(const Command command, const int receiver) noexcept {
  226.         MPI_Send((uint8_t*) &command, 1, MPI_UINT8_T, receiver, 0, MPI_COMM_WORLD);
  227.     }
  228. };
  229.  
  230. class ChildProcess : public Process {
  231.     Matrix SLE_coef;
  232.     Vector SLE_rhs;
  233.     Vector SLE_solution;
  234.     std::pair<uint32_t, uint32_t> solution_range;
  235. public:
  236.     void initialize() final {
  237.         uint32_t n, m;
  238.         recv_pair(n, m, 0);
  239.         SLE_coef.resize(n, Vector(m, 0));
  240.         recv_matrix(SLE_coef.begin(), SLE_coef.end(), 0);
  241.         recv_value(n, 0);
  242.         SLE_rhs.resize(n, 0);
  243.         recv_vector(SLE_rhs.begin(), SLE_rhs.end(), 0);
  244.         recv_value(n, 0);
  245.         SLE_solution.resize(n, 0);
  246.         recv_vector(SLE_solution.begin(), SLE_solution.end(), 0);
  247.         recv_pair(solution_range.first, solution_range.second, 0);
  248.     }
  249.  
  250.     void run() final {
  251.         bool is_cancellation_requested{};
  252.         while (!is_cancellation_requested) {
  253.             Command current_command;
  254.             recv_command(current_command);
  255.             switch (current_command) {
  256.                 case EVALUATE_METRIC: {
  257.                     uint32_t n;
  258.                     recv_value(n, 0);
  259.                     recv_vector(SLE_solution.begin(), SLE_solution.end(), 0);
  260.                     const auto numerator = vector_subtraction(matrix_multiply(SLE_coef, SLE_solution), SLE_rhs);
  261.                     long double numerator_squared_st_diviation = 0, denumerator_squared_st_diviation = 0;
  262.                     std::for_each(numerator.begin(), numerator.end(),
  263.                                   [&numerator_squared_st_diviation](const double value) {
  264.                                       numerator_squared_st_diviation += value * value;
  265.                                   });
  266.                     send_value(numerator_squared_st_diviation, 0);
  267.                     break;
  268.                 }
  269.                 case EVALUATE_ITERATION: {
  270.                     auto vec = vector_subtraction(matrix_multiply(SLE_coef, SLE_solution), SLE_rhs);
  271.                     std::for_each(vec.begin(), vec.end(), [](double& value) { value *= lambda; });
  272.                     for (uint32_t i = solution_range.first; i < solution_range.second; i++)
  273.                         SLE_solution[i] -= vec[i-solution_range.first];
  274.                     send_vector(SLE_solution.begin(), SLE_solution.end(), 0);
  275.                     break;
  276.                 }
  277.                 case EXIT: {
  278.                     is_cancellation_requested = true;
  279.                     break;
  280.                 }
  281.             }
  282.         }
  283.     }
  284.  
  285.     void finalize() final {
  286.  
  287.     }
  288.  
  289. private:
  290.     static MPI_Status recv_command(Command& command) noexcept {
  291.         MPI_Status result;
  292.         MPI_Recv((uint8_t*) &command, 1, MPI_UINT8_T, 0, 0, MPI_COMM_WORLD, &result);
  293.         return result;
  294.     }
  295. };
  296.  
  297. void resolve_process_type(std::unique_ptr<Process>& ptr) {
  298.     int process_id;
  299.     MPI_Comm_rank(MPI_COMM_WORLD, (int*) &process_id);
  300.     if (process_id == 0)
  301.         ptr = std::move(std::make_unique<RootProcess>());
  302.     else
  303.         ptr = std::move(std::make_unique<ChildProcess>());
  304. }
  305.  
  306. int main(int argc, char** argv) {
  307.     MPI_Init(&argc, &argv);
  308.     std::unique_ptr<Process> process_ptr;
  309.     resolve_process_type(process_ptr);
  310.     process_ptr->initialize();
  311.     process_ptr->run();
  312.     process_ptr->finalize();
  313.     MPI_Finalize();
  314.     return 0;
  315. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement