11#include <promeki/config.h>
12#if PROMEKI_ENABLE_CORE
25PROMEKI_NAMESPACE_BEGIN
78 Iterator(BenchmarkState *state, uint64_t pos) : _state(state), _pos(pos) {}
80 uint64_t operator*()
const {
return _pos; }
82 Iterator &operator++() {
84 if (_state !=
nullptr && _pos >= _state->_iterations) {
85 _state->finishTiming();
90 bool operator!=(
const Iterator &other)
const {
return _pos != other._pos; }
93 BenchmarkState *_state;
98 explicit BenchmarkState(uint64_t iterations) : _iterations(iterations) {}
101 uint64_t iterations()
const {
return _iterations; }
108 ensureTimerStarted();
109 return Iterator(
this, 0);
113 Iterator end() {
return Iterator(
this, _iterations); }
126 if (_counter == 0) ensureTimerStarted();
127 if (_counter >= _iterations) {
143 if (!_timerStarted || _paused)
return;
145 _pauseStartNs = _timer.elapsedNs();
150 void resumeTiming() {
151 if (!_paused)
return;
153 _pausedNs += _timer.elapsedNs() - _pauseStartNs;
166 void setItemsProcessed(uint64_t n) { _itemsProcessed = n; }
175 void setBytesProcessed(uint64_t n) { _bytesProcessed = n; }
181 void setLabel(
const String &label) { _label = label; }
194 void setCounter(
const String &key,
double value) {
195 _counters.insert(key, value);
200 const String &label()
const {
return _label; }
203 uint64_t itemsProcessed()
const {
return _itemsProcessed; }
206 uint64_t bytesProcessed()
const {
return _bytesProcessed; }
209 const Map<String, double> &counters()
const {
return _counters; }
215 int64_t effectiveNs()
const {
return _effectiveNs; }
229 int64_t wallNs()
const {
return _wallNs; }
237 bool completed()
const {
return _completed; }
240 friend class BenchmarkRunner;
242 void ensureTimerStarted() {
243 if (_timerStarted)
return;
244 _timerStarted =
true;
248 void finishTiming() {
249 if (_completed)
return;
252 _pausedNs += _timer.elapsedNs() - _pauseStartNs;
255 _wallNs = _timer.elapsedNs();
256 _effectiveNs = _wallNs - _pausedNs;
257 if (_effectiveNs < 0) _effectiveNs = 0;
262 void captureIfUnfinished() {
265 if (_completed)
return;
274 uint64_t _iterations = 0;
275 uint64_t _counter = 0;
276 bool _timerStarted =
false;
277 bool _paused =
false;
278 bool _completed =
false;
280 int64_t _pauseStartNs = 0;
281 int64_t _pausedNs = 0;
283 int64_t _effectiveNs = 0;
284 uint64_t _itemsProcessed = 0;
285 uint64_t _bytesProcessed = 0;
287 Map<String, double> _counters;
299class BenchmarkResult {
310 uint64_t iterations = 0;
312 uint64_t repeats = 0;
314 double avgNsPerIter = 0.0;
316 double minNsPerIter = 0.0;
318 double maxNsPerIter = 0.0;
320 double stddevNsPerIter = 0.0;
322 double itemsPerSecond = 0.0;
324 double bytesPerSecond = 0.0;
326 Map<String, double> custom;
328 bool succeeded =
true;
333 JsonObject toJson()
const;
341 static BenchmarkResult fromJson(
const JsonObject &obj, Error *err =
nullptr);
356 using Function = promeki::Function<void(BenchmarkState &)>;
365 BenchmarkCase(
const String &suite,
const String &name,
const String &description, Function fn)
366 : _suite(suite), _name(name), _description(description), _fn(std::move(fn)) {}
369 const String &suite()
const {
return _suite; }
372 const String &name()
const {
return _name; }
375 const String &description()
const {
return _description; }
378 String fullName()
const {
return _suite +
"." + _name; }
381 void invoke(BenchmarkState &state)
const { _fn(state); }
415class BenchmarkRunner {
427 static int registerCase(
const BenchmarkCase &theCase);
430 static const List<BenchmarkCase> ®isteredCases();
446 static String formatRegisteredCases();
455 void setMinTimeMs(
unsigned int ms) { _minTimeMs = ms; }
461 void setWarmupMs(
unsigned int ms) { _warmupMs = ms; }
467 void setMinIterations(uint64_t n) { _minIterations = n; }
473 void setMaxIterations(uint64_t n) { _maxIterations = n; }
483 void setRepeats(
unsigned int n) { _repeats = n == 0 ? 1 : n; }
494 void setFilter(
const String &pattern) { _filterPattern = pattern; }
500 void setVerbose(
bool verbose) { _verbose = verbose; }
503 unsigned int minTimeMs()
const {
return _minTimeMs; }
506 unsigned int warmupMs()
const {
return _warmupMs; }
509 uint64_t minIterations()
const {
return _minIterations; }
512 uint64_t maxIterations()
const {
return _maxIterations; }
515 unsigned int repeats()
const {
return _repeats; }
518 const String &filter()
const {
return _filterPattern; }
521 const List<BenchmarkResult> &results()
const {
return _results; }
532 int filteredCaseCount()
const;
547 int64_t estimatedDurationMs()
const;
560 Error runCaseByName(
const String &fullName);
567 BenchmarkResult runCase(
const BenchmarkCase &theCase);
570 void clearResults() { _results.clear(); }
577 Error writeJson(
const String &path)
const;
587 JsonObject toJson()
const;
595 static List<BenchmarkResult> loadBaseline(
const String &path, Error *err =
nullptr);
604 String formatTable()
const;
610 String formatComparison(
const List<BenchmarkResult> &baseline)
const;
613 BenchmarkResult measureCase(
const BenchmarkCase &theCase);
614 uint64_t calibrateIterations(
const BenchmarkCase &theCase);
615 void runOne(
const BenchmarkCase &theCase, uint64_t iterations, BenchmarkState &state);
617 bool matchesFilter(
const BenchmarkCase &theCase)
const;
619 unsigned int _minTimeMs = 500;
620 unsigned int _warmupMs = 100;
621 uint64_t _minIterations = 1;
622 uint64_t _maxIterations = 1000000000ULL;
623 unsigned int _repeats = 1;
624 String _filterPattern;
625 bool _verbose =
false;
632 size_t _progressNameWidth = 0;
633 List<BenchmarkResult> _results;
655#define PROMEKI_REGISTER_BENCHMARK(Suite, Name, Description, Fn) \
656 [[maybe_unused]] static int PROMEKI_CONCAT(__promeki_benchmark_, PROMEKI_UNIQUE_ID) = \
657 promeki::BenchmarkRunner::registerCase(promeki::BenchmarkCase((Suite), (Name), (Description), (Fn)));