/* * (C) 2018 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #ifndef BOTAN_TIMER_H_ #define BOTAN_TIMER_H_ #include #include #include #include namespace Botan { class BOTAN_TEST_API Timer final { public: Timer(const std::string& name, const std::string& provider, const std::string& doing, uint64_t event_mult, size_t buf_size, double clock_cycle_ratio, uint64_t clock_speed) : m_name(name + ((provider.empty() || provider == "base") ? "" : " [" + provider + "]")) , m_doing(doing) , m_buf_size(buf_size) , m_event_mult(event_mult) , m_clock_cycle_ratio(clock_cycle_ratio) , m_clock_speed(clock_speed) {} Timer(const std::string& name, size_t buf_size = 0) : Timer(name, "", "", 1, buf_size, 0.0, 0) {} Timer(const Timer& other) = default; static uint64_t get_system_timestamp_ns() { return Botan::OS::get_system_timestamp_ns(); } static uint64_t get_cpu_cycle_counter() { return Botan::OS::get_processor_timestamp(); } void start() { stop(); m_timer_start = Timer::get_system_timestamp_ns(); m_cpu_cycles_start = Timer::get_cpu_cycle_counter(); } void stop(); bool under(std::chrono::milliseconds msec) { return (milliseconds() < msec.count()); } class Timer_Scope final { public: explicit Timer_Scope(Timer& timer) : m_timer(timer) { m_timer.start(); } ~Timer_Scope() { try { m_timer.stop(); } catch(...) {} } private: Timer& m_timer; }; template auto run(F f) -> decltype(f()) { Timer_Scope timer(*this); return f(); } template void run_until_elapsed(std::chrono::milliseconds msec, F f) { while(this->under(msec)) { run(f); } } uint64_t value() const { return m_time_used; } double seconds() const { return milliseconds() / 1000.0; } double milliseconds() const { return value() / 1000000.0; } double ms_per_event() const { return milliseconds() / events(); } uint64_t cycles_consumed() const { if(m_clock_speed != 0) { return static_cast((m_clock_speed * value()) / 1000.0); } return m_cpu_cycles_used; } uint64_t events() const { return m_event_count * m_event_mult; } const std::string& get_name() const { return m_name; } const std::string& doing() const { return m_doing; } size_t buf_size() const { return m_buf_size; } double bytes_per_second() const { return seconds() > 0.0 ? events() / seconds() : 0.0; } double events_per_second() const { return seconds() > 0.0 ? events() / seconds() : 0.0; } double seconds_per_event() const { return events() > 0 ? seconds() / events() : 0.0; } void set_custom_msg(const std::string& s) { m_custom_msg = s; } bool operator<(const Timer& other) const { if(this->doing() != other.doing()) return (this->doing() < other.doing()); return (this->get_name() < other.get_name()); } std::string to_string() const { if(m_custom_msg.size() > 0) { return m_custom_msg; } else if(this->buf_size() == 0) { return result_string_ops(); } else { return result_string_bps(); } } private: std::string result_string_bps() const; std::string result_string_ops() const; // const data std::string m_name, m_doing; size_t m_buf_size; uint64_t m_event_mult; double m_clock_cycle_ratio; uint64_t m_clock_speed; // set at runtime std::string m_custom_msg; uint64_t m_time_used = 0, m_timer_start = 0; uint64_t m_event_count = 0; uint64_t m_max_time = 0, m_min_time = 0; uint64_t m_cpu_cycles_start = 0, m_cpu_cycles_used = 0; }; } #endif