GCC Code Coverage Report


Directory: ./
File: tasks/sosnina_a_matrix_mult_horizontal/mpi/src/ops_mpi.cpp
Date: 2026-01-27 01:59:34
Exec Total Coverage
Lines: 163 178 91.6%
Functions: 18 19 94.7%
Branches: 87 136 64.0%

Line Branch Exec Source
1 #include "sosnina_a_matrix_mult_horizontal/mpi/include/ops_mpi.hpp"
2
3 #include <mpi.h>
4
5 #include <array>
6 #include <cmath>
7 #include <cstddef>
8 #include <vector>
9
10 #include "sosnina_a_matrix_mult_horizontal/common/include/common.hpp"
11
12 namespace sosnina_a_matrix_mult_horizontal {
13
14 128 SosninaAMatrixMultHorizontalMPI::SosninaAMatrixMultHorizontalMPI(const InType &in) {
15 SetTypeOfTask(GetStaticTypeOfTask());
16 128 GetOutput() = std::vector<std::vector<double>>();
17
18 128 int rank = 0;
19
1/2
✓ Branch 1 taken 128 times.
✗ Branch 2 not taken.
128 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
20
21
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 64 times.
128 if (rank == 0) {
22
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
64 matrix_A_ = in.first;
23
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
64 matrix_B_ = in.second;
24 }
25 128 }
26
27 128 bool SosninaAMatrixMultHorizontalMPI::ValidationImpl() {
28 128 int mpi_initialized = 0;
29 128 MPI_Initialized(&mpi_initialized);
30
31
1/2
✓ Branch 0 taken 128 times.
✗ Branch 1 not taken.
128 if (mpi_initialized == 0) {
32 return false;
33 }
34
35 128 int size = 1;
36 128 MPI_Comm_size(MPI_COMM_WORLD, &size);
37 128 return size >= 1;
38 }
39
40 128 bool SosninaAMatrixMultHorizontalMPI::PreProcessingImpl() {
41 128 int rank = 0;
42 128 int size = 1;
43 128 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
44 128 MPI_Comm_size(MPI_COMM_WORLD, &size);
45
46 128 rank_ = rank;
47 128 world_size_ = size;
48 128 GetOutput() = std::vector<std::vector<double>>();
49
50 128 return true;
51 }
52
53 128 bool SosninaAMatrixMultHorizontalMPI::RunImpl() {
54
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 128 times.
128 if (world_size_ == 1) {
55 return RunSequential();
56 }
57
58 128 int rows_a = 0;
59 128 int cols_a = 0;
60 128 int rows_b = 0;
61 128 int cols_b = 0;
62
63
1/2
✓ Branch 1 taken 128 times.
✗ Branch 2 not taken.
128 if (!PrepareAndValidateSizes(rows_a, cols_a, rows_b, cols_b)) {
64 return true;
65 }
66
67 128 std::vector<double> b_flat(static_cast<size_t>(rows_b) * static_cast<size_t>(cols_b));
68
1/2
✓ Branch 1 taken 128 times.
✗ Branch 2 not taken.
128 PrepareAndBroadcastMatrixB(b_flat, rows_b, cols_b);
69
70 128 std::vector<int> my_row_indices;
71 128 std::vector<double> local_a_flat;
72 128 int local_rows = 0;
73
1/2
✓ Branch 1 taken 128 times.
✗ Branch 2 not taken.
128 DistributeMatrixAData(my_row_indices, local_a_flat, local_rows, rows_a, cols_a);
74
75
1/4
✓ Branch 1 taken 128 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
128 std::vector<double> local_result_flat(static_cast<size_t>(local_rows) * static_cast<size_t>(cols_b), 0.0);
76 128 ComputeLocalMultiplication(local_a_flat, b_flat, local_result_flat, local_rows, cols_a, cols_b);
77
78 128 std::vector<double> final_result_flat;
79
1/2
✓ Branch 1 taken 128 times.
✗ Branch 2 not taken.
128 GatherResults(final_result_flat, my_row_indices, local_result_flat, local_rows, rows_a, cols_b);
80
81
1/2
✓ Branch 1 taken 128 times.
✗ Branch 2 not taken.
128 ConvertToMatrix(final_result_flat, rows_a, cols_b);
82
83 return true;
84 }
85
86 bool SosninaAMatrixMultHorizontalMPI::RunSequential() {
87 if (rank_ != 0) {
88 return true;
89 }
90
91 const auto &matrix_a = matrix_A_;
92 const auto &matrix_b = matrix_B_;
93
94 if (matrix_a.empty() || matrix_b.empty()) {
95 GetOutput() = std::vector<std::vector<double>>();
96 return true;
97 }
98
99 size_t rows_a = matrix_a.size();
100 size_t cols_a = matrix_a[0].size();
101 size_t cols_b = matrix_b[0].size();
102
103 auto &output = GetOutput();
104 output = std::vector<std::vector<double>>(rows_a, std::vector<double>(cols_b, 0.0));
105
106 for (size_t i = 0; i < rows_a; ++i) {
107 for (size_t k = 0; k < cols_a; ++k) {
108 double aik = matrix_a[i][k];
109 for (size_t j = 0; j < cols_b; ++j) {
110 output[i][j] += aik * matrix_b[k][j];
111 }
112 }
113 }
114
115 return true;
116 }
117
118 128 bool SosninaAMatrixMultHorizontalMPI::PrepareAndValidateSizes(int &rows_a, int &cols_a, int &rows_b, int &cols_b) {
119
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 64 times.
128 if (rank_ == 0) {
120 64 rows_a = static_cast<int>(matrix_A_.size());
121
2/4
✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 64 times.
✗ Branch 3 not taken.
64 cols_a = rows_a > 0 ? static_cast<int>(matrix_A_[0].size()) : 0;
122 64 rows_b = static_cast<int>(matrix_B_.size());
123
1/2
✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
64 cols_b = rows_b > 0 ? static_cast<int>(matrix_B_[0].size()) : 0;
124 }
125
126 128 std::array<int, 4> sizes = {rows_a, cols_a, rows_b, cols_b};
127 128 MPI_Bcast(sizes.data(), 4, MPI_INT, 0, MPI_COMM_WORLD);
128
129 128 rows_a = sizes[0];
130 128 cols_a = sizes[1];
131 128 rows_b = sizes[2];
132 128 cols_b = sizes[3];
133
134
4/8
✓ Branch 0 taken 128 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 128 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 128 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 128 times.
128 if (cols_a != rows_b || rows_a == 0 || cols_a == 0 || rows_b == 0 || cols_b == 0) {
135 GetOutput() = std::vector<std::vector<double>>();
136 return false;
137 }
138
139 return true;
140 }
141
142 128 void SosninaAMatrixMultHorizontalMPI::PrepareAndBroadcastMatrixB(std::vector<double> &b_flat, int rows_b, int cols_b) {
143
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 64 times.
128 if (rank_ == 0) {
144
2/2
✓ Branch 0 taken 124 times.
✓ Branch 1 taken 64 times.
188 for (int i = 0; i < rows_b; ++i) {
145
2/2
✓ Branch 0 taken 248 times.
✓ Branch 1 taken 124 times.
372 for (int j = 0; j < cols_b; ++j) {
146 248 b_flat[(static_cast<size_t>(i) * static_cast<size_t>(cols_b)) + static_cast<size_t>(j)] = matrix_B_[i][j];
147 }
148 }
149 }
150
151 128 MPI_Bcast(b_flat.data(), rows_b * cols_b, MPI_DOUBLE, 0, MPI_COMM_WORLD);
152 128 }
153
154 64 void SosninaAMatrixMultHorizontalMPI::FillLocalAFlat(const std::vector<int> &my_row_indices,
155 std::vector<double> &local_a_flat, int cols_a) {
156
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 64 times.
140 for (size_t idx = 0; idx < my_row_indices.size(); ++idx) {
157 76 int global_row = my_row_indices[idx];
158
2/2
✓ Branch 0 taken 148 times.
✓ Branch 1 taken 76 times.
224 for (int j = 0; j < cols_a; ++j) {
159 148 local_a_flat[(idx * static_cast<size_t>(cols_a)) + static_cast<size_t>(j)] = matrix_A_[global_row][j];
160 }
161 }
162 64 }
163
164 64 void SosninaAMatrixMultHorizontalMPI::SendRowsToProcess(int dest, const std::vector<int> &dest_rows, int cols_a) {
165 64 int dest_row_count = static_cast<int>(dest_rows.size());
166 64 MPI_Send(&dest_row_count, 1, MPI_INT, dest, 0, MPI_COMM_WORLD);
167
168
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 16 times.
64 if (dest_row_count > 0) {
169 48 std::vector<int> rows_copy = dest_rows;
170
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 MPI_Send(rows_copy.data(), dest_row_count, MPI_INT, dest, 1, MPI_COMM_WORLD);
171
172
1/4
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
48 std::vector<double> buffer(static_cast<size_t>(dest_row_count) * static_cast<size_t>(cols_a));
173
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 48 times.
96 for (int idx = 0; idx < dest_row_count; ++idx) {
174 48 int global_row = dest_rows[idx];
175
2/2
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 48 times.
148 for (int j = 0; j < cols_a; ++j) {
176 100 buffer[(static_cast<size_t>(idx) * static_cast<size_t>(cols_a)) + static_cast<size_t>(j)] =
177 100 matrix_A_[global_row][j];
178 }
179 }
180
181
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 MPI_Send(buffer.data(), dest_row_count * cols_a, MPI_DOUBLE, dest, 2, MPI_COMM_WORLD);
182 }
183 64 }
184
185 256 std::vector<int> SosninaAMatrixMultHorizontalMPI::GetRowsForProcess(int process_rank, int rows_a) const {
186 256 std::vector<int> rows;
187
2/2
✓ Branch 0 taken 496 times.
✓ Branch 1 taken 256 times.
752 for (int i = 0; i < rows_a; ++i) {
188
2/2
✓ Branch 0 taken 220 times.
✓ Branch 1 taken 276 times.
496 if (i % world_size_ == process_rank) {
189 rows.push_back(i);
190 }
191 }
192 256 return rows;
193 }
194
195 64 void SosninaAMatrixMultHorizontalMPI::ReceiveRowsFromRoot(int &local_rows, std::vector<int> &my_row_indices,
196 std::vector<double> &local_a_flat, int cols_a) {
197 64 MPI_Recv(&local_rows, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
198
199
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 16 times.
64 if (local_rows > 0) {
200 48 my_row_indices.resize(static_cast<size_t>(local_rows));
201 48 local_a_flat.resize(static_cast<size_t>(local_rows) * static_cast<size_t>(cols_a));
202
203 48 MPI_Recv(my_row_indices.data(), local_rows, MPI_INT, 0, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
204 48 MPI_Recv(local_a_flat.data(), local_rows * cols_a, MPI_DOUBLE, 0, 2, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
205 }
206 64 }
207
208 128 void SosninaAMatrixMultHorizontalMPI::DistributeMatrixAData(std::vector<int> &my_row_indices,
209 std::vector<double> &local_a_flat, int &local_rows,
210 int rows_a, int cols_a) {
211
2/2
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 28 times.
128 local_rows = (rows_a / world_size_) + (rank_ < (rows_a % world_size_) ? 1 : 0);
212
213
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 128 times.
256 my_row_indices = GetRowsForProcess(rank_, rows_a);
214
215
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 128 times.
128 if (my_row_indices.size() != static_cast<size_t>(local_rows)) {
216 local_rows = static_cast<int>(my_row_indices.size());
217 }
218
219 128 local_a_flat.resize(static_cast<size_t>(local_rows) * static_cast<size_t>(cols_a));
220
221
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 64 times.
128 if (rank_ == 0) {
222 64 FillLocalAFlat(my_row_indices, local_a_flat, cols_a);
223
224
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 64 times.
128 for (int dest = 1; dest < world_size_; ++dest) {
225 64 std::vector<int> dest_rows = GetRowsForProcess(dest, rows_a);
226
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
64 SendRowsToProcess(dest, dest_rows, cols_a);
227 }
228 } else {
229 64 ReceiveRowsFromRoot(local_rows, my_row_indices, local_a_flat, cols_a);
230 }
231 128 }
232
233 64 void SosninaAMatrixMultHorizontalMPI::CollectLocalResults(const std::vector<int> &my_row_indices,
234 const std::vector<double> &local_result_flat,
235 std::vector<double> &final_result_flat, int cols_b) {
236
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 64 times.
140 for (size_t idx = 0; idx < my_row_indices.size(); ++idx) {
237 76 int global_row = my_row_indices[idx];
238
2/2
✓ Branch 0 taken 160 times.
✓ Branch 1 taken 76 times.
236 for (int j = 0; j < cols_b; ++j) {
239 160 final_result_flat[(static_cast<size_t>(global_row) * static_cast<size_t>(cols_b)) + static_cast<size_t>(j)] =
240 160 local_result_flat[(idx * static_cast<size_t>(cols_b)) + static_cast<size_t>(j)];
241 }
242 }
243 64 }
244
245 64 void SosninaAMatrixMultHorizontalMPI::ReceiveResultsFromProcess(int src, std::vector<double> &final_result_flat,
246 int cols_b) const {
247 64 int rows_a = static_cast<int>(final_result_flat.size() / cols_b);
248 64 std::vector<int> src_rows = GetRowsForProcess(src, rows_a);
249 64 int src_row_count = static_cast<int>(src_rows.size());
250
251
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 16 times.
64 if (src_row_count > 0) {
252
1/4
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
48 std::vector<double> buffer(static_cast<size_t>(src_row_count) * static_cast<size_t>(cols_b));
253
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 MPI_Recv(buffer.data(), src_row_count * cols_b, MPI_DOUBLE, src, 3, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
254
255
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 48 times.
96 for (int idx = 0; idx < src_row_count; ++idx) {
256 48 int global_row = src_rows[idx];
257
2/2
✓ Branch 0 taken 108 times.
✓ Branch 1 taken 48 times.
156 for (int j = 0; j < cols_b; ++j) {
258 108 final_result_flat[(static_cast<size_t>(global_row) * static_cast<size_t>(cols_b)) + static_cast<size_t>(j)] =
259 108 buffer[(static_cast<size_t>(idx) * static_cast<size_t>(cols_b)) + static_cast<size_t>(j)];
260 }
261 }
262 }
263 64 }
264
265 64 void SosninaAMatrixMultHorizontalMPI::SendLocalResults(const std::vector<double> &local_result_flat, int local_rows,
266 int cols_b) {
267
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 16 times.
64 if (local_rows > 0) {
268 48 std::vector<double> data_copy = local_result_flat;
269
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 MPI_Send(data_copy.data(), local_rows * cols_b, MPI_DOUBLE, 0, 3, MPI_COMM_WORLD);
270 }
271 64 }
272
273 128 void SosninaAMatrixMultHorizontalMPI::GatherResults(std::vector<double> &final_result_flat,
274 const std::vector<int> &my_row_indices,
275 const std::vector<double> &local_result_flat, int local_rows,
276 int rows_a, int cols_b) const {
277
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 64 times.
128 if (rank_ == 0) {
278 64 final_result_flat.resize(static_cast<size_t>(rows_a) * static_cast<size_t>(cols_b), 0.0);
279
280 64 CollectLocalResults(my_row_indices, local_result_flat, final_result_flat, cols_b);
281
282
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 64 times.
128 for (int src = 1; src < world_size_; ++src) {
283 64 ReceiveResultsFromProcess(src, final_result_flat, cols_b);
284 }
285
286 64 MPI_Bcast(final_result_flat.data(), rows_a * cols_b, MPI_DOUBLE, 0, MPI_COMM_WORLD);
287 } else {
288 64 SendLocalResults(local_result_flat, local_rows, cols_b);
289
290 64 final_result_flat.resize(static_cast<size_t>(rows_a) * static_cast<size_t>(cols_b));
291 64 MPI_Bcast(final_result_flat.data(), rows_a * cols_b, MPI_DOUBLE, 0, MPI_COMM_WORLD);
292 }
293 128 }
294
295 128 void SosninaAMatrixMultHorizontalMPI::ComputeLocalMultiplication(const std::vector<double> &local_a_flat,
296 const std::vector<double> &b_flat,
297 std::vector<double> &local_result_flat, int local_rows,
298 int cols_a, int cols_b) {
299
2/2
✓ Branch 0 taken 124 times.
✓ Branch 1 taken 128 times.
252 for (int i = 0; i < local_rows; ++i) {
300 124 const double *a_row = &local_a_flat[static_cast<size_t>(i) * static_cast<size_t>(cols_a)];
301 124 double *result_row = &local_result_flat[static_cast<size_t>(i) * static_cast<size_t>(cols_b)];
302
303
2/2
✓ Branch 0 taken 248 times.
✓ Branch 1 taken 124 times.
372 for (int k = 0; k < cols_a; ++k) {
304 248 double aik = a_row[k];
305 248 const double *b_row = &b_flat[static_cast<size_t>(k) * static_cast<size_t>(cols_b)];
306
307
2/2
✓ Branch 0 taken 544 times.
✓ Branch 1 taken 248 times.
792 for (int j = 0; j < cols_b; ++j) {
308 544 result_row[j] += aik * b_row[j];
309 }
310 }
311 }
312 128 }
313
314 128 void SosninaAMatrixMultHorizontalMPI::ConvertToMatrix(const std::vector<double> &final_result_flat, int rows_a,
315 int cols_b) {
316
1/2
✓ Branch 2 taken 128 times.
✗ Branch 3 not taken.
128 std::vector<std::vector<double>> result_matrix(rows_a, std::vector<double>(cols_b));
317
318
2/2
✓ Branch 0 taken 248 times.
✓ Branch 1 taken 128 times.
376 for (int i = 0; i < rows_a; ++i) {
319
2/2
✓ Branch 0 taken 536 times.
✓ Branch 1 taken 248 times.
784 for (int j = 0; j < cols_b; ++j) {
320 536 result_matrix[i][j] =
321 536 final_result_flat[(static_cast<size_t>(i) * static_cast<size_t>(cols_b)) + static_cast<size_t>(j)];
322 }
323 }
324
325
1/2
✓ Branch 1 taken 128 times.
✗ Branch 2 not taken.
128 GetOutput() = result_matrix;
326 128 }
327
328 128 bool SosninaAMatrixMultHorizontalMPI::PostProcessingImpl() {
329 128 return true;
330 }
331
332 } // namespace sosnina_a_matrix_mult_horizontal
333