| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "artyushkina_string_matrix/mpi/include/ops_mpi.hpp" | ||
| 2 | |||
| 3 | #include <mpi.h> | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <array> | ||
| 7 | #include <climits> | ||
| 8 | #include <cstddef> | ||
| 9 | #include <ranges> // IWYU pragma: keep | ||
| 10 | #include <utility> | ||
| 11 | #include <vector> | ||
| 12 | |||
| 13 | #include "artyushkina_string_matrix/common/include/common.hpp" | ||
| 14 | |||
| 15 | namespace artyushkina_string_matrix { | ||
| 16 | |||
| 17 | ✗ | ArtyushkinaStringMatrixMPI::ArtyushkinaStringMatrixMPI(const InType &in) { | |
| 18 | SetTypeOfTask(GetStaticTypeOfTask()); | ||
| 19 | |||
| 20 | ✗ | if (!in.empty()) { | |
| 21 | ✗ | GetInput() = in; | |
| 22 | } else { | ||
| 23 | ✗ | GetInput() = InType{}; | |
| 24 | } | ||
| 25 | |||
| 26 | ✗ | GetOutput() = OutType{}; | |
| 27 | ✗ | } | |
| 28 | |||
| 29 | ✗ | bool ArtyushkinaStringMatrixMPI::ValidationImpl() { | |
| 30 | const auto &input = GetInput(); | ||
| 31 | ✗ | if (input.empty()) { | |
| 32 | return false; | ||
| 33 | } | ||
| 34 | |||
| 35 | const std::size_t cols = input[0].size(); | ||
| 36 | ✗ | if (cols == 0) { | |
| 37 | return false; | ||
| 38 | } | ||
| 39 | |||
| 40 | return std::ranges::all_of(input, [cols](const auto &row) { return row.size() == cols; }); | ||
| 41 | } | ||
| 42 | |||
| 43 | ✗ | bool ArtyushkinaStringMatrixMPI::PreProcessingImpl() { | |
| 44 | GetOutput().clear(); | ||
| 45 | ✗ | return true; | |
| 46 | } | ||
| 47 | |||
| 48 | ✗ | std::vector<int> ArtyushkinaStringMatrixMPI::FlattenMatrix(const std::vector<std::vector<int>> &matrix) { | |
| 49 | ✗ | if (matrix.empty() || matrix[0].empty()) { | |
| 50 | ✗ | return {}; | |
| 51 | } | ||
| 52 | |||
| 53 | const std::size_t rows = matrix.size(); | ||
| 54 | const std::size_t cols = matrix[0].size(); | ||
| 55 | |||
| 56 | ✗ | std::vector<int> flat; | |
| 57 | ✗ | flat.reserve(rows * cols); | |
| 58 | |||
| 59 | ✗ | for (const auto &row : matrix) { | |
| 60 | ✗ | flat.insert(flat.end(), row.begin(), row.end()); | |
| 61 | } | ||
| 62 | |||
| 63 | return flat; | ||
| 64 | } | ||
| 65 | |||
| 66 | ✗ | std::pair<int, int> ArtyushkinaStringMatrixMPI::PrepareDimensions(const std::vector<std::vector<int>> &matrix, int rank, | |
| 67 | int &size) { | ||
| 68 | ✗ | int total_rows = static_cast<int>(matrix.size()); | |
| 69 | ✗ | int total_cols = (total_rows > 0) ? static_cast<int>(matrix[0].size()) : 0; | |
| 70 | |||
| 71 | ✗ | if (rank == 0 && total_rows > 0 && total_rows < size) { | |
| 72 | ✗ | size = std::min(total_rows, size); | |
| 73 | } | ||
| 74 | |||
| 75 | ✗ | if (size == 0) { | |
| 76 | ✗ | size = 1; | |
| 77 | } | ||
| 78 | |||
| 79 | ✗ | std::array<int, 2> dimensions = {total_rows, total_cols}; | |
| 80 | ✗ | MPI_Bcast(dimensions.data(), 2, MPI_INT, 0, MPI_COMM_WORLD); | |
| 81 | |||
| 82 | ✗ | return {dimensions[0], dimensions[1]}; | |
| 83 | } | ||
| 84 | |||
| 85 | ✗ | std::pair<int, int> ArtyushkinaStringMatrixMPI::CalculateProcessInfo(int total_rows, int size, int rank) { | |
| 86 | ✗ | if (size <= 0) { | |
| 87 | size = 1; | ||
| 88 | } | ||
| 89 | |||
| 90 | ✗ | const int rows_per_process = total_rows / size; | |
| 91 | ✗ | const int remainder = total_rows % size; | |
| 92 | |||
| 93 | ✗ | const int my_rows = rows_per_process + ((rank < remainder) ? 1 : 0); | |
| 94 | |||
| 95 | int offset = 0; | ||
| 96 | ✗ | for (int i = 0; i < rank; ++i) { | |
| 97 | ✗ | const int rows_for_i = rows_per_process + ((i < remainder) ? 1 : 0); | |
| 98 | ✗ | offset += rows_for_i; | |
| 99 | } | ||
| 100 | |||
| 101 | ✗ | return {my_rows, offset}; | |
| 102 | } | ||
| 103 | |||
| 104 | ✗ | std::vector<int> ArtyushkinaStringMatrixMPI::ScatterData(const std::vector<std::vector<int>> &matrix, int total_rows, | |
| 105 | int total_cols, int size, int rank, int my_rows) { | ||
| 106 | ✗ | if (size <= 0) { | |
| 107 | size = 1; | ||
| 108 | } | ||
| 109 | |||
| 110 | ✗ | std::vector<int> local_data(static_cast<std::size_t>(my_rows) * static_cast<std::size_t>(total_cols)); | |
| 111 | |||
| 112 | ✗ | if (rank == 0) { | |
| 113 | ✗ | std::vector<int> flat_matrix = FlattenMatrix(matrix); | |
| 114 | |||
| 115 | ✗ | const int rows_per_process = total_rows / size; | |
| 116 | ✗ | const int remainder = total_rows % size; | |
| 117 | |||
| 118 | ✗ | std::vector<int> send_counts(size); | |
| 119 | ✗ | std::vector<int> displacements(size); | |
| 120 | |||
| 121 | int offset = 0; | ||
| 122 | ✗ | for (int i = 0; i < size; ++i) { | |
| 123 | ✗ | const int rows_for_i = rows_per_process + ((i < remainder) ? 1 : 0); | |
| 124 | ✗ | send_counts[i] = rows_for_i * total_cols; | |
| 125 | ✗ | displacements[i] = offset * total_cols; | |
| 126 | ✗ | offset += rows_for_i; | |
| 127 | } | ||
| 128 | |||
| 129 | ✗ | MPI_Scatterv(flat_matrix.data(), send_counts.data(), displacements.data(), MPI_INT, local_data.data(), | |
| 130 | my_rows * total_cols, MPI_INT, 0, MPI_COMM_WORLD); | ||
| 131 | } else { | ||
| 132 | ✗ | MPI_Scatterv(nullptr, nullptr, nullptr, MPI_INT, local_data.data(), my_rows * total_cols, MPI_INT, 0, | |
| 133 | MPI_COMM_WORLD); | ||
| 134 | } | ||
| 135 | |||
| 136 | ✗ | return local_data; | |
| 137 | } | ||
| 138 | |||
| 139 | ✗ | std::vector<int> ArtyushkinaStringMatrixMPI::ComputeLocalMinima(const std::vector<int> &local_data, int my_rows, | |
| 140 | int total_cols) { | ||
| 141 | ✗ | std::vector<int> local_minima(static_cast<std::size_t>(my_rows), INT_MAX); | |
| 142 | |||
| 143 | ✗ | for (int i = 0; i < my_rows; ++i) { | |
| 144 | ✗ | for (int j = 0; j < total_cols; ++j) { | |
| 145 | ✗ | const int index = (i * total_cols) + j; | |
| 146 | ✗ | const int val = local_data[static_cast<std::size_t>(index)]; | |
| 147 | ✗ | local_minima[static_cast<std::size_t>(i)] = std::min(val, local_minima[static_cast<std::size_t>(i)]); | |
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | ✗ | return local_minima; | |
| 152 | } | ||
| 153 | |||
| 154 | ✗ | std::vector<int> ArtyushkinaStringMatrixMPI::GatherResults(const std::vector<int> &local_minima, int total_rows, | |
| 155 | int size, int rank, int my_rows) { | ||
| 156 | ✗ | std::vector<int> global_minima; | |
| 157 | |||
| 158 | ✗ | if (rank == 0) { | |
| 159 | ✗ | global_minima.resize(static_cast<std::size_t>(total_rows)); | |
| 160 | } | ||
| 161 | |||
| 162 | ✗ | if (size <= 0) { | |
| 163 | size = 1; | ||
| 164 | } | ||
| 165 | |||
| 166 | ✗ | const int rows_per_process = total_rows / size; | |
| 167 | ✗ | const int remainder = total_rows % size; | |
| 168 | |||
| 169 | ✗ | std::vector<int> recv_counts(size); | |
| 170 | ✗ | std::vector<int> displacements_recv(size); | |
| 171 | |||
| 172 | int rows_so_far = 0; | ||
| 173 | ✗ | for (int i = 0; i < size; ++i) { | |
| 174 | ✗ | const int rows_for_i = rows_per_process + ((i < remainder) ? 1 : 0); | |
| 175 | ✗ | recv_counts[i] = rows_for_i; | |
| 176 | ✗ | displacements_recv[i] = rows_so_far; | |
| 177 | ✗ | rows_so_far += rows_for_i; | |
| 178 | } | ||
| 179 | |||
| 180 | ✗ | MPI_Gatherv(local_minima.data(), my_rows, MPI_INT, global_minima.data(), recv_counts.data(), | |
| 181 | displacements_recv.data(), MPI_INT, 0, MPI_COMM_WORLD); | ||
| 182 | |||
| 183 | ✗ | return global_minima; | |
| 184 | } | ||
| 185 | |||
| 186 | ✗ | bool ArtyushkinaStringMatrixMPI::RunImpl() { | |
| 187 | ✗ | int rank = 0; | |
| 188 | ✗ | int size = 1; | |
| 189 | ✗ | MPI_Comm_rank(MPI_COMM_WORLD, &rank); | |
| 190 | ✗ | MPI_Comm_size(MPI_COMM_WORLD, &size); | |
| 191 | |||
| 192 | const auto &matrix = GetInput(); | ||
| 193 | |||
| 194 | ✗ | if (matrix.empty()) { | |
| 195 | ✗ | if (rank == 0) { | |
| 196 | GetOutput().clear(); | ||
| 197 | } | ||
| 198 | ✗ | return true; | |
| 199 | } | ||
| 200 | |||
| 201 | ✗ | auto [total_rows, total_cols] = PrepareDimensions(matrix, rank, size); | |
| 202 | ✗ | auto [my_rows, offset] = CalculateProcessInfo(total_rows, size, rank); | |
| 203 | |||
| 204 | ✗ | std::vector<int> local_data = ScatterData(matrix, total_rows, total_cols, size, rank, my_rows); | |
| 205 | |||
| 206 | ✗ | std::vector<int> local_minima = ComputeLocalMinima(local_data, my_rows, total_cols); | |
| 207 | |||
| 208 | ✗ | std::vector<int> global_minima = GatherResults(local_minima, total_rows, size, rank, my_rows); | |
| 209 | |||
| 210 | ✗ | if (rank == 0) { | |
| 211 | GetOutput() = std::move(global_minima); | ||
| 212 | } else { | ||
| 213 | GetOutput().clear(); | ||
| 214 | } | ||
| 215 | |||
| 216 | return true; | ||
| 217 | } | ||
| 218 | |||
| 219 | ✗ | bool ArtyushkinaStringMatrixMPI::PostProcessingImpl() { | |
| 220 | ✗ | return true; | |
| 221 | } | ||
| 222 | |||
| 223 | } // namespace artyushkina_string_matrix | ||
| 224 |