GCC Code Coverage Report


Directory: ./
File: tasks/artyushkina_vector/mpi/src/ops_mpi.cpp
Date: 2026-01-27 01:59:34
Exec Total Coverage
Lines: 164 185 88.6%
Functions: 17 17 100.0%
Branches: 96 142 67.6%

Line Branch Exec Source
1 #include "artyushkina_vector/mpi/include/ops_mpi.hpp"
2
3 #include <mpi.h>
4
5 #include <algorithm>
6 #include <cstddef>
7 #include <utility>
8 #include <vector>
9
10 #include "artyushkina_vector/common/include/common.hpp"
11
12 #ifdef __GNUC__
13 # pragma GCC diagnostic push
14 # pragma GCC diagnostic ignored "-Wnull-dereference"
15 #endif
16
17 namespace artyushkina_vector {
18
19
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 VerticalStripMatVecMPI::VerticalStripMatVecMPI(const InType &in) {
20 SetTypeOfTask(GetStaticTypeOfTask());
21 GetInput() = in;
22 14 GetOutput() = Vector{};
23 14 }
24
25 14 bool VerticalStripMatVecMPI::ValidationImpl() {
26 14 int rank = 0;
27 14 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
28
29
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
14 if (rank != 0) {
30 return true;
31 }
32
33 const auto &[matrix, vector] = GetInput();
34
35
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (matrix.empty()) {
36 return vector.empty();
37 }
38
39
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (vector.empty()) {
40 return false;
41 }
42
43 size_t cols = matrix[0].size();
44
45
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 7 times.
15 for (size_t i = 1; i < matrix.size(); ++i) {
46
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (matrix[i].size() != cols) {
47 return false;
48 }
49 }
50
51 7 return vector.size() == cols;
52 }
53
54 14 bool VerticalStripMatVecMPI::PreProcessingImpl() {
55 14 return true;
56 }
57
58 namespace {
59
60 constexpr int kTagVector = 101;
61 constexpr int kTagResult = 102;
62 constexpr int kTagBroadcast = 103;
63 constexpr int kTagSimple = 100;
64 constexpr int kTagEmpty = 104;
65
66 14 void BroadcastDimensions(int &rows, int &cols) {
67 14 MPI_Bcast(&rows, 1, MPI_INT, 0, MPI_COMM_WORLD);
68 14 MPI_Bcast(&cols, 1, MPI_INT, 0, MPI_COMM_WORLD);
69 14 }
70
71 std::pair<int, int> GetProcessParams(int proc, int base, int rem) {
72 24 int proc_start = proc * base;
73
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 8 times.
12 if (proc < rem) {
74 8 proc_start += proc;
75 } else {
76 16 proc_start += rem;
77 }
78
79 int proc_width = base;
80
4/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 8 times.
24 if (proc < rem) {
81 8 proc_width += 1;
82 }
83
84 return {proc_start, proc_width};
85 }
86
87 6 void PrepareAndSendVectorParts(int world_size, const Vector &vector, int base, int rem,
88 std::vector<double> &local_vector) {
89
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
18 for (int proc = 0; proc < world_size; ++proc) {
90 auto [proc_start, proc_width] = GetProcessParams(proc, base, rem);
91
92
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (proc_width <= 0) {
93 int empty_signal = -1;
94 if (proc != 0) {
95 MPI_Send(&empty_signal, 1, MPI_INT, proc, kTagEmpty, MPI_COMM_WORLD);
96 }
97 continue;
98 }
99
100 12 std::vector<double> sendbuf(static_cast<size_t>(proc_width));
101
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 12 times.
30 for (int j = 0; j < proc_width; ++j) {
102 18 auto j_idx = static_cast<size_t>(j);
103 18 auto src_idx = static_cast<size_t>(proc_start) + j_idx;
104 18 sendbuf[j_idx] = vector[src_idx];
105 }
106
107
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if (proc == 0) {
108 local_vector = std::move(sendbuf);
109 } else {
110
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 MPI_Send(sendbuf.data(), proc_width, MPI_DOUBLE, proc, kTagVector, MPI_COMM_WORLD);
111 }
112 }
113 6 }
114
115 6 void ReceiveVectorPart(int my_width, std::vector<double> &local_vector) {
116
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (my_width > 0) {
117 6 local_vector.resize(static_cast<size_t>(my_width));
118 6 MPI_Recv(local_vector.data(), my_width, MPI_DOUBLE, 0, kTagVector, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
119 } else {
120 int empty_signal = 0;
121 MPI_Recv(&empty_signal, 1, MPI_INT, 0, kTagEmpty, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
122 }
123 6 }
124
125 void DistributeVectorStripes(int world_size, const Vector &vector, int base, int rem, std::vector<double> &local_vector,
126 int rank, int my_width) {
127
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if (rank == 0) {
128
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 PrepareAndSendVectorParts(world_size, vector, base, rem, local_vector);
129 } else {
130
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 ReceiveVectorPart(my_width, local_vector);
131 }
132 }
133
134 12 void MultiplyStrip(const std::vector<double> &matrix_flat, const std::vector<double> &local_vector,
135 std::vector<double> &local_result, int rows, int cols, int my_width, int my_start) {
136
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 12 times.
40 for (int i = 0; i < rows; ++i) {
137 28 auto i_idx = static_cast<size_t>(i);
138
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 28 times.
66 for (int j = 0; j < my_width; ++j) {
139 38 auto j_idx = static_cast<size_t>(j);
140 38 auto global_j = my_start + j;
141 38 auto global_j_idx = static_cast<size_t>(global_j);
142 38 auto matrix_idx = static_cast<size_t>(i * cols) + global_j_idx;
143 38 local_result[i_idx] += matrix_flat[matrix_idx] * local_vector[j_idx];
144 }
145 }
146 12 }
147
148 6 void GatherResultsInRoot(int world_size, int rows, const std::vector<double> &local_result,
149 std::vector<double> &final_result) {
150
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (rows > 0) {
151 std::ranges::copy(local_result, final_result.begin());
152 }
153
154
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 for (int proc = 1; proc < world_size; ++proc) {
155
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (rows > 0) {
156
1/2
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 std::vector<double> recv_buf(static_cast<size_t>(rows));
157
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 MPI_Recv(recv_buf.data(), rows, MPI_DOUBLE, proc, kTagResult, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
158
159
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 6 times.
20 for (int i = 0; i < rows; ++i) {
160 14 auto i_idx = static_cast<size_t>(i);
161 14 final_result[i_idx] += recv_buf[i_idx];
162 }
163 } else {
164 int dummy = 0;
165 MPI_Recv(&dummy, 1, MPI_INT, proc, kTagResult, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
166 }
167 }
168 6 }
169
170 12 void BroadcastFinalResult(int rank, int world_size, const std::vector<double> &final_result,
171 std::vector<double> &local_final_result) {
172
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if (rank == 0) {
173
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 for (int proc = 1; proc < world_size; ++proc) {
174
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (!final_result.empty()) {
175 6 MPI_Send(final_result.data(), static_cast<int>(final_result.size()), MPI_DOUBLE, proc, kTagBroadcast,
176 MPI_COMM_WORLD);
177 } else {
178 int empty_signal = -1;
179 MPI_Send(&empty_signal, 1, MPI_INT, proc, kTagEmpty, MPI_COMM_WORLD);
180 }
181 }
182 6 local_final_result = final_result;
183 } else {
184 MPI_Status status;
185 6 MPI_Probe(0, kTagBroadcast, MPI_COMM_WORLD, &status);
186
187 6 int count = 0;
188 6 MPI_Get_count(&status, MPI_DOUBLE, &count);
189
190
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (count > 0) {
191 6 local_final_result.resize(static_cast<size_t>(count));
192 6 MPI_Recv(local_final_result.data(), count, MPI_DOUBLE, 0, kTagBroadcast, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
193 } else {
194 int empty_signal = 0;
195 MPI_Recv(&empty_signal, 1, MPI_INT, 0, kTagEmpty, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
196 local_final_result.clear();
197 }
198 }
199 12 }
200
201 2 bool HandleWorldSizeGreaterThanCols(int world_size, int rank, int rows, int cols, std::vector<double> &result,
202 const InType &input) {
203
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (rows <= 0) {
204 result.clear();
205 MPI_Barrier(MPI_COMM_WORLD);
206 return true;
207 }
208
209 2 result.resize(static_cast<size_t>(rows), 0.0);
210
211
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (rank == 0) {
212 const auto &[matrix, vector] = input;
213
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 for (int i = 0; i < rows; ++i) {
214 1 auto i_idx = static_cast<size_t>(i);
215
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 for (int j = 0; j < cols; ++j) {
216 1 auto j_idx = static_cast<size_t>(j);
217 1 result[i_idx] += matrix[i_idx][j_idx] * vector[j_idx];
218 }
219 }
220
221
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 for (int proc = 1; proc < world_size; ++proc) {
222 1 MPI_Send(result.data(), rows, MPI_DOUBLE, proc, kTagSimple, MPI_COMM_WORLD);
223 }
224 } else {
225 1 MPI_Recv(result.data(), rows, MPI_DOUBLE, 0, kTagSimple, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
226 }
227
228 return true;
229 }
230
231 12 void PrepareMatrixFlat(int rank, int rows, int cols, std::vector<double> &matrix_flat, const InType &input) {
232
3/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
12 if (rank == 0 && rows > 0 && cols > 0) {
233 const auto &[matrix, vector] = input;
234 6 size_t total_size = static_cast<size_t>(rows) * static_cast<size_t>(cols);
235 6 matrix_flat.resize(total_size, 0.0);
236
237
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 6 times.
20 for (int i = 0; i < rows; ++i) {
238 14 auto i_idx = static_cast<size_t>(i);
239
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 14 times.
52 for (int j = 0; j < cols; ++j) {
240 38 auto j_idx = static_cast<size_t>(j);
241 38 auto matrix_idx = static_cast<size_t>(i * cols) + j_idx;
242 38 matrix_flat[matrix_idx] = matrix[i_idx][j_idx];
243 }
244 }
245
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 } else if (rows > 0 && cols > 0) {
246 6 size_t total_size = static_cast<size_t>(rows) * static_cast<size_t>(cols);
247 6 matrix_flat.resize(total_size, 0.0);
248 }
249 12 }
250
251 std::pair<int, int> CalculateDimensions(int rank, const InType &input) {
252 int rows = 0;
253 int cols = 0;
254
255 14 if (rank == 0) {
256 const auto &[matrix, vector] = input;
257 7 rows = static_cast<int>(matrix.size());
258
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 if (rows > 0) {
259 7 cols = static_cast<int>(matrix[0].size());
260 }
261 }
262
263 return {rows, cols};
264 }
265
266 14 bool ProcessDimensions(int world_size, int rank, int rows, int cols, const InType &input_data, Vector &result) {
267
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (rows <= 0 || cols <= 0) {
268 result = Vector{};
269 MPI_Barrier(MPI_COMM_WORLD);
270 return true;
271 }
272
273
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 12 times.
14 if (world_size > cols) {
274 2 std::vector<double> temp_result;
275
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 bool success = HandleWorldSizeGreaterThanCols(world_size, rank, rows, cols, temp_result, input_data);
276 if (success) {
277
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (!temp_result.empty()) {
278
1/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
4 result = Vector(temp_result.begin(), temp_result.end());
279 } else {
280 result = Vector{};
281 }
282 }
283 return true;
284 }
285
286 return false;
287 }
288
289 12 bool PrepareLocalData(int rows, int cols, int my_width, std::vector<double> &matrix_flat,
290 std::vector<double> &local_vector, std::vector<double> &local_result,
291 std::vector<double> &final_result) {
292
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (rows > 0 && cols > 0) {
293 12 size_t matrix_size = static_cast<size_t>(rows) * static_cast<size_t>(cols);
294 12 matrix_flat.resize(matrix_size, 0.0);
295 } else {
296 matrix_flat.clear();
297 }
298
299
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (my_width > 0) {
300 12 local_vector.resize(static_cast<size_t>(my_width), 0.0);
301 } else {
302 local_vector.clear();
303 }
304
305
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (rows > 0) {
306 12 local_result.resize(static_cast<size_t>(rows), 0.0);
307 12 final_result.resize(static_cast<size_t>(rows), 0.0);
308 } else {
309 local_result.clear();
310 final_result.clear();
311 }
312
313 12 return true;
314 }
315
316 12 bool PerformLocalComputation(int rank, int world_size, int rows, int cols, int my_start, int my_width,
317 const InType &input_data, std::vector<double> &matrix_flat,
318 std::vector<double> &local_vector, std::vector<double> &local_result) {
319 12 PrepareMatrixFlat(rank, rows, cols, matrix_flat, input_data);
320
321
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (rows > 0 && cols > 0) {
322 12 MPI_Bcast(matrix_flat.data(), rows * cols, MPI_DOUBLE, 0, MPI_COMM_WORLD);
323 }
324
325
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 DistributeVectorStripes(world_size, rank == 0 ? input_data.second : Vector{}, cols / world_size, cols % world_size,
326 local_vector, rank, my_width);
327
328
2/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
12 if (my_width > 0 && rows > 0 && cols > 0) {
329 12 MultiplyStrip(matrix_flat, local_vector, local_result, rows, cols, my_width, my_start);
330 }
331
332 12 return true;
333 }
334
335 12 bool CollectResults(int rank, int world_size, int rows, const std::vector<double> &local_result,
336 std::vector<double> &final_result) {
337
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if (rank == 0) {
338 6 GatherResultsInRoot(world_size, rows, local_result, final_result);
339
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 } else if (rows > 0) {
340 6 MPI_Send(local_result.data(), rows, MPI_DOUBLE, 0, kTagResult, MPI_COMM_WORLD);
341 } else {
342 int dummy = 0;
343 MPI_Send(&dummy, 1, MPI_INT, 0, kTagResult, MPI_COMM_WORLD);
344 }
345
346 12 return true;
347 }
348
349 } // namespace
350
351 14 bool VerticalStripMatVecMPI::RunImpl() {
352 14 int world_size = 0;
353 14 int rank = 0;
354 14 MPI_Comm_size(MPI_COMM_WORLD, &world_size);
355 14 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
356
357 const auto &input_data = GetInput();
358
359
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
14 auto [rows, cols] = CalculateDimensions(rank, input_data);
360 14 BroadcastDimensions(rows, cols);
361
362 14 Vector result;
363
3/4
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 12 times.
14 if (ProcessDimensions(world_size, rank, rows, cols, input_data, result)) {
364
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 GetOutput() = result;
365 return true;
366 }
367
368 12 int base = cols / world_size;
369 12 int rem = cols % world_size;
370
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 8 times.
12 auto [my_start, my_width] = GetProcessParams(rank, base, rem);
371
372 12 std::vector<double> matrix_flat;
373 12 std::vector<double> local_vector;
374 12 std::vector<double> local_result;
375 12 std::vector<double> final_result;
376
377
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 PrepareLocalData(rows, cols, my_width, matrix_flat, local_vector, local_result, final_result);
378
379
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 PerformLocalComputation(rank, world_size, rows, cols, my_start, my_width, input_data, matrix_flat, local_vector,
380 local_result);
381
382
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 CollectResults(rank, world_size, rows, local_result, final_result);
383
384 12 std::vector<double> local_final_result;
385
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (rows > 0) {
386
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 local_final_result.resize(static_cast<size_t>(rows), 0.0);
387 }
388
389
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 BroadcastFinalResult(rank, world_size, final_result, local_final_result);
390
391
2/6
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
24 GetOutput() = Vector(local_final_result.begin(), local_final_result.end());
392
393 return true;
394 }
395
396 14 bool VerticalStripMatVecMPI::PostProcessingImpl() {
397 14 return true;
398 }
399
400 } // namespace artyushkina_vector
401
402 #ifdef __GNUC__
403 # pragma GCC diagnostic pop
404 #endif
405