Patrick Kelley 8fd444092b initial
2025-05-07 15:35:15 -04:00

154 lines
3.6 KiB
C++

// Copyright (c) 2020-now by the Zeek Project. See LICENSE for details.
/** API to measure execution times and frequency for code area. */
#pragma once
#include <cassert>
#include <chrono>
#include <list>
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
namespace hilti::util::timing {
using Clock = std::chrono::high_resolution_clock;
using Time = Clock::time_point;
using Duration = Clock::duration;
class Collector;
class Ledger;
namespace detail {
/** Singleton object managing all timer state. */
class Manager {
public:
/**
* Renders a summary of execution statistics for all currently existing
* `Ledger` objects.
*/
static void summary(std::ostream& out);
/**
* Returns a pointer to a global singleton manager instance. This returns
* a shared_ptr so that ledgers can store that to ensure the global
* singleton doesn't get destroyed at exit before they go away, too.
*/
static std::shared_ptr<Manager> singleton();
protected:
friend Collector;
friend Ledger;
Manager() : _created(Clock::now()) {}
void register_(Ledger* ledger);
void unregister(Ledger* ledger);
Ledger* newLedger(const std::string& name);
private:
Time _created;
std::unordered_map<std::string, Ledger*> _all_ledgers;
std::list<Ledger> _our_ledgers;
};
} // namespace detail
inline void summary(std::ostream& out) { detail::Manager::summary(out); }
/** Maintains measurements of execution time and frequency for one code area. */
class Ledger {
public:
Ledger(std::string name) : _manager(detail::Manager::singleton()), _name(std::move(name)) {
_manager->register_(this);
}
~Ledger() { _manager->unregister(this); }
Ledger() = delete;
Ledger(const Ledger&) = default;
Ledger(Ledger&&) noexcept = default;
Ledger& operator=(const Ledger&) = delete;
Ledger& operator=(Ledger&&) noexcept = delete;
const std::string& name() const { return _name; }
void summary(std::ostream& out) const;
protected:
friend class Collector;
friend class detail::Manager;
void start() {
if ( _level < 0 )
return;
if ( ++_level != 1 )
return;
assert(_time_started == Time());
_time_started = Clock::now();
}
void stop() {
if ( _level < 0 )
return;
assert(_level > 0);
if ( --_level != 0 )
return;
assert(_time_started != Time());
_time_used += (Clock::now() - _time_started);
_time_started = Time();
++_num_completed;
}
void finish() {
if ( _level > 0 ) {
_time_used += (Clock::now() - _time_started);
_time_started = Time();
++_num_completed;
}
_level = -1;
}
private:
std::shared_ptr<detail::Manager> _manager;
Time _time_started;
Duration _time_used = Duration(0);
uint64_t _num_completed = 0;
int64_t _level = 0;
std::string _name;
};
/** Measure a code block's execution during its life-time. */
class Collector {
public:
Collector(Ledger* ledger) : _ledger(ledger) { ledger->start(); }
Collector(const std::string& name) {
_ledger = detail::Manager::singleton()->newLedger(name);
_ledger->start();
}
~Collector() { _ledger->stop(); }
void finish() { _ledger->finish(); }
Collector() = delete;
Collector(const Collector&) = delete;
Collector(Collector&&) noexcept = delete;
Collector& operator=(const Collector&) = delete;
Collector& operator=(Collector&&) noexcept = delete;
private:
Ledger* _ledger;
};
} // namespace hilti::util::timing