• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MikkelSchubert / adapterremoval / #80

06 Apr 2025 08:21PM UTC coverage: 27.836% (+0.1%) from 27.703%
#80

push

travis-ci

web-flow
improvements to utility functions (#104)

* move is_ascii_* helperfunction to strutils
* update join_text to support any containers
* use std::device_random to generate RNG seeds
* add function for getting an underlying enum value
* addition of / improvements to / tweaks to related tests

20 of 30 new or added lines in 9 files covered. (66.67%)

1 existing line in 1 file now uncovered.

2719 of 9768 relevant lines covered (27.84%)

4127.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/benchmarking.cpp
1
// SPDX-License-Identifier: GPL-3.0-or-later
2
// SPDX-FileCopyrightText: 2024 Mikkel Schubert <mikkelsch@gmail.com>
3
#include "benchmarking.hpp" // for benchmark_toggles, benchmarker
4
#include "debug.hpp"        // for AR_REQUIRE
5
#include "logging.hpp"      // for log
6
#include "mathutils.hpp"    // for arithmetic_mean, standard_deviation
7
#include "simd.hpp"         // for supported
8
#include "strutils.hpp"     // for to_lower
9
#include <algorithm>        // for find, accumulate
10
#include <array>            // for array
11
#include <cmath>            // for round
12
#include <iomanip>          // for fixed, setsetprecision
13
#include <iostream>         // for cout
14
#include <numeric>          // for accumulate
15

16
namespace adapterremoval {
17

18
namespace {
19

20
//! Number of loops to perform prior to benchmarking as burn-in
21
const size_t BENCHMARK_BURN_IN = 1;
22
//! Benchmarks must be repeated at least this number of times
23
const size_t BENCHMARK_MIN_LOOPS = 10;
24
//! Benchmarks must be repeated at most this number of times
25
const size_t BENCHMARK_MAX_LOOPS = 1000;
26
//! Benchmarks must run for at this this number of nano-seconds
27
const double BENCHMARK_MIN_TIME_NS = 5'000'000'000;
28
//! Benchmark loops shorter than this number of nano-seconds cannot be measured
29
const double BENCHMARK_CUTOFF_TIME_NS = 10'000;
30
//! Number of NS between terminal updates
31
const size_t BENCHMARK_UPDATE_INTERVAL = 50'000'000;
32

33
} // namespace
34

35
benchmark_toggles::benchmark_toggles(string_vec keys)
×
36
  : m_toggles(std::move(keys))
×
37
  , m_enabled(m_toggles.size(), false)
×
38
  , m_defaults()
×
39
{
40
}
41

42
bool
43
benchmark_toggles::update_toggles(const string_vec& keys)
×
44
{
45
  bool any_errors = false;
×
46
  m_defaults = keys.empty();
×
47
  for (const auto& key : keys) {
×
48
    const auto it =
×
49
      std::find(m_toggles.begin(), m_toggles.end(), to_lower(key));
×
50

51
    if (it == m_toggles.end()) {
×
52
      log::error() << "Unknown benchmarking toggle '" << key << "'";
×
53
      any_errors = true;
×
54
    } else {
55
      m_enabled.at(it - m_toggles.begin()) = true;
×
56
    }
57
  }
58

59
  if (any_errors) {
×
60
    auto msg = log::error();
×
NEW
61
    msg << "Valid toggles are " << join_text(m_toggles, ", ", ", and ");
×
62
  }
63

64
  return !any_errors;
×
65
}
66

67
bool
68
benchmark_toggles::is_set(const std::string& key) const
×
69
{
70
  const auto it = std::find(m_toggles.begin(), m_toggles.end(), to_lower(key));
×
71

72
  AR_REQUIRE(it != m_toggles.end());
×
73
  return m_enabled.at(it - m_toggles.begin());
×
74
}
75

76
benchmarker::benchmarker(std::string desc, string_vec toggles)
×
77
  : m_description(std::move(desc))
×
78
  , m_toggles(std::move(toggles))
×
79
{
80
}
81

82
void
83
benchmarker::run_if_toggled(const benchmark_toggles& toggles)
×
84
{
85
  const auto s = enabled(toggles);
×
86
  if (s != strategy::skip) {
×
87
    run(s);
×
88
  }
89
}
90

91
/** Called before `setup` to perform any per batch setup */
92
void
93
benchmarker::setup() {};
×
94

95
strategy
96
benchmarker::enabled(const benchmark_toggles& toggles) const
×
97
{
98
  if (toggles.defaults()) {
×
99
    return strategy::benchmark;
100
  }
101

102
  for (const auto& toggle : m_toggles) {
×
103
    if (toggles.is_set(toggle)) {
×
104
      return strategy::benchmark;
×
105
    }
106
  }
107

108
  return m_required ? strategy::passthrough : strategy::skip;
×
109
}
110

111
void
112
benchmarker::run(const strategy s)
×
113
{
114
  static bool header = false;
115
  if (!header) {
×
116
    std::cout << "             Benchmark |       Min |      Mean |       Max | "
×
117
                 "SD (%) | Loops | Outliers"
×
118
              << std::endl;
×
119
    header = true;
×
120
  }
121

122
  if (s != strategy::passthrough) {
×
123
    log::cerr() << "\r\033[K" << std::setw(22) << m_description << " burn-in";
×
124

125
    for (size_t i = 1; i <= BENCHMARK_BURN_IN; ++i) {
×
126
      setup();
×
127
      execute();
×
128

129
      log::cerr() << ".";
×
130
    }
131
  } else {
132
    log::cerr() << "\r\033[K" << std::setw(22) << m_description << " (setup)";
×
133
  }
134

135
  size_t loops = 0;
136
  uint64_t next_update = 0;
137
  do {
×
138
    uint64_t elapsed =
×
139
      std::accumulate(m_durations.begin(), m_durations.end(), uint64_t());
×
140

141
    do {
×
142
      setup();
×
143
      const auto start = clock::now();
×
144
      execute();
×
145
      const auto duration = (clock::now() - start).count();
×
146

147
      loops++;
×
148
      elapsed += duration;
×
149
      m_durations.push_back(duration);
×
150

151
      if (elapsed >= next_update) {
×
152
        log::cerr() << "\r\033[K" << summarize(loops);
×
153
        next_update += BENCHMARK_UPDATE_INTERVAL;
×
154
      }
155
    } while (s == strategy::benchmark &&
×
156
             m_durations.size() < BENCHMARK_MAX_LOOPS &&
×
157
             (m_durations.size() < BENCHMARK_MIN_LOOPS ||
×
158
              (elapsed < BENCHMARK_MIN_TIME_NS &&
×
159
               elapsed / m_durations.size() >= BENCHMARK_CUTOFF_TIME_NS)));
×
160
  } while (s == strategy::benchmark && (loops < 2 * m_durations.size()) &&
×
161
           grubbs_test_prune(m_durations));
×
162

163
  log::cerr() << "\r\033[K";
×
164

165
  if (s != strategy::passthrough) {
×
166
    std::cout << summarize(loops) << std::endl;
×
167
  }
168
}
169

170
std::string
171
benchmarker::summarize(size_t loops) const
×
172
{
173
  const std::array<size_t, 7> COLUMN_WIDTHS{ 22, 9, 9, 9, 6, 5, 8 };
×
174
  std::array<std::string, COLUMN_WIDTHS.size()> values{
×
175
    m_description,
×
176
    "",
177
    "",
178
    "",
179
    "",
180
    std::to_string(m_durations.size()),
×
181
    std::to_string(loops - m_durations.size()),
×
182
  };
183

184
  if (!m_durations.empty()) {
×
185
    const auto min_max =
×
186
      std::minmax_element(m_durations.begin(), m_durations.end());
×
187
    const auto mean = arithmetic_mean(m_durations);
×
188

189
    values.at(1) = format_thousand_sep(*min_max.first / 1e3);
×
190
    values.at(2) = format_thousand_sep(std::round(mean / 1e3));
×
191
    values.at(3) = format_thousand_sep(*min_max.second / 1e3);
×
192

193
    if (m_durations.size() > 1) {
×
194
      const auto sd = standard_deviation(m_durations);
×
195
      values.at(4) = format_fraction(1e9 * sd, 1e7 * mean);
×
196
    }
197
  }
198

199
  std::ostringstream ss;
×
200
  for (size_t i = 0; i < values.size(); ++i) {
×
201
    if (i) {
×
202
      ss << " | ";
×
203
    }
204

205
    ss << std::setw(COLUMN_WIDTHS.at(i)) << values.at(i);
×
206
  }
207

208
  return ss.str();
×
209
}
210

211
} // namespace adapterremoval
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc