GCC Code Coverage Report


Directory: ./
File: tasks/vlasova_a_matrix_multiply_ccs/mpi/src/ops_mpi.cpp
Date: 2026-01-27 01:59:34
Exec Total Coverage
Lines: 154 159 96.9%
Functions: 11 12 91.7%
Branches: 104 178 58.4%

Line Branch Exec Source
1 #include "vlasova_a_matrix_multiply_ccs/mpi/include/ops_mpi.hpp"
2
3 #include <mpi.h>
4
5 #include <algorithm>
6 #include <cmath>
7 #include <cstddef>
8 #include <utility>
9 #include <vector>
10
11 #include "vlasova_a_matrix_multiply_ccs/common/include/common.hpp"
12
13 namespace vlasova_a_matrix_multiply_ccs {
14
15 namespace {
16 constexpr double kEpsilon = 1e-10;
17 } // namespace
18
19
1/2
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 VlasovaAMatrixMultiplyMPI::VlasovaAMatrixMultiplyMPI(const InType &in) {
20 SetTypeOfTask(GetStaticTypeOfTask());
21 GetInput() = in;
22 6 GetOutput() = SparseMatrixCCS{};
23 6 }
24
25 6 bool VlasovaAMatrixMultiplyMPI::ValidationImpl() {
26 const auto &[a, b] = GetInput();
27
5/10
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 6 times.
6 return (a.rows > 0 && a.cols > 0 && b.rows > 0 && b.cols > 0 && a.cols == b.rows);
28 }
29
30 6 bool VlasovaAMatrixMultiplyMPI::PreProcessingImpl() {
31 6 return true;
32 }
33
34 3 void VlasovaAMatrixMultiplyMPI::TransposeMatrixMPI(const SparseMatrixCCS &a, SparseMatrixCCS &at) {
35 3 at.rows = a.cols;
36 3 at.cols = a.rows;
37 3 at.nnz = a.nnz;
38
39
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (a.nnz == 0) {
40 at.values.clear();
41 at.row_indices.clear();
42 at.col_ptrs.assign(at.cols + 1, 0);
43 return;
44 }
45
46 3 std::vector<int> row_count(at.cols, 0);
47
2/2
✓ Branch 0 taken 769 times.
✓ Branch 1 taken 3 times.
772 for (int i = 0; i < a.nnz; i++) {
48 769 row_count[a.row_indices[i]]++;
49 }
50
51
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 at.col_ptrs.resize(at.cols + 1);
52 3 at.col_ptrs[0] = 0;
53
2/2
✓ Branch 0 taken 160 times.
✓ Branch 1 taken 3 times.
163 for (int i = 0; i < at.cols; i++) {
54 160 at.col_ptrs[i + 1] = at.col_ptrs[i] + row_count[i];
55 }
56
57
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 at.values.resize(a.nnz);
58
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 at.row_indices.resize(a.nnz);
59
60
1/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
3 std::vector<int> current_pos(at.cols, 0);
61
2/2
✓ Branch 0 taken 160 times.
✓ Branch 1 taken 3 times.
163 for (int col = 0; col < a.cols; col++) {
62
2/2
✓ Branch 0 taken 769 times.
✓ Branch 1 taken 160 times.
929 for (int i = a.col_ptrs[col]; i < a.col_ptrs[col + 1]; i++) {
63 769 int row = a.row_indices[i];
64 769 double val = a.values[i];
65
66 769 int pos = at.col_ptrs[row] + current_pos[row];
67 769 at.values[pos] = val;
68 769 at.row_indices[pos] = col;
69 769 current_pos[row]++;
70 }
71 }
72 }
73
74 std::pair<int, int> VlasovaAMatrixMultiplyMPI::SplitColumns(int total_cols, int rank, int size) {
75 6 int base_cols = total_cols / size;
76 6 int remainder = total_cols % size;
77
78 int start_col = (rank * base_cols) + std::min(rank, remainder);
79
1/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
6 int end_col = start_col + base_cols + (rank < remainder ? 1 : 0);
80
81 return {start_col, end_col};
82 }
83
84 160 void VlasovaAMatrixMultiplyMPI::ProcessLocalColumn(const SparseMatrixCCS &at, const std::vector<double> &loc_val,
85 const std::vector<int> &loc_row_ind,
86 const std::vector<int> &loc_col_ptr, int col_index,
87 std::vector<double> &temp_row, std::vector<int> &row_marker,
88 std::vector<double> &res_val, std::vector<int> &res_row_ind) {
89 160 int col_start = loc_col_ptr[col_index];
90 160 int col_end = loc_col_ptr[col_index + 1];
91
92
2/2
✓ Branch 0 taken 794 times.
✓ Branch 1 taken 160 times.
954 for (int k = col_start; k < col_end; k++) {
93 794 int row_b = loc_row_ind[k];
94 794 double val_b = loc_val[k];
95
96
2/2
✓ Branch 0 taken 3782 times.
✓ Branch 1 taken 794 times.
4576 for (int idx = at.col_ptrs[row_b]; idx < at.col_ptrs[row_b + 1]; idx++) {
97
2/2
✓ Branch 0 taken 3213 times.
✓ Branch 1 taken 569 times.
3782 int row_a = at.row_indices[idx];
98 3782 double val_a = at.values[idx];
99
100
2/2
✓ Branch 0 taken 3213 times.
✓ Branch 1 taken 569 times.
3782 if (row_marker[row_a] != col_index) {
101 3213 row_marker[row_a] = col_index;
102 3213 temp_row[row_a] = val_a * val_b;
103 } else {
104 569 temp_row[row_a] += val_a * val_b;
105 }
106 }
107 }
108
109
2/2
✓ Branch 0 taken 12600 times.
✓ Branch 1 taken 160 times.
12760 for (int i = 0; i < at.cols; i++) {
110
3/4
✓ Branch 0 taken 3213 times.
✓ Branch 1 taken 9387 times.
✓ Branch 2 taken 3213 times.
✗ Branch 3 not taken.
12600 if (row_marker[i] == col_index && std::abs(temp_row[i]) > kEpsilon) {
111 res_val.push_back(temp_row[i]);
112 res_row_ind.push_back(i);
113 }
114 }
115 160 }
116
117
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 void VlasovaAMatrixMultiplyMPI::ExtractLocalColumns(const SparseMatrixCCS &b, int start_col, int end_col,
118 std::vector<double> &loc_val, std::vector<int> &loc_row_ind,
119 std::vector<int> &loc_col_ptr) {
120 loc_val.clear();
121 loc_row_ind.clear();
122 loc_col_ptr.clear();
123
124 6 loc_col_ptr.push_back(0);
125
126
2/2
✓ Branch 0 taken 160 times.
✓ Branch 1 taken 6 times.
166 for (int col = start_col; col < end_col; col++) {
127 160 int start_index = b.col_ptrs[col];
128 160 int end_index = b.col_ptrs[col + 1];
129
130
2/2
✓ Branch 0 taken 794 times.
✓ Branch 1 taken 160 times.
954 for (int i = start_index; i < end_index; i++) {
131
2/2
✓ Branch 0 taken 749 times.
✓ Branch 1 taken 45 times.
794 loc_val.push_back(b.values[i]);
132 loc_row_ind.push_back(b.row_indices[i]);
133 }
134
135 160 loc_col_ptr.push_back(static_cast<int>(loc_val.size()));
136 }
137 6 }
138
139
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 void VlasovaAMatrixMultiplyMPI::MultiplyLocalMatrices(const SparseMatrixCCS &at, const std::vector<double> &loc_val,
140 const std::vector<int> &loc_row_ind,
141 const std::vector<int> &loc_col_ptr, int loc_cols,
142 std::vector<double> &res_val, std::vector<int> &res_row_ind,
143 std::vector<int> &res_col_ptr) {
144 res_val.clear();
145 res_row_ind.clear();
146 res_col_ptr.clear();
147 6 res_col_ptr.push_back(0);
148
149 6 std::vector<double> temp_row(at.cols, 0.0);
150
1/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
6 std::vector<int> row_marker(at.cols, -1);
151
152
2/2
✓ Branch 0 taken 160 times.
✓ Branch 1 taken 6 times.
166 for (int j = 0; j < loc_cols; j++) {
153
1/2
✓ Branch 1 taken 160 times.
✗ Branch 2 not taken.
160 ProcessLocalColumn(at, loc_val, loc_row_ind, loc_col_ptr, j, temp_row, row_marker, res_val, res_row_ind);
154
1/4
✓ Branch 1 taken 160 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
160 res_col_ptr.push_back(static_cast<int>(res_val.size()));
155 }
156 6 }
157
158 3 bool VlasovaAMatrixMultiplyMPI::ProcessRootRank(const SparseMatrixCCS &a, const SparseMatrixCCS &b,
159 std::vector<double> &loc_res_val, std::vector<int> &loc_res_row_ind,
160 std::vector<int> &loc_res_col_ptr, int size) {
161 3 SparseMatrixCCS c;
162 3 c.rows = a.rows;
163 3 c.cols = b.cols;
164
165
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 std::vector<std::vector<double>> all_values(size);
166
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 std::vector<std::vector<int>> all_row_indices(size);
167
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 std::vector<std::vector<int>> all_col_ptrs(size);
168
169 all_values[0] = std::move(loc_res_val);
170 all_row_indices[0] = std::move(loc_res_row_ind);
171 all_col_ptrs[0] = std::move(loc_res_col_ptr);
172
173
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 for (int src = 1; src < size; src++) {
174 3 int src_nnz = 0;
175 3 int src_cols = 0;
176
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 MPI_Recv(&src_nnz, 1, MPI_INT, src, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
177
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 MPI_Recv(&src_cols, 1, MPI_INT, src, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
178
179
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 std::vector<double> src_vals(src_nnz);
180
1/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
3 std::vector<int> src_rows(src_nnz);
181
1/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
3 std::vector<int> src_ptrs(src_cols + 1);
182
183
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 MPI_Recv(src_vals.data(), src_nnz, MPI_DOUBLE, src, 2, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
184
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 MPI_Recv(src_rows.data(), src_nnz, MPI_INT, src, 3, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
185
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 MPI_Recv(src_ptrs.data(), src_cols + 1, MPI_INT, src, 4, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
186
187 3 all_values[src] = std::move(src_vals);
188 all_row_indices[src] = std::move(src_rows);
189 all_col_ptrs[src] = std::move(src_ptrs);
190 }
191
192
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 c.col_ptrs.push_back(0);
193
194
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 std::vector<int> value_offsets(size, 0);
195
1/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
3 std::vector<int> col_offsets(size, 0);
196
197
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
9 for (int i = 0; i < size; i++) {
198
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (i > 0) {
199 3 value_offsets[i] = value_offsets[i - 1] + static_cast<int>(all_values[i - 1].size());
200 3 col_offsets[i] = col_offsets[i - 1] + static_cast<int>(all_col_ptrs[i - 1].size() - 1);
201 }
202 }
203
204
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
9 for (int i = 0; i < size; i++) {
205
2/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
6 c.values.insert(c.values.end(), all_values[i].begin(), all_values[i].end());
206 6 c.row_indices.insert(c.row_indices.end(), all_row_indices[i].begin(), all_row_indices[i].end());
207
208
2/2
✓ Branch 0 taken 160 times.
✓ Branch 1 taken 6 times.
166 for (size_t j = 1; j < all_col_ptrs[i].size(); j++) {
209
1/4
✓ Branch 1 taken 160 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
160 c.col_ptrs.push_back(all_col_ptrs[i][j] + value_offsets[i]);
210 }
211 }
212
213
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 c.nnz = static_cast<int>(c.values.size());
214
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 GetOutput() = c;
215
216
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 MPI_Barrier(MPI_COMM_WORLD);
217 3 return true;
218 3 }
219
220 3 bool VlasovaAMatrixMultiplyMPI::ProcessWorkerRank(const std::vector<double> &loc_res_val,
221 const std::vector<int> &loc_res_row_ind,
222 const std::vector<int> &loc_res_col_ptr, int loc_cols) {
223 3 int local_nnz = static_cast<int>(loc_res_val.size());
224 3 int local_cols = loc_cols;
225
226 3 MPI_Send(&local_nnz, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
227 3 MPI_Send(&local_cols, 1, MPI_INT, 0, 1, MPI_COMM_WORLD);
228 3 MPI_Send(loc_res_val.data(), local_nnz, MPI_DOUBLE, 0, 2, MPI_COMM_WORLD);
229 3 MPI_Send(loc_res_row_ind.data(), local_nnz, MPI_INT, 0, 3, MPI_COMM_WORLD);
230 3 MPI_Send(loc_res_col_ptr.data(), loc_cols + 1, MPI_INT, 0, 4, MPI_COMM_WORLD);
231
232 3 MPI_Barrier(MPI_COMM_WORLD);
233 3 return true;
234 }
235
236 6 bool VlasovaAMatrixMultiplyMPI::RunImpl() {
237 const auto &[a, b] = GetInput();
238
239 6 int rank = 0;
240 6 int size = 0;
241 6 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
242 6 MPI_Comm_size(MPI_COMM_WORLD, &size);
243
244 6 SparseMatrixCCS at;
245
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (rank == 0) {
246
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 TransposeMatrixMPI(a, at);
247 } else {
248 3 at.rows = a.cols;
249 3 at.cols = a.rows;
250 }
251
252
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 MPI_Bcast(&at.rows, 1, MPI_INT, 0, MPI_COMM_WORLD);
253
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 MPI_Bcast(&at.cols, 1, MPI_INT, 0, MPI_COMM_WORLD);
254
255
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (rank == 0) {
256 3 at.nnz = static_cast<int>(at.values.size());
257 }
258
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 MPI_Bcast(&at.nnz, 1, MPI_INT, 0, MPI_COMM_WORLD);
259
260
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (rank != 0) {
261
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 at.values.resize(at.nnz);
262
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 at.row_indices.resize(at.nnz);
263
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 at.col_ptrs.resize(at.cols + 1);
264 }
265
266
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 MPI_Bcast(at.values.data(), at.nnz, MPI_DOUBLE, 0, MPI_COMM_WORLD);
267
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 MPI_Bcast(at.row_indices.data(), at.nnz, MPI_INT, 0, MPI_COMM_WORLD);
268
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 MPI_Bcast(at.col_ptrs.data(), at.cols + 1, MPI_INT, 0, MPI_COMM_WORLD);
269
270
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 auto [start_col, end_col] = SplitColumns(b.cols, rank, size);
271 6 int loc_cols = end_col - start_col;
272
273 6 std::vector<double> loc_b_val;
274 6 std::vector<int> loc_b_row_ind;
275 6 std::vector<int> loc_b_col_ptr;
276
277
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 ExtractLocalColumns(b, start_col, end_col, loc_b_val, loc_b_row_ind, loc_b_col_ptr);
278
279 6 std::vector<double> loc_res_val;
280 6 std::vector<int> loc_res_row_ind;
281 6 std::vector<int> loc_res_col_ptr;
282
283
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 MultiplyLocalMatrices(at, loc_b_val, loc_b_row_ind, loc_b_col_ptr, loc_cols, loc_res_val, loc_res_row_ind,
284 loc_res_col_ptr);
285
286
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (rank == 0) {
287
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 return ProcessRootRank(a, b, loc_res_val, loc_res_row_ind, loc_res_col_ptr, size);
288 }
289
290
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 return ProcessWorkerRank(loc_res_val, loc_res_row_ind, loc_res_col_ptr, loc_cols);
291 6 }
292
293 6 bool VlasovaAMatrixMultiplyMPI::PostProcessingImpl() {
294 6 int rank = 0;
295 6 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
296
297 const auto &c = GetOutput();
298
299
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (rank == 0) {
300
3/6
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
3 return c.rows > 0 && c.cols > 0 && c.col_ptrs.size() == static_cast<size_t>(c.cols) + 1;
301 }
302
303 3 return c.rows == 0 && c.cols == 0;
304 }
305
306 } // namespace vlasova_a_matrix_multiply_ccs
307