Directory: | cvmfs/ |
---|---|
File: | cvmfs/util/algorithm.cc |
Date: | 2025-06-22 02:36:02 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 54 | 151 | 35.8% |
Branches: | 17 | 158 | 10.8% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /** | ||
2 | * This file is part of the CernVM File System. | ||
3 | * | ||
4 | * Some common functions. | ||
5 | */ | ||
6 | |||
7 | #ifndef __STDC_FORMAT_MACROS | ||
8 | // NOLINTNEXTLINE | ||
9 | #define __STDC_FORMAT_MACROS | ||
10 | #endif | ||
11 | |||
12 | #include "util/algorithm.h" | ||
13 | |||
14 | #include <algorithm> | ||
15 | #include <cassert> | ||
16 | #include <cmath> | ||
17 | #include <cstdio> | ||
18 | #include <cstdlib> | ||
19 | #include <cstring> | ||
20 | |||
21 | #include "util/string.h" | ||
22 | |||
23 | |||
24 | #ifdef CVMFS_NAMESPACE_GUARD | ||
25 | namespace CVMFS_NAMESPACE_GUARD { | ||
26 | #endif | ||
27 | |||
28 | bool HighPrecisionTimer::g_is_enabled = false; | ||
29 | |||
30 | |||
31 | 251 | double DiffTimeSeconds(struct timeval start, struct timeval end) { | |
32 | // Time subtraction, from GCC documentation | ||
33 |
2/2✓ Branch 0 taken 38 times.
✓ Branch 1 taken 213 times.
|
251 | if (end.tv_usec < start.tv_usec) { |
34 | 38 | const int64_t nsec = (end.tv_usec - start.tv_usec) / 1000000 + 1; | |
35 | 38 | start.tv_usec -= 1000000 * nsec; | |
36 | 38 | start.tv_sec += nsec; | |
37 | } | ||
38 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 251 times.
|
251 | if (end.tv_usec - start.tv_usec > 1000000) { |
39 | ✗ | const int64_t nsec = (end.tv_usec - start.tv_usec) / 1000000; | |
40 | ✗ | start.tv_usec += 1000000 * nsec; | |
41 | ✗ | start.tv_sec -= nsec; | |
42 | } | ||
43 | |||
44 | // Compute the time remaining to wait in microseconds. | ||
45 | // tv_usec is certainly positive. | ||
46 | 251 | const uint64_t elapsed_usec = | |
47 | 251 | ((end.tv_sec - start.tv_sec) * 1000000) + (end.tv_usec - start.tv_usec); | |
48 | 251 | return static_cast<double>(elapsed_usec) / 1000000.0; | |
49 | } | ||
50 | |||
51 | |||
52 | 70 | void StopWatch::Start() { | |
53 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 70 times.
|
70 | assert(!running_); |
54 | |||
55 | 70 | gettimeofday(&start_, NULL); | |
56 | 70 | running_ = true; | |
57 | 70 | } | |
58 | |||
59 | |||
60 | 69 | void StopWatch::Stop() { | |
61 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 69 times.
|
69 | assert(running_); |
62 | |||
63 | 69 | gettimeofday(&end_, NULL); | |
64 | 69 | running_ = false; | |
65 | 69 | } | |
66 | |||
67 | |||
68 | 31 | void StopWatch::Reset() { | |
69 | 31 | start_ = timeval(); | |
70 | 31 | end_ = timeval(); | |
71 | 31 | running_ = false; | |
72 | 31 | } | |
73 | |||
74 | |||
75 | 137 | double StopWatch::GetTime() const { | |
76 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 137 times.
|
137 | assert(!running_); |
77 | |||
78 | 137 | return DiffTimeSeconds(start_, end_); | |
79 | } | ||
80 | |||
81 | namespace { | ||
82 | |||
83 | ✗ | static unsigned int CountDigits(uint64_t n) { | |
84 | ✗ | if (n == 0) { | |
85 | ✗ | return 1; | |
86 | } | ||
87 | ✗ | return static_cast<unsigned int>(floor(log10(static_cast<double>(n)))) + 1; | |
88 | } | ||
89 | |||
90 | ✗ | static std::string GenerateStars(unsigned int n) { return std::string(n, '*'); } | |
91 | |||
92 | } // anonymous namespace | ||
93 | |||
94 | 35566 | Log2Histogram::Log2Histogram(unsigned int nbins) { | |
95 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 35566 times.
|
35566 | assert(nbins != 0); |
96 |
1/2✓ Branch 1 taken 35566 times.
✗ Branch 2 not taken.
|
35566 | this->bins_.assign(nbins + 1, 0); // +1 for overflow bin. |
97 |
1/2✓ Branch 1 taken 35566 times.
✗ Branch 2 not taken.
|
35566 | this->boundary_values_.assign(nbins + 1, 0); // +1 to avoid big if statement |
98 | |||
99 | unsigned int i; | ||
100 |
2/2✓ Branch 0 taken 1065060 times.
✓ Branch 1 taken 35566 times.
|
1100626 | for (i = 1; i <= nbins; i++) { |
101 | 1065060 | this->boundary_values_[i] = (1 << ((i - 1) + 1)); | |
102 | } | ||
103 | 35566 | } | |
104 | |||
105 | 60 | std::vector<atomic_int32> UTLog2Histogram::GetBins(const Log2Histogram &h) { | |
106 | 60 | return h.bins_; | |
107 | } | ||
108 | |||
109 | 260 | unsigned int Log2Histogram::GetQuantile(float n) { | |
110 | 260 | const uint64_t total = this->N(); | |
111 | // pivot is the index of the element corresponding to the requested quantile | ||
112 | 260 | uint64_t pivot = static_cast<uint64_t>(static_cast<float>(total) * n); | |
113 | 260 | float normalized_pivot = 0.0; | |
114 | // now we iterate through all the bins | ||
115 | // note that we _exclude_ the overflow bin | ||
116 | 260 | unsigned int i = 0; | |
117 |
2/2✓ Branch 1 taken 3760 times.
✓ Branch 2 taken 20 times.
|
3780 | for (i = 1; i <= this->bins_.size() - 1; i++) { |
118 | const unsigned int bin_value = | ||
119 | 3760 | static_cast<unsigned int>(atomic_read32(&(this->bins_[i]))); | |
120 |
2/2✓ Branch 0 taken 240 times.
✓ Branch 1 taken 3520 times.
|
3760 | if (pivot <= bin_value) { |
121 | 240 | normalized_pivot = static_cast<float>(pivot) | |
122 | 240 | / static_cast<float>(bin_value); | |
123 | 240 | break; | |
124 | } | ||
125 | 3520 | pivot -= bin_value; | |
126 | } | ||
127 |
2/2✓ Branch 1 taken 20 times.
✓ Branch 2 taken 240 times.
|
260 | if (i >= this->bins_.size()) { |
128 | 20 | return this->boundary_values_[this->bins_.size() - 1]; | |
129 | } | ||
130 | // now i stores the index of the bin corresponding to the requested quantile | ||
131 | // and normalized_pivot is the element we want inside the bin | ||
132 | 240 | const unsigned int min_value = this->boundary_values_[i - 1]; | |
133 | 240 | const unsigned int max_value = this->boundary_values_[i]; | |
134 | // and we return the linear interpolation | ||
135 | return min_value | ||
136 | 240 | + static_cast<unsigned int>(static_cast<float>(max_value - min_value) | |
137 | 240 | * normalized_pivot); | |
138 | } | ||
139 | |||
140 | ✗ | std::string Log2Histogram::ToString() { | |
141 | ✗ | unsigned int i = 0; | |
142 | |||
143 | ✗ | unsigned int max_left_boundary_count = 1; | |
144 | ✗ | unsigned int max_right_boundary_count = 1; | |
145 | ✗ | unsigned int max_value_count = 1; | |
146 | ✗ | unsigned int max_stars = 0; | |
147 | ✗ | unsigned int max_bins = 0; | |
148 | ✗ | const unsigned int total_stars = 38; | |
149 | ✗ | uint64_t total_sum_of_bins = 0; | |
150 | |||
151 | ✗ | for (i = 1; i <= this->bins_.size() - 1; i++) { | |
152 | ✗ | max_left_boundary_count = std::max(max_left_boundary_count, | |
153 | ✗ | CountDigits(boundary_values_[i] / 2)); | |
154 | ✗ | max_right_boundary_count = std::max(max_right_boundary_count, | |
155 | ✗ | CountDigits(boundary_values_[i] - 1)); | |
156 | ✗ | max_value_count = std::max(max_value_count, | |
157 | ✗ | CountDigits(atomic_read32(&(this->bins_[i])))); | |
158 | ✗ | max_bins = std::max( | |
159 | ✗ | max_bins, static_cast<unsigned int>(atomic_read32(&(this->bins_[i])))); | |
160 | ✗ | total_sum_of_bins += static_cast<unsigned int>( | |
161 | ✗ | atomic_read32(&(this->bins_[i]))); | |
162 | } | ||
163 | |||
164 | ✗ | max_bins = std::max( | |
165 | ✗ | max_bins, static_cast<unsigned int>(atomic_read32(&(this->bins_[0])))); | |
166 | ✗ | total_sum_of_bins += static_cast<unsigned int>( | |
167 | ✗ | atomic_read32(&(this->bins_[0]))); | |
168 | |||
169 | ✗ | if (total_sum_of_bins != 0) { | |
170 | ✗ | max_stars = max_bins * total_stars / total_sum_of_bins; | |
171 | } | ||
172 | |||
173 | const std::string format = | ||
174 | ✗ | " %" + | |
175 | ✗ | StringifyUint(max_left_boundary_count < 2 ? 2 : max_left_boundary_count) + | |
176 | ✗ | "d -> %" + StringifyUint(max_right_boundary_count) + "d : %" + | |
177 | ✗ | StringifyUint(max_value_count) + "d | %" + | |
178 | ✗ | StringifyUint(max_stars < 12 ? 12 : max_stars) + "s |\n"; | |
179 | |||
180 | const std::string title_format = | ||
181 | ✗ | " %" + | |
182 | ✗ | StringifyUint( | |
183 | ✗ | (max_left_boundary_count < 2 ? 2 : max_left_boundary_count) + | |
184 | ✗ | max_right_boundary_count + 4) + | |
185 | ✗ | "s | %" + StringifyUint(max_value_count + 4) + "s | %" + | |
186 | ✗ | StringifyUint(max_stars < 12 ? 12 : max_stars) + "s |\n"; | |
187 | |||
188 | const std::string overflow_format = | ||
189 | ✗ | "%" + | |
190 | ✗ | StringifyUint(max_left_boundary_count + max_right_boundary_count + 5) + | |
191 | ✗ | "s : %" + StringifyUint(max_value_count + 4) + "d | %" + | |
192 | ✗ | StringifyUint(max_stars < 12 ? 12 : max_stars) + "s |\n"; | |
193 | |||
194 | const std::string total_format = | ||
195 | ✗ | "%" + | |
196 | ✗ | StringifyUint(max_left_boundary_count + max_right_boundary_count + 5 < 8 | |
197 | ✗ | ? 8 | |
198 | : max_left_boundary_count + max_right_boundary_count + | ||
199 | ✗ | 5) + | |
200 | ✗ | "s : %" + StringifyUint(max_value_count + 4) + "lld\n"; | |
201 | |||
202 | ✗ | std::string result_string = ""; | |
203 | |||
204 | ✗ | const unsigned int kBufSize = 300; | |
205 | char buffer[kBufSize]; | ||
206 | ✗ | memset(buffer, 0, sizeof(buffer)); | |
207 | |||
208 | ✗ | snprintf( | |
209 | buffer, kBufSize, title_format.c_str(), "nsec", "count", "distribution"); | ||
210 | ✗ | result_string += buffer; | |
211 | ✗ | memset(buffer, 0, sizeof(buffer)); | |
212 | |||
213 | ✗ | for (i = 1; i <= this->bins_.size() - 1; i++) { | |
214 | ✗ | unsigned int n_of_stars = 0; | |
215 | ✗ | if (total_sum_of_bins != 0) { | |
216 | ✗ | n_of_stars = static_cast<unsigned int>(atomic_read32(&(this->bins_[i]))) | |
217 | ✗ | * total_stars / total_sum_of_bins; | |
218 | } | ||
219 | |||
220 | ✗ | snprintf(buffer, | |
221 | kBufSize, | ||
222 | format.c_str(), | ||
223 | ✗ | boundary_values_[i - 1], | |
224 | ✗ | boundary_values_[i] - 1, | |
225 | ✗ | static_cast<unsigned int>(atomic_read32(&this->bins_[i])), | |
226 | ✗ | GenerateStars(n_of_stars).c_str()); | |
227 | ✗ | result_string += buffer; | |
228 | ✗ | memset(buffer, 0, sizeof(buffer)); | |
229 | } | ||
230 | |||
231 | ✗ | unsigned int n_of_stars = 0; | |
232 | ✗ | if (total_sum_of_bins != 0) { | |
233 | ✗ | n_of_stars = static_cast<unsigned int>(atomic_read32(&(this->bins_[0]))) | |
234 | ✗ | * total_stars / total_sum_of_bins; | |
235 | } | ||
236 | |||
237 | ✗ | snprintf(buffer, | |
238 | kBufSize, | ||
239 | overflow_format.c_str(), | ||
240 | "overflow", | ||
241 | ✗ | static_cast<unsigned int>(atomic_read32(&(this->bins_[0]))), | |
242 | ✗ | GenerateStars(n_of_stars).c_str()); | |
243 | ✗ | result_string += buffer; | |
244 | ✗ | memset(buffer, 0, sizeof(buffer)); | |
245 | |||
246 | ✗ | snprintf(buffer, kBufSize, total_format.c_str(), "total", total_sum_of_bins); | |
247 | ✗ | result_string += buffer; | |
248 | ✗ | memset(buffer, 0, sizeof(buffer)); | |
249 | |||
250 | const float qs[15] = {.1, .2, .25, .3, .4, .5, .6, .7, | ||
251 | .75, .8, .9, .95, .99, .995, .999}; | ||
252 | ✗ | snprintf(buffer, kBufSize, | |
253 | "\n\nQuantiles\n" | ||
254 | "%0.4f,%0.4f,%0.4f,%0.4f,%0.4f,%0.4f,%0.4f,%0.4f,%0.4f,%0.4f,%0.4f," | ||
255 | "%0.4f,%0.4f,%0.4f,%0.4f\n" | ||
256 | "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n" | ||
257 | "End Quantiles" | ||
258 | "\n-----------------------\n", | ||
259 | ✗ | qs[0], qs[1], qs[2], qs[3], qs[4], qs[5], qs[6], qs[7], qs[8], qs[9], | |
260 | ✗ | qs[10], qs[11], qs[12], qs[13], qs[14], GetQuantile(qs[0]), | |
261 | ✗ | GetQuantile(qs[1]), GetQuantile(qs[2]), GetQuantile(qs[3]), | |
262 | ✗ | GetQuantile(qs[4]), GetQuantile(qs[5]), GetQuantile(qs[6]), | |
263 | ✗ | GetQuantile(qs[7]), GetQuantile(qs[8]), GetQuantile(qs[9]), | |
264 | ✗ | GetQuantile(qs[10]), GetQuantile(qs[11]), GetQuantile(qs[12]), | |
265 | ✗ | GetQuantile(qs[13]), GetQuantile(qs[14])); | |
266 | |||
267 | ✗ | result_string += buffer; | |
268 | ✗ | memset(buffer, 0, sizeof(buffer)); | |
269 | |||
270 | ✗ | return result_string; | |
271 | } | ||
272 | |||
273 | ✗ | void Log2Histogram::PrintLog2Histogram() { | |
274 | ✗ | printf("%s", this->ToString().c_str()); | |
275 | } | ||
276 | |||
277 | #ifdef CVMFS_NAMESPACE_GUARD | ||
278 | } // namespace CVMFS_NAMESPACE_GUARD | ||
279 | #endif | ||
280 |