GCC Code Coverage Report


Directory: ./
File: tasks/kondrashova_v_gauss_filter_vertical_split/mpi/src/ops_mpi.cpp
Date: 2026-01-27 01:59:34
Exec Total Coverage
Lines: 0 135 0.0%
Functions: 0 14 0.0%
Branches: 0 88 0.0%

Line Branch Exec Source
1 #include "kondrashova_v_gauss_filter_vertical_split/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 <vector>
10
11 #include "kondrashova_v_gauss_filter_vertical_split/common/include/common.hpp"
12
13 namespace kondrashova_v_gauss_filter_vertical_split {
14
15 const std::array<std::array<int, 3>, 3> KondrashovaVGaussFilterVerticalSplitMPI::kGaussKernel = {
16 {{{1, 2, 1}}, {{2, 4, 2}}, {{1, 2, 1}}}};
17 const int KondrashovaVGaussFilterVerticalSplitMPI::kGaussKernelSum = 16;
18
19 KondrashovaVGaussFilterVerticalSplitMPI::KondrashovaVGaussFilterVerticalSplitMPI(const InType &in) {
20 SetTypeOfTask(GetStaticTypeOfTask());
21 GetInput() = in;
22 }
23
24 uint8_t KondrashovaVGaussFilterVerticalSplitMPI::ApplyGaussToLocalPixel(const std::vector<uint8_t> &local_data,
25 int local_width, int height, int channels,
26 int px, int py, int channel) {
27 int sum = 0;
28
29 for (int ky = -1; ky <= 1; ++ky) {
30 for (int kx = -1; kx <= 1; ++kx) {
31 int nx = std::clamp(px + kx, 0, local_width - 1);
32 int ny = std::clamp(py + ky, 0, height - 1);
33
34 int idx = (((ny * local_width) + nx) * channels) + channel;
35 auto kernel_row = static_cast<size_t>(ky) + 1;
36 auto kernel_col = static_cast<size_t>(kx) + 1;
37 sum += local_data[idx] * kGaussKernel.at(kernel_row).at(kernel_col);
38 }
39 }
40
41 return static_cast<uint8_t>(std::clamp(sum / kGaussKernelSum, 0, 255));
42 }
43
44 bool KondrashovaVGaussFilterVerticalSplitMPI::ValidationImpl() {
45 int rank = 0;
46 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
47
48 if (rank == 0) {
49 const auto &input = GetInput();
50
51 auto expected_size = static_cast<size_t>(input.width) * input.height * input.channels;
52 return input.pixels.size() == expected_size && input.width >= 3 && input.height >= 3 && input.channels >= 1 &&
53 input.channels <= 4;
54 }
55 return true;
56 }
57
58 bool KondrashovaVGaussFilterVerticalSplitMPI::PreProcessingImpl() {
59 int rank = 0;
60 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
61
62 if (rank == 0) {
63 const auto &input = GetInput();
64 auto &output = GetOutput();
65
66 output.width = input.width;
67 output.height = input.height;
68 output.channels = input.channels;
69 output.pixels.resize(input.pixels.size());
70 }
71 return true;
72 }
73
74 void KondrashovaVGaussFilterVerticalSplitMPI::BroadcastImageDimensions(int &width, int &height, int &channels) {
75 int rank = 0;
76 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
77
78 if (rank == 0) {
79 width = GetInput().width;
80 height = GetInput().height;
81 channels = GetInput().channels;
82 }
83
84 MPI_Bcast(&width, 1, MPI_INT, 0, MPI_COMM_WORLD);
85 MPI_Bcast(&height, 1, MPI_INT, 0, MPI_COMM_WORLD);
86 MPI_Bcast(&channels, 1, MPI_INT, 0, MPI_COMM_WORLD);
87 }
88
89 void KondrashovaVGaussFilterVerticalSplitMPI::CalculateColumnDistribution(int width, int size,
90 std::vector<int> &col_counts,
91 std::vector<int> &col_offsets) {
92 int base_cols = width / size;
93 int extra_cols = width % size;
94
95 col_counts.resize(size);
96 col_offsets.resize(size);
97
98 for (int i = 0; i < size; ++i) {
99 col_counts[i] = base_cols + (i < extra_cols ? 1 : 0);
100 col_offsets[i] = (i == 0) ? 0 : col_offsets[i - 1] + col_counts[i - 1];
101 }
102 }
103
104 void KondrashovaVGaussFilterVerticalSplitMPI::CopyPixelsToBuffer(const std::vector<uint8_t> &src,
105 std::vector<uint8_t> &dst, int src_width,
106 int dst_width, int height, int channels,
107 int src_start_col) {
108 for (int row = 0; row < height; ++row) {
109 for (int col = 0; col < dst_width; ++col) {
110 for (int ch = 0; ch < channels; ++ch) {
111 int src_idx = (((row * src_width) + (src_start_col + col)) * channels) + ch;
112 int dst_idx = (((row * dst_width) + col) * channels) + ch;
113 dst[dst_idx] = src[src_idx];
114 }
115 }
116 }
117 }
118
119 void KondrashovaVGaussFilterVerticalSplitMPI::CopyBufferToOutput(const std::vector<uint8_t> &src,
120 std::vector<uint8_t> &dst, int src_width,
121 int dst_width, int height, int channels,
122 int dst_start_col) {
123 for (int row = 0; row < height; ++row) {
124 for (int col = 0; col < src_width; ++col) {
125 for (int ch = 0; ch < channels; ++ch) {
126 int src_idx = (((row * src_width) + col) * channels) + ch;
127 int dst_idx = (((row * dst_width) + (dst_start_col + col)) * channels) + ch;
128 dst[dst_idx] = src[src_idx];
129 }
130 }
131 }
132 }
133
134 void KondrashovaVGaussFilterVerticalSplitMPI::DistributeImageData(int rank, int size, int width, int height,
135 int channels, const std::vector<int> &col_counts,
136 const std::vector<int> &col_offsets,
137 std::vector<uint8_t> &local_data, int extended_cols) {
138 local_data.resize(static_cast<size_t>(extended_cols) * height * channels);
139
140 if (rank == 0) {
141 const auto &input_pixels = GetInput().pixels;
142
143 for (int proc = 0; proc < size; ++proc) {
144 int p_start = std::max(0, col_offsets[proc] - 1);
145 int p_end = std::min(width, col_offsets[proc] + col_counts[proc] + 1);
146 int p_cols = p_end - p_start;
147
148 std::vector<uint8_t> send_data(static_cast<size_t>(p_cols) * height * channels);
149 CopyPixelsToBuffer(input_pixels, send_data, width, p_cols, height, channels, p_start);
150
151 if (proc == 0) {
152 local_data = send_data;
153 } else {
154 MPI_Send(send_data.data(), static_cast<int>(send_data.size()), MPI_BYTE, proc, 0, MPI_COMM_WORLD);
155 }
156 }
157 } else {
158 MPI_Status status;
159 MPI_Recv(local_data.data(), static_cast<int>(local_data.size()), MPI_BYTE, 0, 0, MPI_COMM_WORLD, &status);
160 }
161 }
162
163 void KondrashovaVGaussFilterVerticalSplitMPI::ApplyGaussFilterToLocalData(const std::vector<uint8_t> &local_data,
164 std::vector<uint8_t> &local_result,
165 int extended_cols, int local_cols, int height,
166 int channels, int offset_in_extended) {
167 local_result.resize(static_cast<size_t>(local_cols) * height * channels);
168
169 for (int row = 0; row < height; ++row) {
170 for (int lx = 0; lx < local_cols; ++lx) {
171 int col = offset_in_extended + lx;
172 for (int ch = 0; ch < channels; ++ch) {
173 int result_idx = (((row * local_cols) + lx) * channels) + ch;
174 local_result[result_idx] = ApplyGaussToLocalPixel(local_data, extended_cols, height, channels, col, row, ch);
175 }
176 }
177 }
178 }
179
180 void KondrashovaVGaussFilterVerticalSplitMPI::GatherResults(int rank, int size, int width, int height, int channels,
181 const std::vector<int> &col_counts,
182 const std::vector<int> &col_offsets, int local_start_col,
183 int local_cols, const std::vector<uint8_t> &local_result) {
184 if (rank == 0) {
185 auto &output_pixels = GetOutput().pixels;
186
187 CopyBufferToOutput(local_result, output_pixels, local_cols, width, height, channels, local_start_col);
188
189 for (int proc = 1; proc < size; ++proc) {
190 int p_cols = col_counts[proc];
191 std::vector<uint8_t> recv_data(static_cast<size_t>(p_cols) * height * channels);
192 MPI_Status status;
193 MPI_Recv(recv_data.data(), static_cast<int>(recv_data.size()), MPI_BYTE, proc, 1, MPI_COMM_WORLD, &status);
194
195 CopyBufferToOutput(recv_data, output_pixels, p_cols, width, height, channels, col_offsets[proc]);
196 }
197 } else {
198 MPI_Send(local_result.data(), static_cast<int>(local_result.size()), MPI_BYTE, 0, 1, MPI_COMM_WORLD);
199 }
200 }
201
202 void KondrashovaVGaussFilterVerticalSplitMPI::BroadcastResultToAllProcesses(int width, int height, int channels) {
203 int rank = 0;
204 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
205
206 if (rank != 0) {
207 auto &output = GetOutput();
208 output.width = width;
209 output.height = height;
210 output.channels = channels;
211 output.pixels.resize(static_cast<size_t>(width) * height * channels);
212 }
213
214 MPI_Bcast(GetOutput().pixels.data(), static_cast<int>(GetOutput().pixels.size()), MPI_BYTE, 0, MPI_COMM_WORLD);
215 }
216
217 bool KondrashovaVGaussFilterVerticalSplitMPI::RunImpl() {
218 int rank = 0;
219 int size = 0;
220 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
221 MPI_Comm_size(MPI_COMM_WORLD, &size);
222
223 int width = 0;
224 int height = 0;
225 int channels = 0;
226 BroadcastImageDimensions(width, height, channels);
227
228 std::vector<int> col_counts;
229 std::vector<int> col_offsets;
230 CalculateColumnDistribution(width, size, col_counts, col_offsets);
231
232 int local_start_col = col_offsets[rank];
233 int local_cols = col_counts[rank];
234
235 int extended_start = std::max(0, local_start_col - 1);
236 int extended_end = std::min(width, local_start_col + local_cols + 1);
237 int extended_cols = extended_end - extended_start;
238 int offset_in_extended = local_start_col - extended_start;
239
240 std::vector<uint8_t> local_data;
241 DistributeImageData(rank, size, width, height, channels, col_counts, col_offsets, local_data, extended_cols);
242
243 std::vector<uint8_t> local_result;
244 ApplyGaussFilterToLocalData(local_data, local_result, extended_cols, local_cols, height, channels,
245 offset_in_extended);
246
247 GatherResults(rank, size, width, height, channels, col_counts, col_offsets, local_start_col, local_cols,
248 local_result);
249
250 BroadcastResultToAllProcesses(width, height, channels);
251
252 return true;
253 }
254
255 bool KondrashovaVGaussFilterVerticalSplitMPI::PostProcessingImpl() {
256 return true;
257 }
258
259 } // namespace kondrashova_v_gauss_filter_vertical_split
260