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