GCC Code Coverage Report


Directory: ./
File: tasks/batkov_f_contrast_enh_lin_hist_stretch/all/src/ops_all.cpp
Date: 2026-06-04 20:25:32
Exec Total Coverage
Lines: 102 124 82.3%
Functions: 10 11 90.9%
Branches: 65 120 54.2%

Line Branch Exec Source
1 #include "batkov_f_contrast_enh_lin_hist_stretch/all/include/ops_all.hpp"
2
3 #include <mpi.h>
4
5 #include <algorithm>
6 #include <array>
7 #include <cstddef>
8 #include <cstdint>
9 #include <limits>
10 #include <thread>
11 #include <utility>
12 #include <vector>
13
14 #include "batkov_f_contrast_enh_lin_hist_stretch/common/include/common.hpp"
15 #include "util/include/util.hpp"
16
17 namespace batkov_f_contrast_enh_lin_hist_stretch {
18
19 namespace {
20
21 12 void BuildScatterLayout(size_t comm_size, size_t image_size, std::vector<int> &sendcounts, std::vector<int> &displs) {
22 12 sendcounts.resize(comm_size);
23 12 displs.resize(comm_size);
24
25 size_t offset = 0;
26 12 const size_t base = image_size / comm_size;
27 12 const size_t rem = image_size % comm_size;
28
29
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 12 times.
36 for (size_t rank = 0; rank < comm_size; ++rank) {
30
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
48 sendcounts[rank] = static_cast<int>(base + (rank < rem ? 1 : 0));
31 24 displs[rank] = static_cast<int>(offset);
32
33 24 offset += sendcounts[rank];
34 }
35 12 }
36
37 6 std::pair<uint8_t, uint8_t> FindMinMaxParallel(const InType &chunk, size_t num_threads) {
38 const size_t chunk_size = chunk.size();
39 6 const size_t block = chunk_size / num_threads;
40
41 6 std::vector<uint8_t> mins(num_threads, std::numeric_limits<uint8_t>::max());
42
1/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
6 std::vector<uint8_t> maxs(num_threads, std::numeric_limits<uint8_t>::min());
43
44 6 std::vector<std::thread> threads;
45
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 threads.reserve(num_threads);
46
47
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
18 for (size_t thread_index = 0; thread_index < num_threads; ++thread_index) {
48 12 const size_t begin = thread_index * block;
49
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 const size_t end = (thread_index == num_threads - 1) ? chunk_size : begin + block;
50
51
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 threads.emplace_back([&, thread_index, begin, end]() {
52 12 uint8_t local_min = std::numeric_limits<uint8_t>::max();
53 12 uint8_t local_max = std::numeric_limits<uint8_t>::min();
54
55
2/2
✓ Branch 0 taken 1512144 times.
✓ Branch 1 taken 12 times.
1512156 for (size_t i = begin; i < end; ++i) {
56
4/4
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 1512086 times.
✓ Branch 2 taken 46 times.
✓ Branch 3 taken 1512098 times.
1512202 local_min = std::min(local_min, chunk[i]);
57 1512144 local_max = std::max(local_max, chunk[i]);
58 }
59
60 12 mins[thread_index] = local_min;
61 12 maxs[thread_index] = local_max;
62 12 });
63 }
64
65
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
18 for (auto &th : threads) {
66
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 th.join();
67 }
68
69 6 return {*std::ranges::min_element(mins), *std::ranges::max_element(maxs)};
70 6 }
71
72
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 std::pair<uint8_t, uint8_t> FindLocalMinMax(const InType &chunk, size_t parallel_threshold, size_t num_threads) {
73
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (chunk.empty()) {
74 return {std::numeric_limits<uint8_t>::max(), std::numeric_limits<uint8_t>::min()};
75 }
76
77
3/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
12 if (chunk.size() < parallel_threshold || num_threads <= 1) {
78 const auto [min_it, max_it] = std::ranges::minmax_element(chunk);
79 6 return {*min_it, *max_it};
80 }
81
82 6 return FindMinMaxParallel(chunk, num_threads);
83 }
84
85 std::array<uint8_t, 256> BuildStretchLut(float coeff_a, float coeff_b) {
86 12 std::array<uint8_t, 256> lut{};
87
2/2
✓ Branch 0 taken 3072 times.
✓ Branch 1 taken 12 times.
3084 for (size_t pixel = 0; pixel < 256; ++pixel) {
88 3072 lut.at(pixel) = static_cast<uint8_t>(std::clamp((coeff_a * static_cast<float>(pixel)) + coeff_b, 0.0F, 255.0F));
89 }
90
91 return lut;
92 }
93
94 12 void ApplyStretchParallel(const InType &chunk_in, OutType &chunk_out, size_t num_threads,
95 const std::array<uint8_t, 256> &lut) {
96 const size_t n = chunk_in.size();
97 12 chunk_out.resize(n);
98
99
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (n == 0) {
100 return;
101 }
102
103
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (num_threads <= 1) {
104 for (size_t i = 0; i < n; ++i) {
105 chunk_out[i] = lut.at(chunk_in[i]);
106 }
107
108 return;
109 }
110
111 12 const size_t block_size = n / num_threads;
112 12 std::vector<std::thread> threads;
113
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 threads.reserve(num_threads);
114
115
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 12 times.
36 for (size_t thread_index = 0; thread_index < num_threads; ++thread_index) {
116 24 const size_t begin = thread_index * block_size;
117
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 12 times.
24 const size_t end = (thread_index == num_threads - 1) ? n : begin + block_size;
118
119
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 threads.emplace_back([&, begin, end]() {
120
2/2
✓ Branch 0 taken 1590180 times.
✓ Branch 1 taken 24 times.
1590204 for (size_t i = begin; i < end; ++i) {
121 1590180 chunk_out[i] = lut.at(chunk_in[i]);
122 }
123 });
124 }
125
126
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 12 times.
36 for (auto &th : threads) {
127
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 th.join();
128 }
129 12 }
130
131 void CopyChunkParallel(const InType &chunk_in, OutType &chunk_out, size_t num_threads) {
132 const size_t n = chunk_in.size();
133 chunk_out.resize(n);
134 if (n == 0) {
135 return;
136 }
137
138 if (num_threads <= 1) {
139 std::ranges::copy(chunk_in, chunk_out.begin());
140 return;
141 }
142
143 const size_t block_size = n / num_threads;
144 std::vector<std::thread> threads;
145 threads.reserve(num_threads);
146
147 for (size_t thread_index = 0; thread_index < num_threads; ++thread_index) {
148 const size_t begin = thread_index * block_size;
149 const size_t end = (thread_index == num_threads - 1) ? n : begin + block_size;
150
151 threads.emplace_back([&, begin, end]() {
152 for (size_t i = begin; i < end; ++i) {
153 chunk_out[i] = chunk_in[i];
154 }
155 });
156 }
157
158 for (auto &th : threads) {
159 th.join();
160 }
161 }
162
163 } // namespace
164
165
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 BatkovFContrastEnhLinHistStretchALL::BatkovFContrastEnhLinHistStretchALL(const InType &in) {
166 SetTypeOfTask(GetStaticTypeOfTask());
167
168
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 MPI_Comm_rank(MPI_COMM_WORLD, &rank_);
169
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 MPI_Comm_size(MPI_COMM_WORLD, &comm_size_);
170
171
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if (rank_ == 0) {
172
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 GetInput() = in;
173 }
174 12 }
175
176 12 bool BatkovFContrastEnhLinHistStretchALL::ValidationImpl() {
177
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if (rank_ == 0) {
178 6 return !GetInput().empty();
179 }
180 6 return GetInput().empty();
181 }
182
183 12 bool BatkovFContrastEnhLinHistStretchALL::PreProcessingImpl() {
184 12 uint64_t input_size_u64 = 0;
185
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if (rank_ == 0) {
186 6 input_size_u64 = static_cast<uint64_t>(GetInput().size());
187 }
188 12 MPI_Bcast(&input_size_u64, 1, MPI_UINT64_T, 0, MPI_COMM_WORLD);
189
190
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (input_size_u64 > static_cast<uint64_t>(std::numeric_limits<std::ptrdiff_t>::max())) {
191 return false;
192 }
193
194 12 image_size_ = static_cast<size_t>(input_size_u64);
195 12 GetOutput().resize(image_size_);
196 12 return image_size_ > 0;
197 }
198
199 12 bool BatkovFContrastEnhLinHistStretchALL::RunImpl() {
200 12 const auto rank = static_cast<size_t>(rank_);
201 12 const auto comm_size = static_cast<size_t>(comm_size_);
202
203 12 std::vector<int> sendcounts;
204 12 std::vector<int> displs;
205
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 BuildScatterLayout(comm_size, image_size_, sendcounts, displs);
206
207 12 const int recvcount = sendcounts[rank];
208
1/4
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
12 std::vector<uint8_t> local_input(static_cast<size_t>(recvcount));
209 12 std::vector<uint8_t> local_output;
210
211
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 const uint8_t *sendbuf = (rank == 0) ? GetInput().data() : nullptr;
212
213
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 MPI_Scatterv(sendbuf, sendcounts.data(), displs.data(), MPI_UNSIGNED_CHAR, local_input.data(), recvcount,
214 MPI_UNSIGNED_CHAR, 0, MPI_COMM_WORLD);
215
216 constexpr size_t kParallelMinMaxThreshold = 100000;
217
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 const size_t num_threads = static_cast<size_t>(std::max(1, ppc::util::GetNumThreads()));
218
219
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 auto [local_min, local_max] = FindLocalMinMax(local_input, kParallelMinMaxThreshold, num_threads);
220
221 12 unsigned char global_min = 0;
222 12 unsigned char global_max = 0;
223
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 MPI_Allreduce(&local_min, &global_min, 1, MPI_UNSIGNED_CHAR, MPI_MIN, MPI_COMM_WORLD);
224
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 MPI_Allreduce(&local_max, &global_max, 1, MPI_UNSIGNED_CHAR, MPI_MAX, MPI_COMM_WORLD);
225
226
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (global_min == global_max) {
227 CopyChunkParallel(local_input, local_output, num_threads);
228 } else {
229 12 const float coeff_a = 255.0F / static_cast<float>(global_max - global_min);
230 12 const float coeff_b = -coeff_a * static_cast<float>(global_min);
231 const auto lut = BuildStretchLut(coeff_a, coeff_b);
232
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 ApplyStretchParallel(local_input, local_output, num_threads, lut);
233 }
234
235 auto &output = GetOutput();
236
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 uint8_t *gather_recv = (rank == 0) ? output.data() : nullptr;
237
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 MPI_Gatherv(local_output.data(), recvcount, MPI_UNSIGNED_CHAR, gather_recv, sendcounts.data(), displs.data(),
238 MPI_UNSIGNED_CHAR, 0, MPI_COMM_WORLD);
239
240
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 MPI_Bcast(output.data(), static_cast<int>(image_size_), MPI_UNSIGNED_CHAR, 0, MPI_COMM_WORLD);
241
242 12 return true;
243 }
244
245 12 bool BatkovFContrastEnhLinHistStretchALL::PostProcessingImpl() {
246 12 return !GetOutput().empty();
247 }
248
249 } // namespace batkov_f_contrast_enh_lin_hist_stretch
250