GCC Code Coverage Report


Directory: ./
File: tasks/trofimov_n_max_val_matrix/mpi/src/ops_mpi.cpp
Date: 2026-01-27 01:59:34
Exec Total Coverage
Lines: 0 105 0.0%
Functions: 0 11 0.0%
Branches: 0 106 0.0%

Line Branch Exec Source
1 #include "trofimov_n_max_val_matrix/mpi/include/ops_mpi.hpp"
2
3 #include <mpi.h>
4
5 #include <algorithm>
6 #include <array>
7 #include <cstddef>
8 #include <tuple>
9 #include <utility>
10 #include <vector>
11
12 #include "trofimov_n_max_val_matrix/common/include/common.hpp"
13
14 namespace trofimov_n_max_val_matrix {
15
16 namespace {
17 constexpr int kRootRank = 0;
18 } // namespace
19
20 TrofimovNMaxValMatrixMPI::TrofimovNMaxValMatrixMPI(const InType &in) {
21 SetTypeOfTask(GetStaticTypeOfTask());
22 GetInput() = InType(in.begin(), in.end());
23 GetOutput() = OutType();
24 }
25
26 bool TrofimovNMaxValMatrixMPI::ValidationImpl() {
27 if (GetInput().empty()) {
28 return false;
29 }
30
31 const std::size_t cols = GetInput()[0].size();
32 for (const auto &row : GetInput()) {
33 if (row.size() != cols) {
34 return false;
35 }
36 }
37
38 return GetOutput().empty();
39 }
40
41 bool TrofimovNMaxValMatrixMPI::PreProcessingImpl() {
42 int rank = 0;
43 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
44
45 if (rank == kRootRank) {
46 GetOutput() = std::vector<int>(GetInput().size(), 0);
47 } else {
48 GetOutput().clear();
49 }
50
51 return true;
52 }
53
54 namespace {
55
56 std::tuple<int, int> CalculateLocalRows(int rank, int size, int total_rows) {
57 const int rows_per_process = total_rows / size;
58 const int remainder = total_rows % size;
59
60 const int start_row = (rank * rows_per_process) + std::min(rank, remainder);
61 const int local_rows = rows_per_process + (rank < remainder ? 1 : 0);
62
63 return {start_row, local_rows};
64 }
65
66 std::vector<int> CalculateLocalMaxima(const std::vector<std::vector<int>> &local_input) {
67 std::vector<int> local_maxima;
68 local_maxima.reserve(local_input.size());
69
70 for (const auto &row : local_input) {
71 if (!row.empty()) {
72 local_maxima.push_back(*std::ranges::max_element(row));
73 }
74 }
75
76 return local_maxima;
77 }
78
79 void HandleEmptyCase(int rank, int total_rows) {
80 if (rank == kRootRank) {
81 std::vector<int> recv_counts(1, 0);
82 std::vector<int> displacements(1, 0);
83 MPI_Gather(&total_rows, 1, MPI_INT, recv_counts.data(), 1, MPI_INT, kRootRank, MPI_COMM_WORLD);
84 } else {
85 int zero = 0;
86 MPI_Gather(&zero, 1, MPI_INT, nullptr, 0, MPI_INT, kRootRank, MPI_COMM_WORLD);
87 }
88 }
89
90 void GatherResults(int rank, int size, int local_rows, const std::vector<int> &local_maxima, std::vector<int> &output,
91 int total_rows) {
92 std::vector<int> recv_counts(static_cast<std::size_t>(size), 0);
93 std::vector<int> displacements(static_cast<std::size_t>(size), 0);
94
95 MPI_Gather(&local_rows, 1, MPI_INT, recv_counts.data(), 1, MPI_INT, kRootRank, MPI_COMM_WORLD);
96
97 if (rank == kRootRank) {
98 displacements[0] = 0;
99 for (int i = 1; i < size; ++i) {
100 displacements[static_cast<std::size_t>(i)] =
101 displacements[static_cast<std::size_t>(i - 1)] + recv_counts[static_cast<std::size_t>(i - 1)];
102 }
103
104 if (output.size() != static_cast<std::size_t>(total_rows)) {
105 output.resize(static_cast<std::size_t>(total_rows));
106 }
107 }
108
109 MPI_Gatherv(local_maxima.data(), local_rows, MPI_INT, rank == kRootRank ? output.data() : nullptr, recv_counts.data(),
110 displacements.data(), MPI_INT, kRootRank, MPI_COMM_WORLD);
111 }
112
113 bool SendRowsToProcess(int dest, int total_rows, int total_cols, int size,
114 const std::vector<std::vector<int>> &original_input) {
115 auto [dest_start_row, dest_local_rows] = CalculateLocalRows(dest, size, total_rows);
116
117 if (dest_local_rows <= 0) {
118 return false;
119 }
120
121 std::array<int, 2> dest_info = {dest_local_rows, total_cols};
122 MPI_Send(dest_info.data(), 2, MPI_INT, dest, 0, MPI_COMM_WORLD);
123
124 for (int i = 0; i < dest_local_rows; ++i) {
125 int global_row = dest_start_row + i;
126 MPI_Send(original_input[static_cast<std::size_t>(global_row)].data(), total_cols, MPI_INT, dest, i + 1,
127 MPI_COMM_WORLD);
128 }
129
130 return true;
131 }
132
133 void ProcessRootData(int local_rows, int start_row, const std::vector<std::vector<int>> &original_input,
134 std::vector<std::vector<int>> &local_input) {
135 local_input.reserve(static_cast<std::size_t>(local_rows));
136 for (int i = 0; i < local_rows; ++i) {
137 int global_row = start_row + i;
138 local_input.push_back(original_input[static_cast<std::size_t>(global_row)]);
139 }
140 }
141
142 bool ReceiveRowsFromRoot(std::vector<std::vector<int>> &local_input) {
143 std::array<int, 2> recv_info = {0, 0};
144 MPI_Recv(recv_info.data(), 2, MPI_INT, kRootRank, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
145
146 int recv_rows = recv_info[0];
147 int recv_cols = recv_info[1];
148
149 if (recv_rows <= 0 || recv_cols <= 0) {
150 return false;
151 }
152
153 local_input.reserve(static_cast<std::size_t>(recv_rows));
154 for (int i = 0; i < recv_rows; ++i) {
155 std::vector<int> row(static_cast<std::size_t>(recv_cols));
156 MPI_Recv(row.data(), recv_cols, MPI_INT, kRootRank, i + 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
157 local_input.push_back(std::move(row));
158 }
159
160 return true;
161 }
162
163 } // namespace
164
165 bool TrofimovNMaxValMatrixMPI::RunImpl() {
166 int rank = 0;
167 int size = 0;
168 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
169 MPI_Comm_size(MPI_COMM_WORLD, &size);
170
171 int total_rows = 0;
172 int total_cols = 0;
173
174 if (rank == kRootRank) {
175 const auto &input = GetInput();
176 total_rows = static_cast<int>(input.size());
177 if (total_rows > 0) {
178 total_cols = static_cast<int>(input[0].size());
179 }
180 }
181
182 MPI_Bcast(&total_rows, 1, MPI_INT, kRootRank, MPI_COMM_WORLD);
183 MPI_Bcast(&total_cols, 1, MPI_INT, kRootRank, MPI_COMM_WORLD);
184
185 const bool invalid_matrix = total_rows <= 0 || total_cols <= 0;
186 if (invalid_matrix) {
187 HandleEmptyCase(rank, total_rows);
188 if (rank == kRootRank) {
189 GetOutput().clear();
190 }
191 return true;
192 }
193
194 auto [start_row, local_rows] = CalculateLocalRows(rank, size, total_rows);
195
196 if (local_rows <= 0) {
197 HandleEmptyCase(rank, total_rows);
198 return true;
199 }
200
201 std::vector<std::vector<int>> local_input;
202
203 if (rank == kRootRank) {
204 const auto &original_input = GetInput();
205
206 for (int dest = 1; dest < size; ++dest) {
207 SendRowsToProcess(dest, total_rows, total_cols, size, original_input);
208 }
209
210 ProcessRootData(local_rows, start_row, original_input, local_input);
211 } else {
212 const bool received = ReceiveRowsFromRoot(local_input);
213 if (!received) {
214 HandleEmptyCase(rank, total_rows);
215 return true;
216 }
217 }
218
219 if (local_input.empty()) {
220 HandleEmptyCase(rank, total_rows);
221 return true;
222 }
223
224 auto local_maxima = CalculateLocalMaxima(local_input);
225 GatherResults(rank, size, static_cast<int>(local_input.size()), local_maxima, GetOutput(), total_rows);
226
227 return true;
228 }
229
230 bool TrofimovNMaxValMatrixMPI::PostProcessingImpl() {
231 return true;
232 }
233
234 } // namespace trofimov_n_max_val_matrix
235