GCC Code Coverage Report


Directory: ./
File: tasks/badanov_a_select_edge_sobel/all/src/ops_all.cpp
Date: 2026-06-04 20:25:32
Exec Total Coverage
Lines: 112 117 95.7%
Functions: 10 10 100.0%
Branches: 67 94 71.3%

Line Branch Exec Source
1 #include "badanov_a_select_edge_sobel/all/include/ops_all.hpp"
2
3 #include <algorithm>
4 #include <cmath>
5 #include <cstddef>
6 #include <cstdint>
7 #include <mutex>
8 #include <thread>
9 #include <vector>
10
11 #include "badanov_a_select_edge_sobel/common/include/common.hpp"
12 #include "mpi.h"
13
14 namespace badanov_a_select_edge_sobel {
15
16
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 BadanovASelectEdgeSobelALL::BadanovASelectEdgeSobelALL(const InType &in) {
17 SetTypeOfTask(GetStaticTypeOfTask());
18
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 GetInput() = in;
19 20 GetOutput() = std::vector<uint8_t>();
20
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 MPI_Comm_rank(MPI_COMM_WORLD, &rank_);
21
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 MPI_Comm_size(MPI_COMM_WORLD, &size_);
22 20 }
23
24 20 bool BadanovASelectEdgeSobelALL::ValidationImpl() {
25 const auto &input = GetInput();
26 20 return !input.empty();
27 }
28
29
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 14 times.
20 bool BadanovASelectEdgeSobelALL::PreProcessingImpl() {
30 const auto &input = GetInput();
31
32 20 width_ = static_cast<int>(std::sqrt(input.size()));
33 20 height_ = width_;
34
35
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 14 times.
20 if (width_ * height_ != static_cast<int>(input.size())) {
36 6 width_ = static_cast<int>(input.size());
37 6 height_ = 1;
38 }
39
40 20 GetOutput() = std::vector<uint8_t>(input.size(), 0);
41
42 20 return true;
43 }
44
45 10 void BadanovASelectEdgeSobelALL::ApplySobelOperator(const std::vector<uint8_t> &input, std::vector<float> &magnitude,
46 float &max_magnitude) {
47 10 const int height = height_;
48 10 const int width = width_;
49
50
2/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
10 if (height < 3 || width < 3) {
51 max_magnitude = 0.0F;
52 return;
53 }
54
55 // Распределение строк между MPI-процессами
56
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 int rows_per_process = (height - 2) / size_;
57 10 rows_per_process = std::max(rows_per_process, 1);
58
59 10 int start_row = 1 + (rank_ * rows_per_process);
60 10 int end_row = start_row + rows_per_process;
61
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 end_row = std::min(end_row, height - 1);
62
63
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (start_row >= end_row) {
64 max_magnitude = 0.0F;
65 return;
66 }
67
68 // Внутри каждого процесса используем STL (std::thread)
69 10 unsigned int num_threads = std::thread::hardware_concurrency();
70
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (num_threads == 0) {
71 num_threads = 2;
72 }
73
74 10 int rows_to_process = end_row - start_row;
75
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 6 times.
10 unsigned int rows_per_thread = static_cast<unsigned int>(rows_to_process) / num_threads;
76 10 rows_per_thread = std::max<unsigned int>(rows_per_thread, 1U);
77 10 num_threads = (static_cast<unsigned int>(rows_to_process) + rows_per_thread - 1) / rows_per_thread;
78
79 10 std::vector<std::thread> threads;
80 10 std::mutex max_mutex;
81 10 float local_max = 0.0F;
82
83
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 10 times.
52 for (unsigned int thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
84 42 int thread_start_row = start_row + static_cast<int>(thread_idx * rows_per_thread);
85 int thread_end_row =
86
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 10 times.
42 (thread_idx == num_threads - 1) ? end_row : (thread_start_row + static_cast<int>(rows_per_thread));
87
88
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 threads.emplace_back([&, thread_start_row, thread_end_row]() {
89 42 float thread_local_max = 0.0F;
90
91
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 42 times.
84 for (int row = thread_start_row; row < thread_end_row; ++row) {
92
2/2
✓ Branch 0 taken 468 times.
✓ Branch 1 taken 42 times.
510 for (int col = 1; col < width - 1; ++col) {
93 468 float gradient_x = 0.0F;
94 468 float gradient_y = 0.0F;
95
96 468 ComputeGradientAtPixel(input, row, col, gradient_x, gradient_y);
97
98 468 const float magnitude_value = std::sqrt((gradient_x * gradient_x) + (gradient_y * gradient_y));
99 468 const size_t idx = (static_cast<size_t>(row) * static_cast<size_t>(width)) + static_cast<size_t>(col);
100 468 magnitude[idx] = magnitude_value;
101
102 468 thread_local_max = std::max(magnitude_value, thread_local_max);
103 }
104 }
105
106 42 std::scoped_lock lock(max_mutex);
107
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 37 times.
47 local_max = std::max(thread_local_max, local_max);
108 42 });
109 }
110
111
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 10 times.
52 for (auto &thread : threads) {
112
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 thread.join();
113 }
114
115 10 max_magnitude = local_max;
116 10 }
117
118 468 void BadanovASelectEdgeSobelALL::ComputeGradientAtPixel(const std::vector<uint8_t> &input, int row, int col,
119 float &gradient_x, float &gradient_y) const {
120 468 gradient_x = 0.0F;
121 468 gradient_y = 0.0F;
122
123
2/2
✓ Branch 0 taken 1404 times.
✓ Branch 1 taken 468 times.
1872 for (int kernel_row = -1; kernel_row <= 1; ++kernel_row) {
124
2/2
✓ Branch 0 taken 4212 times.
✓ Branch 1 taken 1404 times.
5616 for (int kernel_col = -1; kernel_col <= 1; ++kernel_col) {
125 4212 const size_t pixel_index =
126 4212 (static_cast<size_t>(row + kernel_row) * static_cast<size_t>(width_)) + static_cast<size_t>(col + kernel_col);
127 4212 const uint8_t pixel = input[pixel_index];
128
129 4212 const int kx_idx = kernel_row + 1;
130 4212 const int ky_idx = kernel_col + 1;
131 4212 const int kernel_x_value = kKernelX.at(static_cast<size_t>(kx_idx)).at(static_cast<size_t>(ky_idx));
132 4212 const int kernel_y_value = kKernelY.at(static_cast<size_t>(kx_idx)).at(static_cast<size_t>(ky_idx));
133
134 4212 gradient_x += static_cast<float>(pixel) * static_cast<float>(kernel_x_value);
135 4212 gradient_y += static_cast<float>(pixel) * static_cast<float>(kernel_y_value);
136 }
137 }
138 468 }
139
140 10 void BadanovASelectEdgeSobelALL::ApplyThreshold(const std::vector<float> &magnitude, float max_magnitude,
141 std::vector<uint8_t> &output) const {
142
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 8 times.
10 if (max_magnitude <= 0.0F) {
143 std::ranges::fill(output, 0);
144 2 return;
145 }
146
147 8 const float scale = 255.0F / max_magnitude;
148 const size_t size = magnitude.size();
149 8 const int threshold = threshold_;
150
151 8 unsigned int num_threads = std::thread::hardware_concurrency();
152
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (num_threads == 0) {
153 num_threads = 2;
154 }
155
156 8 std::vector<std::thread> threads;
157 8 size_t chunk_size = (size + num_threads - 1) / num_threads;
158
159
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 8 times.
40 for (unsigned int thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
160 32 size_t start = thread_idx * chunk_size;
161
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 8 times.
32 size_t end = (thread_idx == num_threads - 1) ? size : (start + chunk_size);
162
163
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 threads.emplace_back([&, start, end]() {
164
2/2
✓ Branch 0 taken 1184 times.
✓ Branch 1 taken 32 times.
1216 for (size_t i = start; i < end; ++i) {
165
2/2
✓ Branch 0 taken 913 times.
✓ Branch 1 taken 271 times.
2097 output[i] = (magnitude[i] * scale > static_cast<float>(threshold)) ? 255 : 0;
166 }
167 32 });
168 }
169
170
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 8 times.
40 for (auto &thread : threads) {
171
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
32 thread.join();
172 }
173 8 }
174
175
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 10 times.
20 bool BadanovASelectEdgeSobelALL::RunImpl() {
176 const auto &input = GetInput();
177 auto &output = GetOutput();
178
179
3/4
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
20 if (height_ < 3 || width_ < 3) {
180 10 output = input;
181 10 return true;
182 }
183
184
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (input.empty()) {
185 output.clear();
186 return true;
187 }
188
189 10 std::vector<float> magnitude(input.size(), 0.0F);
190 10 float max_magnitude = 0.0F;
191
192
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 ApplySobelOperator(input, magnitude, max_magnitude);
193
194 10 float global_max = 0.0F;
195
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 MPI_Allreduce(&max_magnitude, &global_max, 1, MPI_FLOAT, MPI_MAX, MPI_COMM_WORLD);
196
197 10 std::vector<float> recv_buffer;
198
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
10 if (rank_ == 0) {
199
1/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
5 recv_buffer.assign(magnitude.size(), 0.0F);
200 }
201
202
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 int magnitude_size = static_cast<int>(magnitude.size());
203
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 MPI_Reduce(magnitude.data(), recv_buffer.data(), magnitude_size, MPI_FLOAT, MPI_MAX, 0, MPI_COMM_WORLD);
204
205
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
10 if (rank_ == 0) {
206
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 ApplyThreshold(recv_buffer, global_max, output);
207 } else {
208
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 ApplyThreshold(magnitude, global_max, output);
209 }
210
211 10 int output_size = static_cast<int>(output.size());
212
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 MPI_Bcast(output.data(), output_size, MPI_BYTE, 0, MPI_COMM_WORLD);
213
214 return true;
215 }
216
217 20 bool BadanovASelectEdgeSobelALL::PostProcessingImpl() {
218 20 return true;
219 }
220
221 } // namespace badanov_a_select_edge_sobel
222