GCC Code Coverage Report


Directory: ./
File: tasks/vlasova_a_image_smoothing/mpi/src/ops_mpi.cpp
Date: 2026-01-27 01:59:34
Exec Total Coverage
Lines: 115 123 93.5%
Functions: 8 8 100.0%
Branches: 73 116 62.9%

Line Branch Exec Source
1 #include "vlasova_a_image_smoothing/mpi/include/ops_mpi.hpp"
2
3 #include <mpi.h>
4
5 #include <algorithm>
6 #include <array>
7 #include <cstddef>
8 #include <cstdint>
9 #include <iterator>
10 #include <vector>
11
12 #include "vlasova_a_image_smoothing/common/include/common.hpp"
13
14 namespace vlasova_a_image_smoothing {
15
16 namespace {
17
18 12288 std::uint8_t ComputePixelMedian(int col_idx, int row_idx, int overlap_start,
19 const std::vector<std::uint8_t> &local_data, int width, int height, int window_size) {
20 12288 const int radius = window_size / 2;
21 12288 std::vector<std::uint8_t> neighbors;
22
1/2
✓ Branch 1 taken 12288 times.
✗ Branch 2 not taken.
12288 neighbors.reserve(static_cast<std::size_t>(window_size) * window_size);
23
24
2/2
✓ Branch 0 taken 36864 times.
✓ Branch 1 taken 12288 times.
49152 for (int dy = -radius; dy <= radius; ++dy) {
25
2/2
✓ Branch 0 taken 110592 times.
✓ Branch 1 taken 36864 times.
147456 for (int dx = -radius; dx <= radius; ++dx) {
26 110592 const int neighbor_x = col_idx + dx;
27 110592 const int neighbor_y = row_idx + dy;
28
29
4/4
✓ Branch 0 taken 109440 times.
✓ Branch 1 taken 1152 times.
✓ Branch 2 taken 108300 times.
✓ Branch 3 taken 1140 times.
110592 if (neighbor_x >= 0 && neighbor_x < width && neighbor_y >= 0 && neighbor_y < height) {
30 108300 const int local_row = neighbor_y - overlap_start;
31
1/2
✓ Branch 0 taken 108300 times.
✗ Branch 1 not taken.
108300 const std::size_t index = (static_cast<std::size_t>(local_row) * width) + neighbor_x;
32 neighbors.push_back(local_data[index]);
33 }
34 }
35 }
36
37
1/2
✓ Branch 0 taken 12288 times.
✗ Branch 1 not taken.
12288 if (!neighbors.empty()) {
38
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12288 times.
12288 auto middle = std::next(neighbors.begin(), static_cast<std::ptrdiff_t>(neighbors.size() / 2));
39 12288 std::nth_element(neighbors.begin(), middle, neighbors.end());
40 12288 return *middle;
41 }
42
43 const int local_row = row_idx - overlap_start;
44 const std::size_t index = (static_cast<std::size_t>(local_row) * width) + col_idx;
45 return local_data[index];
46 }
47
48 3 void PrepareScatterData(int size, int width, int height, int radius, std::vector<int> &sendcounts,
49 std::vector<int> &displs) {
50 3 const int base_rows = height / size;
51 3 const int extra_rows = height % size;
52
53
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
9 for (int proc = 0; proc < size; ++proc) {
54
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 const int proc_start = (proc * base_rows) + std::min(proc, extra_rows);
55
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 const int proc_end = proc_start + base_rows + (proc < extra_rows ? 1 : 0);
56
57
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (proc_end > proc_start) {
58 6 const int proc_overlap_start = std::max(0, proc_start - radius);
59 6 const int proc_overlap_end = std::min(height, proc_end + radius);
60 6 sendcounts[static_cast<std::size_t>(proc)] = (proc_overlap_end - proc_overlap_start) * width;
61 6 displs[static_cast<std::size_t>(proc)] = proc_overlap_start * width;
62 } else {
63 sendcounts[static_cast<std::size_t>(proc)] = 0;
64 displs[static_cast<std::size_t>(proc)] = 0;
65 }
66 }
67 3 }
68
69 3 void PrepareGatherData(int size, int width, int height, std::vector<int> &sendcounts, std::vector<int> &displs) {
70 3 const int base_rows = height / size;
71 3 const int extra_rows = height % size;
72
73
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
9 for (int proc = 0; proc < size; ++proc) {
74
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 const int proc_start = (proc * base_rows) + std::min(proc, extra_rows);
75
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 const int proc_end = proc_start + base_rows + (proc < extra_rows ? 1 : 0);
76 6 sendcounts[static_cast<std::size_t>(proc)] = (proc_end - proc_start) * width;
77 6 displs[static_cast<std::size_t>(proc)] = proc_start * width;
78 }
79 3 }
80
81 } // namespace
82
83
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 VlasovaAImageSmoothingMPI::VlasovaAImageSmoothingMPI(const InType &in) {
84 SetTypeOfTask(GetStaticTypeOfTask());
85 GetInput() = in;
86 6 }
87
88 6 bool VlasovaAImageSmoothingMPI::ValidationImpl() {
89 6 int rank = 0;
90 6 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
91
92
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (rank == 0) {
93 const auto &input = GetInput();
94
95
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 if (input.width <= 0 || input.height <= 0) {
96 return false;
97 }
98
99
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (input.data.empty()) {
100 return false;
101 }
102
103
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 const std::size_t expected_size = static_cast<std::size_t>(input.width) * input.height;
104
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (input.data.size() != expected_size) {
105 return false;
106 }
107 }
108
109 6 int is_valid = 1;
110 6 MPI_Bcast(&is_valid, 1, MPI_INT, 0, MPI_COMM_WORLD);
111
112 6 return is_valid == 1;
113 }
114
115 6 bool VlasovaAImageSmoothingMPI::PreProcessingImpl() {
116 6 int rank = 0;
117 6 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
118
119
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (rank == 0) {
120 const auto &input = GetInput();
121 3 width_ = input.width;
122 3 height_ = input.height;
123 }
124
125 6 std::array<int, 2> dimensions = {width_, height_};
126 6 MPI_Bcast(dimensions.data(), 2, MPI_INT, 0, MPI_COMM_WORLD);
127 6 MPI_Bcast(&window_size_, 1, MPI_INT, 0, MPI_COMM_WORLD);
128
129 6 width_ = dimensions[0];
130 6 height_ = dimensions[1];
131
132 6 return true;
133 }
134
135 6 bool VlasovaAImageSmoothingMPI::RunImpl() {
136 6 int rank = 0;
137 6 int size = 1;
138 6 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
139 6 MPI_Comm_size(MPI_COMM_WORLD, &size);
140
141 6 const int radius = window_size_ / 2;
142 6 const int base_rows = height_ / size;
143 6 const int extra_rows = height_ % size;
144
145
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 const int start_row = (rank * base_rows) + std::min(rank, extra_rows);
146
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 const int end_row = start_row + base_rows + (rank < extra_rows ? 1 : 0);
147 6 const int local_height = end_row - start_row;
148
149
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (local_height <= 0) {
150 GetOutput().width = width_;
151 GetOutput().height = height_;
152 GetOutput().data.clear();
153 return true;
154 }
155
156
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 const int overlap_start = std::max(0, start_row - radius);
157
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 const int overlap_end = std::min(height_, end_row + radius);
158 6 const int overlap_height = overlap_end - overlap_start;
159
160 6 std::vector<std::uint8_t> full_image;
161
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (rank == 0) {
162
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 full_image = GetInput().data;
163 }
164
165
1/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
6 std::vector<int> sendcounts(static_cast<std::size_t>(size));
166
1/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
6 std::vector<int> displs(static_cast<std::size_t>(size));
167
168
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (rank == 0) {
169 3 PrepareScatterData(size, width_, height_, radius, sendcounts, displs);
170 }
171
172
1/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
6 std::vector<std::uint8_t> local_image(static_cast<std::size_t>(overlap_height) * width_);
173
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 MPI_Scatterv((rank == 0 ? full_image.data() : nullptr), sendcounts.data(), displs.data(), MPI_UNSIGNED_CHAR,
174
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 local_image.data(), overlap_height * width_, MPI_UNSIGNED_CHAR, 0, MPI_COMM_WORLD);
175
176
1/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
6 std::vector<std::uint8_t> local_result(static_cast<std::size_t>(local_height) * width_);
177
2/2
✓ Branch 0 taken 192 times.
✓ Branch 1 taken 6 times.
198 for (int local_row = 0; local_row < local_height; ++local_row) {
178 192 const int global_row = start_row + local_row;
179
180
2/2
✓ Branch 0 taken 12288 times.
✓ Branch 1 taken 192 times.
12480 for (int col_idx = 0; col_idx < width_; ++col_idx) {
181 12288 const std::size_t index = (static_cast<std::size_t>(local_row) * width_) + col_idx;
182 12288 local_result[index] =
183
1/2
✓ Branch 1 taken 12288 times.
✗ Branch 2 not taken.
12288 ComputePixelMedian(col_idx, global_row, overlap_start, local_image, width_, height_, window_size_);
184 }
185 }
186
187 6 std::vector<std::uint8_t> result_image;
188
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (rank == 0) {
189
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 result_image.resize(static_cast<std::size_t>(width_) * height_);
190 }
191
192
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (rank == 0) {
193 3 PrepareGatherData(size, width_, height_, sendcounts, displs);
194 }
195
196
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 MPI_Gatherv(local_result.data(), local_height * width_, MPI_UNSIGNED_CHAR,
197
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 (rank == 0 ? result_image.data() : nullptr), sendcounts.data(), displs.data(), MPI_UNSIGNED_CHAR, 0,
198 MPI_COMM_WORLD);
199
200
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (rank == 0) {
201 3 GetOutput().width = width_;
202 3 GetOutput().height = height_;
203
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 GetOutput().data = result_image;
204 } else {
205 3 GetOutput().width = width_;
206 3 GetOutput().height = height_;
207 }
208
209 6 int data_size = static_cast<int>(GetOutput().data.size());
210
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 MPI_Bcast(&data_size, 1, MPI_INT, 0, MPI_COMM_WORLD);
211
212
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (rank != 0) {
213
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 GetOutput().data.resize(static_cast<std::size_t>(data_size));
214 }
215
216
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 MPI_Bcast(GetOutput().data.data(), data_size, MPI_UNSIGNED_CHAR, 0, MPI_COMM_WORLD);
217 return true;
218 }
219
220
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 bool VlasovaAImageSmoothingMPI::PostProcessingImpl() {
221 const auto &output = GetOutput();
222
223
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (output.data.empty()) {
224 return false;
225 }
226
227 6 const std::size_t expected_size = static_cast<std::size_t>(width_) * height_;
228 6 return output.data.size() == expected_size;
229 }
230
231 } // namespace vlasova_a_image_smoothing
232