| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "leonova_a_star/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 <cstdint> | ||
| 10 | #include <limits> | ||
| 11 | #include <new> | ||
| 12 | #include <utility> | ||
| 13 | #include <vector> | ||
| 14 | |||
| 15 | #include "leonova_a_star/common/include/common.hpp" | ||
| 16 | |||
| 17 | namespace leonova_a_star { | ||
| 18 | |||
| 19 | namespace { | ||
| 20 | |||
| 21 | constexpr size_t kMaxMatrixSize = 10000; | ||
| 22 | constexpr size_t kMaxVectorSize = 1000000; | ||
| 23 | constexpr int kMaxProcesses = 1024; | ||
| 24 | |||
| 25 | bool CheckMatricesNotEmpty(const std::vector<std::vector<int>> &matrix_a, | ||
| 26 | const std::vector<std::vector<int>> &matrix_b) { | ||
| 27 |
8/16✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 12 times.
✓ Branch 8 taken 12 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 12 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 12 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 12 times.
|
24 | return !matrix_a.empty() && !matrix_b.empty() && !matrix_a[0].empty() && !matrix_b[0].empty(); |
| 28 | } | ||
| 29 | |||
| 30 | 15 | void FillMatrixBRow(int row_idx, int cols, const std::vector<std::vector<int>> &matrix_b, std::vector<int> &output) { | |
| 31 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
|
15 | if (static_cast<size_t>(row_idx) >= matrix_b.size()) { |
| 32 | ✗ | output.insert(output.end(), static_cast<size_t>(cols), 0); | |
| 33 | ✗ | return; | |
| 34 | } | ||
| 35 | |||
| 36 | const auto &row = matrix_b[static_cast<size_t>(row_idx)]; | ||
| 37 | size_t row_size = row.size(); | ||
| 38 |
2/2✓ Branch 0 taken 30 times.
✓ Branch 1 taken 15 times.
|
45 | for (int col_idx = 0; col_idx < cols; ++col_idx) { |
| 39 | 30 | output.push_back(std::cmp_less(col_idx, row_size) ? row[static_cast<size_t>(col_idx)] : 0); | |
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | void FillMatrixBFlat(int cols_a_int, int cols_b_int, const std::vector<std::vector<int>> &matrix_b, | ||
| 44 | std::vector<int> &matrix_b_flat) { | ||
| 45 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 6 times.
|
21 | for (int index = 0; index < cols_a_int; ++index) { |
| 46 | 15 | FillMatrixBRow(index, cols_b_int, matrix_b, matrix_b_flat); | |
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | 12 | void DistributeMatrixB(int rank, int cols_a_int, int cols_b_int, const std::vector<std::vector<int>> &matrix_b, | |
| 51 | std::vector<int> &matrix_b_flat) { | ||
| 52 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
|
12 | if (rank == 0) { |
| 53 | 6 | size_t reserve_size = static_cast<size_t>(cols_a_int) * static_cast<size_t>(cols_b_int); | |
| 54 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | if (reserve_size <= kMaxVectorSize) { |
| 55 | 6 | matrix_b_flat.reserve(reserve_size); | |
| 56 | } | ||
| 57 | FillMatrixBFlat(cols_a_int, cols_b_int, matrix_b, matrix_b_flat); | ||
| 58 | } else { | ||
| 59 | 6 | size_t new_size = static_cast<size_t>(cols_a_int) * static_cast<size_t>(cols_b_int); | |
| 60 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | if (new_size <= kMaxVectorSize) { |
| 61 | 6 | matrix_b_flat.resize(new_size); | |
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | 12 | int total_elements = cols_a_int * cols_b_int; | |
| 66 | // Проверка на переполнение | ||
| 67 |
3/6✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
|
12 | if (total_elements > 0 && std::cmp_less_equal(total_elements, static_cast<int>(kMaxVectorSize)) && |
| 68 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | static_cast<size_t>(total_elements) <= matrix_b_flat.size()) { |
| 69 | 12 | MPI_Bcast(matrix_b_flat.data(), total_elements, MPI_INT, 0, MPI_COMM_WORLD); | |
| 70 | } | ||
| 71 | 12 | } | |
| 72 | |||
| 73 | 17 | bool SafeVectorResize(std::vector<int> &vec, size_t new_size) { | |
| 74 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
|
17 | if (new_size == 0 || new_size > kMaxVectorSize) { |
| 75 | return false; | ||
| 76 | } | ||
| 77 | |||
| 78 | try { | ||
| 79 |
1/2✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
|
17 | vec.resize(new_size); |
| 80 | return true; | ||
| 81 | ✗ | } catch (const std::bad_alloc &) { | |
| 82 | return false; | ||
| 83 | ✗ | } | |
| 84 | } | ||
| 85 | |||
| 86 | 11 | bool SafeVectorResize(std::vector<int> &vec, size_t new_size, int value) { | |
| 87 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if (new_size == 0 || new_size > kMaxVectorSize) { |
| 88 | return false; | ||
| 89 | } | ||
| 90 | |||
| 91 | try { | ||
| 92 | vec.assign(new_size, value); | ||
| 93 | return true; | ||
| 94 | ✗ | } catch (const std::bad_alloc &) { | |
| 95 | return false; | ||
| 96 | ✗ | } | |
| 97 | } | ||
| 98 | |||
| 99 | 12 | bool PrepareRowDistribution(int size, int rows_a_int, int cols_a_int, std::vector<int> &rows_per_rank, | |
| 100 | std::vector<int> &displacements, std::vector<int> &elements_per_rank) { | ||
| 101 |
2/4✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
|
12 | if (rows_a_int <= 0 || cols_a_int <= 0 || size <= 0 || size > kMaxProcesses || |
| 102 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | std::cmp_greater(static_cast<size_t>(rows_a_int), kMaxMatrixSize)) { |
| 103 | rows_per_rank.clear(); | ||
| 104 | displacements.clear(); | ||
| 105 | elements_per_rank.clear(); | ||
| 106 | ✗ | return false; | |
| 107 | } | ||
| 108 | |||
| 109 | // Проверка на переполнение при умножении | ||
| 110 | 12 | if (static_cast<size_t>(rows_a_int) > SIZE_MAX / static_cast<size_t>(cols_a_int) || | |
| 111 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | static_cast<size_t>(rows_a_int) * static_cast<size_t>(cols_a_int) > kMaxVectorSize) { |
| 112 | rows_per_rank.clear(); | ||
| 113 | displacements.clear(); | ||
| 114 | elements_per_rank.clear(); | ||
| 115 | ✗ | return false; | |
| 116 | } | ||
| 117 | |||
| 118 | 12 | int rows_per_process = rows_a_int / size; | |
| 119 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | int extra_rows = rows_a_int % size; |
| 120 | |||
| 121 | rows_per_rank.clear(); | ||
| 122 | displacements.clear(); | ||
| 123 | elements_per_rank.clear(); | ||
| 124 | |||
| 125 | 12 | rows_per_rank.reserve(static_cast<size_t>(size)); | |
| 126 | 12 | displacements.reserve(static_cast<size_t>(size)); | |
| 127 | 12 | elements_per_rank.reserve(static_cast<size_t>(size)); | |
| 128 | |||
| 129 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 12 times.
|
36 | for (int index = 0; index < size; ++index) { |
| 130 | 24 | int rows = rows_per_process; | |
| 131 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 20 times.
|
24 | if (index < extra_rows) { |
| 132 | 4 | rows += 1; | |
| 133 | } | ||
| 134 | rows_per_rank.push_back(rows); | ||
| 135 | } | ||
| 136 | |||
| 137 | int offset = 0; | ||
| 138 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 12 times.
|
36 | for (int index = 0; index < size; ++index) { |
| 139 | // Проверка на переполнение | ||
| 140 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
|
24 | if (offset > INT_MAX / cols_a_int) { |
| 141 | rows_per_rank.clear(); | ||
| 142 | displacements.clear(); | ||
| 143 | elements_per_rank.clear(); | ||
| 144 | ✗ | return false; | |
| 145 | } | ||
| 146 | 24 | displacements.push_back(offset * cols_a_int); | |
| 147 | 24 | offset += rows_per_rank[static_cast<size_t>(index)]; | |
| 148 | } | ||
| 149 | |||
| 150 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 12 times.
|
36 | for (int index = 0; index < size; ++index) { |
| 151 | 24 | elements_per_rank.push_back(rows_per_rank[static_cast<size_t>(index)] * cols_a_int); | |
| 152 | } | ||
| 153 | |||
| 154 | return true; | ||
| 155 | } | ||
| 156 | |||
| 157 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | void FlattenMatrixA(const std::vector<std::vector<int>> &matrix_a, int cols_a_int, std::vector<int> &matrix_a_flat) { |
| 158 | size_t rows = matrix_a.size(); | ||
| 159 | |||
| 160 | // Проверка на переполнение перед умножением | ||
| 161 |
2/4✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
|
6 | if (static_cast<size_t>(cols_a_int) > 0 && rows > SIZE_MAX / static_cast<size_t>(cols_a_int)) { |
| 162 | return; | ||
| 163 | } | ||
| 164 | |||
| 165 | 6 | size_t reserve_size = rows * static_cast<size_t>(cols_a_int); | |
| 166 | |||
| 167 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | if (reserve_size == 0 || reserve_size > kMaxVectorSize) { |
| 168 | return; | ||
| 169 | } | ||
| 170 | |||
| 171 | 6 | matrix_a_flat.reserve(reserve_size); | |
| 172 | |||
| 173 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
|
18 | for (size_t i = 0; i < rows; ++i) { |
| 174 | const auto &row = matrix_a[i]; | ||
| 175 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | size_t row_size = row.size(); |
| 176 | size_t copy_size = std::min(row_size, static_cast<size_t>(cols_a_int)); | ||
| 177 | |||
| 178 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | if (copy_size > 0) { |
| 179 | // Безопасное преобразование с проверкой | ||
| 180 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | if (copy_size > static_cast<size_t>(std::numeric_limits<int64_t>::max())) { |
| 181 | ✗ | return; | |
| 182 | } | ||
| 183 | auto copy_offset = static_cast<int64_t>(copy_size); | ||
| 184 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | if (row.begin() + copy_offset > row.end()) { |
| 185 | return; | ||
| 186 | } | ||
| 187 | 12 | matrix_a_flat.insert(matrix_a_flat.end(), row.begin(), row.begin() + copy_offset); | |
| 188 | } | ||
| 189 | |||
| 190 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (std::cmp_greater(static_cast<size_t>(cols_a_int), row_size)) { |
| 191 | ✗ | size_t padding = static_cast<size_t>(cols_a_int) - row_size; | |
| 192 | ✗ | matrix_a_flat.insert(matrix_a_flat.end(), padding, 0); | |
| 193 | } | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 197 | 11 | void ScatterMatrixA(int rank, int cols_a_int, const std::vector<std::vector<int>> &matrix_a, | |
| 198 | const std::vector<int> &displacements, const std::vector<int> &elements_per_rank, | ||
| 199 | std::vector<int> &local_rows_flat) { | ||
| 200 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 5 times.
|
11 | if (rank == 0) { |
| 201 | 6 | std::vector<int> matrix_a_flat; | |
| 202 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | FlattenMatrixA(matrix_a, cols_a_int, matrix_a_flat); |
| 203 | |||
| 204 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (matrix_a_flat.size() > kMaxVectorSize) { |
| 205 | ✗ | if (!local_rows_flat.empty()) { | |
| 206 | std::ranges::fill(local_rows_flat, 0); | ||
| 207 | } | ||
| 208 | return; | ||
| 209 | } | ||
| 210 | |||
| 211 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | MPI_Scatterv(matrix_a_flat.data(), elements_per_rank.data(), displacements.data(), MPI_INT, local_rows_flat.data(), |
| 212 | elements_per_rank[static_cast<size_t>(rank)], MPI_INT, 0, MPI_COMM_WORLD); | ||
| 213 | } else { | ||
| 214 | 5 | MPI_Scatterv(nullptr, elements_per_rank.data(), displacements.data(), MPI_INT, local_rows_flat.data(), | |
| 215 | 5 | elements_per_rank[static_cast<size_t>(rank)], MPI_INT, 0, MPI_COMM_WORLD); | |
| 216 | } | ||
| 217 | } | ||
| 218 | |||
| 219 | 30 | void MultiplyRowByElement(int value, const std::vector<int> &b_row, int *result_row, int cols) { | |
| 220 |
3/6✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 30 times.
✗ Branch 5 not taken.
|
30 | if (result_row == nullptr || b_row.empty() || cols <= 0) { |
| 221 | ✗ | return; | |
| 222 | } | ||
| 223 | |||
| 224 | 30 | size_t b_size = b_row.size(); | |
| 225 |
1/2✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
|
30 | size_t limit = std::min(b_size, static_cast<size_t>(cols)); |
| 226 | |||
| 227 | // Проверка, что result_row имеет достаточно места | ||
| 228 |
1/2✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
|
30 | if (limit == 0) { |
| 229 | return; | ||
| 230 | } | ||
| 231 | |||
| 232 |
2/2✓ Branch 0 taken 66 times.
✓ Branch 1 taken 30 times.
|
96 | for (size_t j = 0; j < limit; ++j) { |
| 233 | 66 | result_row[j] += value * b_row[j]; | |
| 234 | } | ||
| 235 | } | ||
| 236 | |||
| 237 | 12 | void MultiplyRow(const std::vector<int> &row_a, const std::vector<std::vector<int>> &local_b, int *result_row, | |
| 238 | int cols_a, int cols_b) { | ||
| 239 |
4/8✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 12 times.
|
12 | if (result_row == nullptr || row_a.empty() || local_b.empty() || cols_a <= 0 || cols_b <= 0) { |
| 240 | return; | ||
| 241 | } | ||
| 242 | |||
| 243 | size_t row_a_size = row_a.size(); | ||
| 244 | size_t local_b_size = local_b.size(); | ||
| 245 | size_t limit = std::min(row_a_size, local_b_size); | ||
| 246 | 12 | limit = std::min(limit, static_cast<size_t>(cols_a)); | |
| 247 | |||
| 248 |
2/2✓ Branch 0 taken 30 times.
✓ Branch 1 taken 12 times.
|
42 | for (size_t k = 0; k < limit; ++k) { |
| 249 | // Проверка, что local_b[k] существует | ||
| 250 |
1/2✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
|
30 | if (k >= local_b.size()) { |
| 251 | break; | ||
| 252 | } | ||
| 253 | 30 | int aik = row_a[k]; | |
| 254 | const auto &b_row = local_b[k]; | ||
| 255 | 30 | MultiplyRowByElement(aik, b_row, result_row, cols_b); | |
| 256 | } | ||
| 257 | } | ||
| 258 | |||
| 259 |
1/2✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
|
11 | void ComputeLocalMultiplication(int local_rows_count, int cols_a_int, int cols_b_int, |
| 260 | const std::vector<std::vector<int>> &local_a, | ||
| 261 | const std::vector<std::vector<int>> &local_b, std::vector<int> &local_result_flat) { | ||
| 262 |
2/4✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
|
11 | if (local_result_flat.empty() || cols_b_int <= 0) { |
| 263 | return; | ||
| 264 | } | ||
| 265 | |||
| 266 | std::ranges::fill(local_result_flat, 0); | ||
| 267 | |||
| 268 | 11 | size_t rows = std::min(static_cast<size_t>(local_rows_count), local_a.size()); | |
| 269 | 11 | auto cols_b = static_cast<size_t>(cols_b_int); | |
| 270 | |||
| 271 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 11 times.
|
23 | for (size_t i = 0; i < rows; ++i) { |
| 272 | // Проверка, что local_a[i] существует | ||
| 273 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | if (i >= local_a.size()) { |
| 274 | break; | ||
| 275 | } | ||
| 276 | |||
| 277 | // Проверка на переполнение при вычислении offset | ||
| 278 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | if (i > SIZE_MAX / cols_b) { |
| 279 | break; | ||
| 280 | } | ||
| 281 | |||
| 282 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | size_t offset = i * cols_b; |
| 283 | |||
| 284 | // Проверка границ | ||
| 285 |
2/4✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
|
12 | if (offset > local_result_flat.size() || local_result_flat.size() - offset < cols_b) { |
| 286 | break; | ||
| 287 | } | ||
| 288 | |||
| 289 | 12 | int *result_row = local_result_flat.data() + offset; | |
| 290 | 12 | MultiplyRow(local_a[i], local_b, result_row, cols_a_int, cols_b_int); | |
| 291 | } | ||
| 292 | } | ||
| 293 | |||
| 294 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | bool PrepareGatherParameters(const std::vector<int> &rows_per_rank, int cols_b_int, |
| 295 | std::vector<int> &result_elements_per_rank, std::vector<int> &result_displacements) { | ||
| 296 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if (rows_per_rank.empty()) { |
| 297 | result_elements_per_rank.clear(); | ||
| 298 | result_displacements.clear(); | ||
| 299 | ✗ | return true; | |
| 300 | } | ||
| 301 | |||
| 302 | size_t size = rows_per_rank.size(); | ||
| 303 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if (size > kMaxVectorSize) { |
| 304 | result_elements_per_rank.clear(); | ||
| 305 | result_displacements.clear(); | ||
| 306 | ✗ | return false; | |
| 307 | } | ||
| 308 | |||
| 309 | result_elements_per_rank.clear(); | ||
| 310 | result_displacements.clear(); | ||
| 311 | |||
| 312 | 11 | result_elements_per_rank.reserve(size); | |
| 313 | 11 | result_displacements.reserve(size); | |
| 314 | |||
| 315 | 11 | int displacement = 0; | |
| 316 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 22 times.
|
33 | for (size_t index = 0; index < size; ++index) { |
| 317 | 22 | int rows = rows_per_rank[index]; | |
| 318 | // Проверка на переполнение | ||
| 319 |
4/6✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 21 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 21 times.
|
22 | if (rows < 0 || cols_b_int < 0 || (rows > 0 && cols_b_int > INT_MAX / rows)) { |
| 320 | result_elements_per_rank.clear(); | ||
| 321 | result_displacements.clear(); | ||
| 322 | ✗ | return false; | |
| 323 | } | ||
| 324 | |||
| 325 |
1/2✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
|
22 | int elements = rows * cols_b_int; |
| 326 | result_elements_per_rank.push_back(elements); | ||
| 327 | result_displacements.push_back(displacement); | ||
| 328 | |||
| 329 | // Проверка на переполнение displacement | ||
| 330 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
|
22 | if (displacement > INT_MAX - elements) { |
| 331 | result_elements_per_rank.clear(); | ||
| 332 | result_displacements.clear(); | ||
| 333 | ✗ | return false; | |
| 334 | } | ||
| 335 | 22 | displacement += elements; | |
| 336 | } | ||
| 337 | |||
| 338 | return true; | ||
| 339 | } | ||
| 340 | |||
| 341 | 11 | void GatherResults(int local_rows_count, int cols_b_int, const std::vector<int> &rows_per_rank, | |
| 342 | const std::vector<int> &local_result_flat, std::vector<int> &full_result_flat) { | ||
| 343 | 11 | std::vector<int> result_elements_per_rank; | |
| 344 | 11 | std::vector<int> result_displacements; | |
| 345 | |||
| 346 |
2/4✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
✗ Branch 4 not taken.
|
11 | if (!PrepareGatherParameters(rows_per_rank, cols_b_int, result_elements_per_rank, result_displacements)) { |
| 347 | return; | ||
| 348 | } | ||
| 349 | |||
| 350 |
2/4✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
|
11 | if (result_elements_per_rank.empty() || result_displacements.empty()) { |
| 351 | return; | ||
| 352 | } | ||
| 353 | |||
| 354 | // Проверка на переполнение | ||
| 355 |
2/4✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
|
11 | if (local_rows_count > 0 && cols_b_int > 0 && local_rows_count <= INT_MAX / cols_b_int) { |
| 356 | 11 | int send_count = local_rows_count * cols_b_int; | |
| 357 |
1/2✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
|
11 | if (send_count > 0 && static_cast<size_t>(send_count) <= local_result_flat.size()) { |
| 358 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | MPI_Gatherv(local_result_flat.data(), send_count, MPI_INT, full_result_flat.data(), |
| 359 | result_elements_per_rank.data(), result_displacements.data(), MPI_INT, 0, MPI_COMM_WORLD); | ||
| 360 | } | ||
| 361 | } | ||
| 362 | } | ||
| 363 | |||
| 364 | 6 | std::vector<std::vector<int>> ConvertFlatToMatrix(const std::vector<int> &flat_data, int rows, int cols) { | |
| 365 |
2/4✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
|
6 | if (rows <= 0 || cols <= 0 || flat_data.empty()) { |
| 366 | ✗ | return {}; | |
| 367 | } | ||
| 368 | |||
| 369 | // Проверка на переполнение | ||
| 370 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (rows > INT_MAX / cols) { |
| 371 | ✗ | return {}; | |
| 372 | } | ||
| 373 | |||
| 374 | // Проверка на переполнение size_t | ||
| 375 | 6 | if (static_cast<size_t>(rows) > SIZE_MAX / static_cast<size_t>(cols)) { | |
| 376 | return {}; | ||
| 377 | } | ||
| 378 | |||
| 379 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | size_t expected_size = static_cast<size_t>(rows) * static_cast<size_t>(cols); |
| 380 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (flat_data.size() < expected_size) { |
| 381 | ✗ | return {}; | |
| 382 | } | ||
| 383 | |||
| 384 | 6 | std::vector<std::vector<int>> result; | |
| 385 | try { | ||
| 386 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | result.resize(static_cast<size_t>(rows)); |
| 387 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
|
18 | for (auto &row : result) { |
| 388 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | row.resize(static_cast<size_t>(cols), 0); |
| 389 | } | ||
| 390 | ✗ | } catch (const std::bad_alloc &) { | |
| 391 | ✗ | return {}; | |
| 392 | ✗ | } | |
| 393 | |||
| 394 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
|
18 | for (int i = 0; i < rows; ++i) { |
| 395 | // Проверка на переполнение при вычислении индекса | ||
| 396 | size_t row_offset = 0; | ||
| 397 | 12 | if (static_cast<size_t>(i) > SIZE_MAX / static_cast<size_t>(cols)) { | |
| 398 | return {}; | ||
| 399 | } | ||
| 400 | 12 | row_offset = static_cast<size_t>(i) * static_cast<size_t>(cols); | |
| 401 | |||
| 402 |
2/2✓ Branch 0 taken 26 times.
✓ Branch 1 taken 12 times.
|
38 | for (int j = 0; j < cols; ++j) { |
| 403 |
1/2✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
|
26 | size_t idx = row_offset + static_cast<size_t>(j); |
| 404 |
1/2✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
|
26 | if (idx < flat_data.size()) { |
| 405 | 26 | result[static_cast<size_t>(i)][static_cast<size_t>(j)] = flat_data[idx]; | |
| 406 | } | ||
| 407 | } | ||
| 408 | } | ||
| 409 | |||
| 410 | return result; | ||
| 411 | 6 | } | |
| 412 | |||
| 413 | 12 | std::pair<bool, std::array<int, 3>> ValidateAndGetDimensions(int rank, int size, | |
| 414 | const std::vector<std::vector<int>> &matrix_a, | ||
| 415 | const std::vector<std::vector<int>> &matrix_b) { | ||
| 416 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (size <= 0 || size > kMaxProcesses) { |
| 417 | ✗ | return {false, {}}; | |
| 418 | } | ||
| 419 | |||
| 420 | if (!CheckMatricesNotEmpty(matrix_a, matrix_b)) { | ||
| 421 | ✗ | return {false, {}}; | |
| 422 | } | ||
| 423 | |||
| 424 | size_t rows_a = matrix_a.size(); | ||
| 425 | size_t cols_a = matrix_a[0].size(); | ||
| 426 | size_t cols_b = matrix_b[0].size(); | ||
| 427 | |||
| 428 |
2/4✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
|
12 | if (rows_a > kMaxMatrixSize || cols_a > kMaxMatrixSize || cols_b > kMaxMatrixSize) { |
| 429 | ✗ | return {false, {}}; | |
| 430 | } | ||
| 431 | |||
| 432 | 12 | std::array<int, 3> dims{0, 0, 0}; | |
| 433 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
|
12 | if (rank == 0) { |
| 434 | 6 | dims[0] = static_cast<int>(rows_a); | |
| 435 | 6 | dims[1] = static_cast<int>(cols_a); | |
| 436 | 6 | dims[2] = static_cast<int>(cols_b); | |
| 437 | } | ||
| 438 | |||
| 439 | 12 | MPI_Bcast(dims.data(), 3, MPI_INT, 0, MPI_COMM_WORLD); | |
| 440 | 12 | return {true, dims}; | |
| 441 | } | ||
| 442 | |||
| 443 | 12 | bool ValidateDimensions(int rows_a_int, int cols_a_int, int cols_b_int) { | |
| 444 |
2/4✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
|
12 | if (rows_a_int <= 0 || cols_a_int <= 0 || cols_b_int <= 0) { |
| 445 | return false; | ||
| 446 | } | ||
| 447 | |||
| 448 | 24 | if (std::cmp_greater(static_cast<size_t>(rows_a_int), kMaxMatrixSize) || | |
| 449 |
2/4✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
|
12 | std::cmp_greater(static_cast<size_t>(cols_a_int), kMaxMatrixSize) || |
| 450 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | std::cmp_greater(static_cast<size_t>(cols_b_int), kMaxMatrixSize)) { |
| 451 | return false; | ||
| 452 | } | ||
| 453 | |||
| 454 | // Проверка на возможное переполнение при умножении | ||
| 455 | if (static_cast<size_t>(rows_a_int) > SIZE_MAX / static_cast<size_t>(cols_a_int) || | ||
| 456 | static_cast<size_t>(cols_a_int) > SIZE_MAX / static_cast<size_t>(cols_b_int) || | ||
| 457 | static_cast<size_t>(rows_a_int) > SIZE_MAX / static_cast<size_t>(cols_b_int)) { | ||
| 458 | return false; | ||
| 459 | } | ||
| 460 | |||
| 461 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | if (static_cast<size_t>(rows_a_int) * static_cast<size_t>(cols_a_int) > kMaxVectorSize || |
| 462 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | static_cast<size_t>(cols_a_int) * static_cast<size_t>(cols_b_int) > kMaxVectorSize || |
| 463 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | static_cast<size_t>(rows_a_int) * static_cast<size_t>(cols_b_int) > kMaxVectorSize) { |
| 464 | ✗ | return false; | |
| 465 | } | ||
| 466 | |||
| 467 | return true; | ||
| 468 | } | ||
| 469 | |||
| 470 | 12 | bool PrepareLocalData(int rank, int cols_a_int, int cols_b_int, const std::vector<std::vector<int>> &matrix_b, | |
| 471 | std::vector<int> &matrix_b_flat, const std::vector<int> &elements_per_rank, | ||
| 472 | std::vector<int> &local_rows_flat) { | ||
| 473 | 12 | DistributeMatrixB(rank, cols_a_int, cols_b_int, matrix_b, matrix_b_flat); | |
| 474 | |||
| 475 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | auto rank_idx = static_cast<size_t>(rank); |
| 476 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (rank_idx >= elements_per_rank.size()) { |
| 477 | return false; | ||
| 478 | } | ||
| 479 | |||
| 480 | 12 | int elements = elements_per_rank[rank_idx]; | |
| 481 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 11 times.
|
12 | if (elements <= 0) { |
| 482 | return false; | ||
| 483 | } | ||
| 484 | |||
| 485 | try { | ||
| 486 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | local_rows_flat.reserve(static_cast<size_t>(elements)); |
| 487 | ✗ | } catch (const std::bad_alloc &) { | |
| 488 | return false; | ||
| 489 | ✗ | } | |
| 490 | |||
| 491 | 11 | return SafeVectorResize(local_rows_flat, static_cast<size_t>(elements)); | |
| 492 | } | ||
| 493 | |||
| 494 | // Вспомогательная функция для заполнения матрицы из плоского массива | ||
| 495 | 22 | bool FillMatrixFromFlat(std::vector<std::vector<int>> &matrix, int rows, int cols, const std::vector<int> &flat_data, | |
| 496 | size_t start_idx) { | ||
| 497 |
3/6✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 22 times.
|
22 | if (rows <= 0 || cols <= 0 || matrix.empty() || flat_data.empty()) { |
| 498 | return false; | ||
| 499 | } | ||
| 500 | |||
| 501 | // Проверка на переполнение при вычислении индексов | ||
| 502 | 22 | if (static_cast<size_t>(rows) > SIZE_MAX / static_cast<size_t>(cols)) { | |
| 503 | return false; | ||
| 504 | } | ||
| 505 | |||
| 506 |
2/2✓ Branch 0 taken 39 times.
✓ Branch 1 taken 22 times.
|
61 | for (int i = 0; i < rows; ++i) { |
| 507 | // Проверка на переполнение | ||
| 508 | 39 | if (static_cast<size_t>(i) > SIZE_MAX / static_cast<size_t>(cols)) { | |
| 509 | return false; | ||
| 510 | } | ||
| 511 | |||
| 512 | 39 | size_t row_offset = static_cast<size_t>(i) * static_cast<size_t>(cols); | |
| 513 | size_t base_idx = 0; | ||
| 514 | |||
| 515 | // Проверка на переполнение при сложении | ||
| 516 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
|
39 | if (start_idx > SIZE_MAX - row_offset) { |
| 517 | return false; | ||
| 518 | } | ||
| 519 | |||
| 520 | 39 | base_idx = start_idx + row_offset; | |
| 521 | |||
| 522 |
2/2✓ Branch 0 taken 87 times.
✓ Branch 1 taken 39 times.
|
126 | for (int j = 0; j < cols; ++j) { |
| 523 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 87 times.
|
87 | size_t idx = base_idx + static_cast<size_t>(j); |
| 524 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 87 times.
|
87 | if (idx >= flat_data.size()) { |
| 525 | return false; | ||
| 526 | } | ||
| 527 | 87 | matrix[static_cast<size_t>(i)][static_cast<size_t>(j)] = flat_data[idx]; | |
| 528 | } | ||
| 529 | } | ||
| 530 | |||
| 531 | return true; | ||
| 532 | } | ||
| 533 | |||
| 534 | // Разделенная на две функции для уменьшения cognitive complexity | ||
| 535 | 11 | std::vector<std::vector<int>> CreateLocalMatrixA(int local_rows_count, int cols_a_int, | |
| 536 | const std::vector<int> &local_rows_flat) { | ||
| 537 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if (cols_a_int <= 0 || local_rows_count <= 0) { |
| 538 | ✗ | return {}; | |
| 539 | } | ||
| 540 | |||
| 541 | // Проверка на переполнение | ||
| 542 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if (cols_a_int > 0 && local_rows_count > INT_MAX / cols_a_int) { |
| 543 | ✗ | return {}; | |
| 544 | } | ||
| 545 | |||
| 546 | // Проверка на переполнение size_t | ||
| 547 | 11 | if (static_cast<size_t>(local_rows_count) > SIZE_MAX / static_cast<size_t>(cols_a_int)) { | |
| 548 | return {}; | ||
| 549 | } | ||
| 550 | |||
| 551 | 11 | std::vector<std::vector<int>> local_a; | |
| 552 | try { | ||
| 553 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | local_a.resize(static_cast<size_t>(local_rows_count)); |
| 554 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 11 times.
|
23 | for (auto &row : local_a) { |
| 555 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | row.resize(static_cast<size_t>(cols_a_int), 0); |
| 556 | } | ||
| 557 | ✗ | } catch (const std::bad_alloc &) { | |
| 558 | ✗ | return {}; | |
| 559 | ✗ | } | |
| 560 | |||
| 561 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
|
11 | if (!FillMatrixFromFlat(local_a, local_rows_count, cols_a_int, local_rows_flat, 0)) { |
| 562 | ✗ | return {}; | |
| 563 | } | ||
| 564 | |||
| 565 | return local_a; | ||
| 566 | 11 | } | |
| 567 | |||
| 568 | 11 | std::vector<std::vector<int>> CreateLocalMatrixB(int cols_a_int, int cols_b_int, | |
| 569 | const std::vector<int> &matrix_b_flat) { | ||
| 570 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if (cols_a_int <= 0 || cols_b_int <= 0) { |
| 571 | ✗ | return {}; | |
| 572 | } | ||
| 573 | |||
| 574 | // Проверка на переполнение | ||
| 575 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if (cols_b_int > 0 && cols_a_int > INT_MAX / cols_b_int) { |
| 576 | ✗ | return {}; | |
| 577 | } | ||
| 578 | |||
| 579 | // Проверка на переполнение size_t | ||
| 580 | 11 | if (static_cast<size_t>(cols_a_int) > SIZE_MAX / static_cast<size_t>(cols_b_int)) { | |
| 581 | return {}; | ||
| 582 | } | ||
| 583 | |||
| 584 | 11 | std::vector<std::vector<int>> local_b; | |
| 585 | try { | ||
| 586 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | local_b.resize(static_cast<size_t>(cols_a_int)); |
| 587 |
2/2✓ Branch 0 taken 27 times.
✓ Branch 1 taken 11 times.
|
38 | for (auto &row : local_b) { |
| 588 |
1/2✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
|
27 | row.resize(static_cast<size_t>(cols_b_int), 0); |
| 589 | } | ||
| 590 | ✗ | } catch (const std::bad_alloc &) { | |
| 591 | ✗ | return {}; | |
| 592 | ✗ | } | |
| 593 | |||
| 594 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
|
11 | if (!FillMatrixFromFlat(local_b, cols_a_int, cols_b_int, matrix_b_flat, 0)) { |
| 595 | ✗ | return {}; | |
| 596 | } | ||
| 597 | |||
| 598 | return local_b; | ||
| 599 | 11 | } | |
| 600 | |||
| 601 | // Основная функция теперь просто объединяет результаты | ||
| 602 | 11 | std::pair<std::vector<std::vector<int>>, std::vector<std::vector<int>>> CreateLocalMatrices( | |
| 603 | int local_rows_count, int cols_a_int, int cols_b_int, const std::vector<int> &local_rows_flat, | ||
| 604 | const std::vector<int> &matrix_b_flat) { | ||
| 605 | 11 | auto local_a = CreateLocalMatrixA(local_rows_count, cols_a_int, local_rows_flat); | |
| 606 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if (local_a.empty()) { |
| 607 | ✗ | return {{}, {}}; | |
| 608 | } | ||
| 609 | |||
| 610 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | auto local_b = CreateLocalMatrixB(cols_a_int, cols_b_int, matrix_b_flat); |
| 611 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if (local_b.empty()) { |
| 612 | ✗ | return {{}, {}}; | |
| 613 | } | ||
| 614 | |||
| 615 | return {std::move(local_a), std::move(local_b)}; | ||
| 616 | 11 | } | |
| 617 | |||
| 618 | 11 | std::vector<int> ComputeLocalResult(int local_rows_count, int cols_a_int, int cols_b_int, | |
| 619 | const std::vector<std::vector<int>> &local_a, | ||
| 620 | const std::vector<std::vector<int>> &local_b) { | ||
| 621 | 11 | std::vector<int> local_result_flat; | |
| 622 | |||
| 623 | // Проверка на переполнение | ||
| 624 |
2/4✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
|
11 | if (local_rows_count > 0 && cols_b_int > 0 && local_rows_count > INT_MAX / cols_b_int) { |
| 625 | ✗ | return {}; | |
| 626 | } | ||
| 627 | |||
| 628 | // Проверка на переполнение size_t | ||
| 629 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if (static_cast<size_t>(local_rows_count) > SIZE_MAX / static_cast<size_t>(cols_b_int)) { |
| 630 | ✗ | return {}; | |
| 631 | } | ||
| 632 | |||
| 633 | 11 | size_t local_result_size = static_cast<size_t>(local_rows_count) * static_cast<size_t>(cols_b_int); | |
| 634 | |||
| 635 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if (local_result_size == 0 || local_result_size > kMaxVectorSize) { |
| 636 | ✗ | return {}; | |
| 637 | } | ||
| 638 | |||
| 639 |
2/4✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
|
11 | if (!SafeVectorResize(local_result_flat, local_result_size, 0)) { |
| 640 | ✗ | return {}; | |
| 641 | } | ||
| 642 | |||
| 643 | 11 | ComputeLocalMultiplication(local_rows_count, cols_a_int, cols_b_int, local_a, local_b, local_result_flat); | |
| 644 | return local_result_flat; | ||
| 645 | } | ||
| 646 | |||
| 647 | } // namespace | ||
| 648 | |||
| 649 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | LeonovaAStarMPI::LeonovaAStarMPI(const InType &in) { |
| 650 | SetTypeOfTask(GetStaticTypeOfTask()); | ||
| 651 | GetInput() = in; | ||
| 652 | 12 | } | |
| 653 | |||
| 654 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | bool LeonovaAStarMPI::ValidateMatricesOnMaster() { |
| 655 | const auto &matrix_a = std::get<0>(GetInput()); | ||
| 656 | const auto &matrix_b = std::get<1>(GetInput()); | ||
| 657 | |||
| 658 | if (!CheckMatricesNotEmpty(matrix_a, matrix_b)) { | ||
| 659 | return false; | ||
| 660 | } | ||
| 661 | |||
| 662 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
|
12 | if (matrix_a.size() > kMaxMatrixSize || matrix_b.size() > kMaxMatrixSize) { |
| 663 | return false; | ||
| 664 | } | ||
| 665 | |||
| 666 | size_t rows_a = matrix_a.size(); | ||
| 667 | size_t cols_a = matrix_a[0].size(); | ||
| 668 | |||
| 669 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 12 times.
|
24 | for (size_t index = 1; index < rows_a; ++index) { |
| 670 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (index >= matrix_a.size() || matrix_a[index].size() != cols_a) { |
| 671 | return false; | ||
| 672 | } | ||
| 673 | } | ||
| 674 | |||
| 675 | size_t rows_b = matrix_b.size(); | ||
| 676 | size_t cols_b = matrix_b[0].size(); | ||
| 677 | |||
| 678 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 12 times.
|
30 | for (size_t index = 1; index < rows_b; ++index) { |
| 679 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
|
18 | if (index >= matrix_b.size() || matrix_b[index].size() != cols_b) { |
| 680 | return false; | ||
| 681 | } | ||
| 682 | } | ||
| 683 | |||
| 684 | 12 | return cols_a == rows_b; | |
| 685 | } | ||
| 686 | |||
| 687 | 24 | bool LeonovaAStarMPI::ValidationImpl() { | |
| 688 | 24 | int rank = 0; | |
| 689 | 24 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); | |
| 690 | |||
| 691 | bool is_valid_local = true; | ||
| 692 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 12 times.
|
24 | if (rank == 0) { |
| 693 | 12 | is_valid_local = ValidateMatricesOnMaster(); | |
| 694 | } | ||
| 695 | |||
| 696 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | int is_valid_int = is_valid_local ? 1 : 0; |
| 697 | 24 | MPI_Bcast(&is_valid_int, 1, MPI_INT, 0, MPI_COMM_WORLD); | |
| 698 | 24 | return (is_valid_int == 1); | |
| 699 | } | ||
| 700 | |||
| 701 | 12 | bool LeonovaAStarMPI::PreProcessingImpl() { | |
| 702 | 12 | return true; | |
| 703 | } | ||
| 704 | |||
| 705 | 12 | std::vector<std::vector<int>> LeonovaAStarMPI::MultiplyMatricesMpi(const std::vector<std::vector<int>> &matrix_a, | |
| 706 | const std::vector<std::vector<int>> &matrix_b) { | ||
| 707 | 12 | int rank = 0; | |
| 708 | 12 | int size = 0; | |
| 709 | 12 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); | |
| 710 | 12 | MPI_Comm_size(MPI_COMM_WORLD, &size); | |
| 711 | |||
| 712 | 12 | auto [valid, dims] = ValidateAndGetDimensions(rank, size, matrix_a, matrix_b); | |
| 713 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (!valid) { |
| 714 | ✗ | return {}; | |
| 715 | } | ||
| 716 | |||
| 717 | 12 | int rows_a_int = dims[0]; | |
| 718 | 12 | int cols_a_int = dims[1]; | |
| 719 | 12 | int cols_b_int = dims[2]; | |
| 720 | |||
| 721 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (!::leonova_a_star::ValidateDimensions(rows_a_int, cols_a_int, cols_b_int)) { |
| 722 | ✗ | return {}; | |
| 723 | } | ||
| 724 | |||
| 725 | 12 | std::vector<int> rows_per_rank; | |
| 726 | 12 | std::vector<int> displacements; | |
| 727 | 12 | std::vector<int> elements_per_rank; | |
| 728 |
2/4✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
|
12 | if (!PrepareRowDistribution(size, rows_a_int, cols_a_int, rows_per_rank, displacements, elements_per_rank)) { |
| 729 | ✗ | return {}; | |
| 730 | } | ||
| 731 | |||
| 732 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (rows_per_rank.empty()) { |
| 733 | ✗ | return {}; | |
| 734 | } | ||
| 735 | |||
| 736 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | int local_rows_count = rows_per_rank[static_cast<size_t>(rank)]; |
| 737 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (local_rows_count < 0) { |
| 738 | ✗ | return {}; | |
| 739 | } | ||
| 740 | |||
| 741 | 12 | std::vector<int> matrix_b_flat; | |
| 742 | 12 | std::vector<int> local_rows_flat; | |
| 743 |
3/4✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 11 times.
|
12 | if (!PrepareLocalData(rank, cols_a_int, cols_b_int, matrix_b, matrix_b_flat, elements_per_rank, local_rows_flat)) { |
| 744 | 1 | return {}; | |
| 745 | } | ||
| 746 | |||
| 747 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | ScatterMatrixA(rank, cols_a_int, matrix_a, displacements, elements_per_rank, local_rows_flat); |
| 748 | auto [local_a, local_b] = | ||
| 749 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | CreateLocalMatrices(local_rows_count, cols_a_int, cols_b_int, local_rows_flat, matrix_b_flat); |
| 750 |
2/4✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
|
11 | if (local_a.empty() || local_b.empty()) { |
| 751 | ✗ | return {}; | |
| 752 | } | ||
| 753 | |||
| 754 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | std::vector<int> local_result_flat = ComputeLocalResult(local_rows_count, cols_a_int, cols_b_int, local_a, local_b); |
| 755 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if (local_result_flat.empty()) { |
| 756 | ✗ | return {}; | |
| 757 | } | ||
| 758 | |||
| 759 | 11 | std::vector<int> full_result_flat; | |
| 760 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 5 times.
|
11 | if (rank == 0) { |
| 761 | // Проверка на переполнение size_t | ||
| 762 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (static_cast<size_t>(rows_a_int) > SIZE_MAX / static_cast<size_t>(cols_b_int)) { |
| 763 | ✗ | return {}; | |
| 764 | } | ||
| 765 | |||
| 766 | 6 | size_t full_result_size = static_cast<size_t>(rows_a_int) * static_cast<size_t>(cols_b_int); | |
| 767 |
2/4✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
|
6 | if (!SafeVectorResize(full_result_flat, full_result_size)) { |
| 768 | ✗ | return {}; | |
| 769 | } | ||
| 770 | } | ||
| 771 | |||
| 772 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | GatherResults(local_rows_count, cols_b_int, rows_per_rank, local_result_flat, full_result_flat); |
| 773 | |||
| 774 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 5 times.
|
11 | if (rank == 0) { |
| 775 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | return ConvertFlatToMatrix(full_result_flat, rows_a_int, cols_b_int); |
| 776 | } | ||
| 777 | |||
| 778 | 5 | return {}; | |
| 779 | } | ||
| 780 | |||
| 781 | 12 | bool LeonovaAStarMPI::ResizeOutputMatrix(int rows, int cols) { | |
| 782 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (rows < 0 || cols < 0) { |
| 783 | GetOutput().clear(); | ||
| 784 | ✗ | return false; | |
| 785 | } | ||
| 786 | |||
| 787 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | if (std::cmp_greater(static_cast<size_t>(rows), kMaxMatrixSize) || |
| 788 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | std::cmp_greater(static_cast<size_t>(cols), kMaxMatrixSize)) { |
| 789 | GetOutput().clear(); | ||
| 790 | ✗ | return false; | |
| 791 | } | ||
| 792 | |||
| 793 | try { | ||
| 794 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | GetOutput().resize(static_cast<size_t>(rows)); |
| 795 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 12 times.
|
36 | for (auto &row : GetOutput()) { |
| 796 |
1/2✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
|
24 | row.resize(static_cast<size_t>(cols), 0); |
| 797 | } | ||
| 798 | return true; | ||
| 799 | ✗ | } catch (const std::bad_alloc &) { | |
| 800 | GetOutput().clear(); | ||
| 801 | return false; | ||
| 802 | ✗ | } | |
| 803 | } | ||
| 804 | |||
| 805 | 12 | std::pair<int, int> LeonovaAStarMPI::GetResultDimensions(int rank) { | |
| 806 | 12 | int rows = 0; | |
| 807 | 12 | int cols = 0; | |
| 808 | |||
| 809 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
|
12 | if (rank == 0) { |
| 810 | 6 | rows = static_cast<int>(GetOutput().size()); | |
| 811 |
2/4✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
|
6 | if (rows > 0 && !GetOutput().empty()) { |
| 812 | 6 | cols = static_cast<int>(GetOutput()[0].size()); | |
| 813 | } | ||
| 814 | } | ||
| 815 | |||
| 816 | 12 | MPI_Bcast(&rows, 1, MPI_INT, 0, MPI_COMM_WORLD); | |
| 817 | 12 | MPI_Bcast(&cols, 1, MPI_INT, 0, MPI_COMM_WORLD); | |
| 818 | |||
| 819 | 12 | return {rows, cols}; | |
| 820 | } | ||
| 821 | |||
| 822 | ✗ | bool LeonovaAStarMPI::ValidateDimensions(int rows, int cols) { | |
| 823 | 12 | return rows >= 0 && cols >= 0; | |
| 824 | } | ||
| 825 | |||
| 826 | 6 | void LeonovaAStarMPI::BroadcastFromMaster(int rows, int cols) { | |
| 827 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
|
18 | for (int i = 0; i < rows; ++i) { |
| 828 |
3/6✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
|
12 | if (static_cast<size_t>(i) < GetOutput().size() && !GetOutput()[static_cast<size_t>(i)].empty() && cols > 0) { |
| 829 | 12 | MPI_Bcast(GetOutput()[static_cast<size_t>(i)].data(), cols, MPI_INT, 0, MPI_COMM_WORLD); | |
| 830 | } | ||
| 831 | } | ||
| 832 | 6 | } | |
| 833 | |||
| 834 | 6 | void LeonovaAStarMPI::ReceiveFromMaster(int rows, int cols) { | |
| 835 | 6 | GetOutput().resize(static_cast<size_t>(rows)); | |
| 836 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
|
18 | for (auto &row : GetOutput()) { |
| 837 | 12 | row.resize(static_cast<size_t>(cols)); | |
| 838 | } | ||
| 839 | |||
| 840 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
|
18 | for (int i = 0; i < rows; ++i) { |
| 841 |
2/4✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
|
12 | if (static_cast<size_t>(i) < GetOutput().size() && cols > 0) { |
| 842 | 12 | MPI_Bcast(GetOutput()[static_cast<size_t>(i)].data(), cols, MPI_INT, 0, MPI_COMM_WORLD); | |
| 843 | } | ||
| 844 | } | ||
| 845 | 6 | } | |
| 846 | |||
| 847 | 12 | void LeonovaAStarMPI::BroadcastResult(int rank) { | |
| 848 | 12 | auto [result_rows, result_cols] = GetResultDimensions(rank); | |
| 849 | |||
| 850 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (!ValidateDimensions(result_rows, result_cols)) { |
| 851 | GetOutput().clear(); | ||
| 852 | ✗ | return; | |
| 853 | } | ||
| 854 | |||
| 855 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | if (!ResizeOutputMatrix(result_rows, result_cols)) { |
| 856 | return; | ||
| 857 | } | ||
| 858 | |||
| 859 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
|
12 | if (rank == 0) { |
| 860 | 6 | BroadcastFromMaster(result_rows, result_cols); | |
| 861 | } else { | ||
| 862 | 6 | ReceiveFromMaster(result_rows, result_cols); | |
| 863 | } | ||
| 864 | } | ||
| 865 | |||
| 866 | 12 | bool LeonovaAStarMPI::RunImpl() { | |
| 867 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | if (!ValidationImpl()) { |
| 868 | return false; | ||
| 869 | } | ||
| 870 | |||
| 871 | 12 | int rank = 0; | |
| 872 | 12 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); | |
| 873 | |||
| 874 | const auto &matrix_a = std::get<0>(GetInput()); | ||
| 875 | const auto &matrix_b = std::get<1>(GetInput()); | ||
| 876 | |||
| 877 | 12 | std::vector<std::vector<int>> result = MultiplyMatricesMpi(matrix_a, matrix_b); | |
| 878 | |||
| 879 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
|
12 | if (rank == 0) { |
| 880 | 6 | GetOutput() = std::move(result); | |
| 881 | } else { | ||
| 882 | GetOutput().clear(); | ||
| 883 | } | ||
| 884 | |||
| 885 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | BroadcastResult(rank); |
| 886 | return true; | ||
| 887 | 12 | } | |
| 888 | |||
| 889 | 12 | bool LeonovaAStarMPI::PostProcessingImpl() { | |
| 890 | 12 | return true; | |
| 891 | } | ||
| 892 | |||
| 893 | } // namespace leonova_a_star | ||
| 894 |