GCC Code Coverage Report


Directory: ./
File: tasks/melnik_i_min_neigh_diff_vec/mpi/src/ops_mpi.cpp
Date: 2026-01-27 01:59:34
Exec Total Coverage
Lines: 103 103 100.0%
Functions: 11 11 100.0%
Branches: 56 78 71.8%

Line Branch Exec Source
1 #include "melnik_i_min_neigh_diff_vec/mpi/include/ops_mpi.hpp"
2
3 #include <mpi.h>
4
5 #include <array>
6 #include <cstdlib>
7 #include <limits>
8 #include <tuple>
9 #include <vector>
10
11 #include "melnik_i_min_neigh_diff_vec/common/include/common.hpp"
12
13 namespace melnik_i_min_neigh_diff_vec {
14
15
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 MelnikIMinNeighDiffVecMPI::MelnikIMinNeighDiffVecMPI(const InType &in) {
16 SetTypeOfTask(GetStaticTypeOfTask());
17
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 GetInput() = in;
18 GetOutput() = std::make_tuple(-1, -1);
19 42 }
20
21 42 bool MelnikIMinNeighDiffVecMPI::ValidationImpl() {
22 42 int rank = 0;
23 42 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
24
25 42 int global_size = 0;
26
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 21 times.
42 if (rank == 0) {
27 21 global_size = static_cast<int>(GetInput().size());
28 }
29
30 42 MPI_Bcast(&global_size, 1, MPI_INT, 0, MPI_COMM_WORLD);
31 42 return global_size >= 2;
32 }
33
34 42 bool MelnikIMinNeighDiffVecMPI::PreProcessingImpl() {
35 42 return true;
36 }
37
38 42 void MelnikIMinNeighDiffVecMPI::ScatterData(std::vector<int> &local_data, const std::vector<int> &counts,
39 const std::vector<int> &displs, int rank) {
40
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 21 times.
42 int *sendbuf = (rank == 0) ? this->GetInput().data() : nullptr;
41 42 MPI_Scatterv(sendbuf, counts.data(), displs.data(), MPI_INT, local_data.data(), static_cast<int>(local_data.size()),
42 MPI_INT, 0, MPI_COMM_WORLD);
43 42 }
44
45 42 void MelnikIMinNeighDiffVecMPI::ComputeLocalMin(Result &local_res, const std::vector<int> &local_data, int local_size,
46 int local_displ) {
47 42 local_res.delta = std::numeric_limits<int>::max();
48 42 local_res.index = -1;
49
2/2
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 2 times.
42 if (local_size >= 2) {
50
2/2
✓ Branch 0 taken 6339 times.
✓ Branch 1 taken 40 times.
6379 for (int i = 0; i < local_size - 1; i++) {
51
2/2
✓ Branch 0 taken 6293 times.
✓ Branch 1 taken 46 times.
6339 int cur_diff = std::abs(local_data[i + 1] - local_data[i]);
52
5/6
✓ Branch 0 taken 6293 times.
✓ Branch 1 taken 46 times.
✓ Branch 2 taken 6251 times.
✓ Branch 3 taken 42 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 6251 times.
6339 if (cur_diff < local_res.delta || (cur_diff == local_res.delta && (local_displ + i) < local_res.index)) {
53 46 local_res.delta = cur_diff;
54 46 local_res.index = local_displ + i;
55 }
56 }
57 }
58 42 }
59
60 42 void MelnikIMinNeighDiffVecMPI::HandleBoundaryDiffs(Result &local_res, int local_size,
61 const std::vector<int> &local_data, int local_displ, int rank,
62 int comm_size) {
63
1/2
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
42 int left_boundary = (local_size > 0) ? local_data.front() : 0;
64 42 int right_boundary = (local_size > 0) ? local_data.back() : 0;
65 42 int recv_from_left = 0;
66 42 int recv_from_right = 0;
67
68 42 PerformBoundaryCommunications(left_boundary, right_boundary, recv_from_left, recv_from_right, rank, comm_size);
69
70
1/2
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
42 if (local_size > 0) {
71 42 UpdateResultWithBoundaryDiffs(local_res, left_boundary, right_boundary, recv_from_left, recv_from_right,
72 local_displ, local_size, rank, comm_size);
73 }
74 42 }
75
76 42 void MelnikIMinNeighDiffVecMPI::PerformBoundaryCommunications(int left_boundary, int right_boundary,
77 int &recv_from_left, int &recv_from_right, int rank,
78 int comm_size) {
79 // 4 operations maximum: send+recv from both neighbours
80 42 std::array<MPI_Request, 4> requests = {MPI_REQUEST_NULL, MPI_REQUEST_NULL, MPI_REQUEST_NULL, MPI_REQUEST_NULL};
81 int req_count = 0;
82
83 // left neighbour
84
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 21 times.
42 if (rank > 0) {
85 21 MPI_Isend(&left_boundary, 1, MPI_INT, rank - 1, 0, MPI_COMM_WORLD, &requests.at(req_count));
86 ++req_count;
87 21 MPI_Irecv(&recv_from_left, 1, MPI_INT, rank - 1, 1, MPI_COMM_WORLD, &requests.at(req_count));
88 ++req_count;
89 }
90
91 // right neighbour
92
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 21 times.
42 if (rank < comm_size - 1) {
93 21 MPI_Irecv(&recv_from_right, 1, MPI_INT, rank + 1, 0, MPI_COMM_WORLD, &requests.at(req_count));
94 21 ++req_count;
95 21 MPI_Isend(&right_boundary, 1, MPI_INT, rank + 1, 1, MPI_COMM_WORLD, &requests.at(req_count));
96 21 ++req_count;
97 }
98
99
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 if (req_count > 0) {
100 42 MPI_Waitall(req_count, requests.data(), MPI_STATUSES_IGNORE);
101 }
102 42 }
103
104 42 void MelnikIMinNeighDiffVecMPI::UpdateResultWithBoundaryDiffs(Result &local_res, int left_boundary, int right_boundary,
105 int recv_from_left, int recv_from_right, int local_displ,
106 int local_size, int rank, int comm_size) {
107 // left neighbour result vs left boundary
108
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 21 times.
42 if (rank > 0) {
109 21 int boundary_delta = std::abs(left_boundary - recv_from_left);
110 21 int boundary_idx = local_displ - 1;
111
5/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
21 if (boundary_delta < local_res.delta || (boundary_delta == local_res.delta && boundary_idx < local_res.index)) {
112 17 local_res.delta = boundary_delta;
113 17 local_res.index = boundary_idx;
114 }
115 }
116
117 // right neighbour result vs right boundary
118
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 21 times.
42 if (rank < comm_size - 1) {
119 21 int boundary_delta = std::abs(recv_from_right - right_boundary);
120 21 int boundary_idx = local_displ + local_size - 1;
121
5/6
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 13 times.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 13 times.
21 if (boundary_delta < local_res.delta || (boundary_delta == local_res.delta && boundary_idx < local_res.index)) {
122 2 local_res.delta = boundary_delta;
123 2 local_res.index = boundary_idx;
124 }
125 }
126 42 }
127
128 42 void MelnikIMinNeighDiffVecMPI::ReduceAndBroadcastResult(Result &global_res, const Result &local_res) {
129 42 MPI_Reduce(&local_res, &global_res, 1, MPI_2INT, MPI_MINLOC, 0, MPI_COMM_WORLD);
130 42 MPI_Bcast(&global_res, 1, MPI_2INT, 0, MPI_COMM_WORLD);
131 42 }
132
133 42 bool MelnikIMinNeighDiffVecMPI::RunImpl() {
134 42 int rank = 0;
135 42 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
136 42 int comm_size = 1;
137 42 MPI_Comm_size(MPI_COMM_WORLD, &comm_size);
138
139 42 int global_size = 0;
140
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 21 times.
42 if (rank == 0) {
141 21 global_size = static_cast<int>(this->GetInput().size());
142 }
143 42 MPI_Bcast(&global_size, 1, MPI_INT, 0, MPI_COMM_WORLD);
144
145 // Distribution with remainder
146 42 std::vector<int> counts(comm_size);
147
1/4
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
42 std::vector<int> displs(comm_size);
148
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 21 times.
42 if (rank == 0) {
149 21 int base = global_size / comm_size;
150 21 int rem = global_size % comm_size;
151 int offset = 0;
152
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 21 times.
63 for (int i = 0; i < comm_size; i++) {
153
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 3 times.
81 counts[i] = base + (i < rem ? 1 : 0);
154 42 displs[i] = offset;
155 42 offset += counts[i];
156 }
157 }
158
159
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 int local_size = 0;
160
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 MPI_Scatter(counts.data(), 1, MPI_INT, &local_size, 1, MPI_INT, 0, MPI_COMM_WORLD);
161
162
1/4
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
42 std::vector<int> local_data(local_size);
163
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 ScatterData(local_data, counts, displs, rank);
164
165 // Compute displacement
166 42 int local_displ = 0;
167
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 MPI_Scan(&local_size, &local_displ, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
168 42 local_displ -= local_size;
169
170 42 Result local_res;
171 42 ComputeLocalMin(local_res, local_data, local_size, local_displ);
172
173
1/2
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
42 if (comm_size > 1) {
174
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 HandleBoundaryDiffs(local_res, local_size, local_data, local_displ, rank, comm_size);
175 }
176
177 42 Result global_res;
178
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 ReduceAndBroadcastResult(global_res, local_res);
179
180
1/2
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
42 if (global_res.index >= 0) {
181 42 GetOutput() = std::make_tuple(global_res.index, global_res.index + 1);
182 42 return true;
183 }
184 return false;
185 }
186
187 42 bool MelnikIMinNeighDiffVecMPI::PostProcessingImpl() {
188 42 return true;
189 }
190
191 } // namespace melnik_i_min_neigh_diff_vec
192