// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner // under NSF AWARD 1414736 and by the respective contributors. // All rights reserved. // // SPDX-License-Identifier: BSD-3-Clause #pragma once // On GCC < 4.8, the following define is often missing. Due to the // fact that this library only uses sleep_for, this should be safe #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5 && __GNUC_MINOR__ < 8 #define _GLIBCXX_USE_NANOSLEEP #endif #include #include // NOLINT(build/c++11) #include #include #include #include namespace CLI { /// This is a simple timer with pretty printing. Creating the timer starts counting. class Timer { protected: /// This is a typedef to make clocks easier to use using clock = std::chrono::steady_clock; /// This typedef is for points in time using time_point = std::chrono::time_point; /// This is the type of a printing function, you can make your own using time_print_t = std::function; /// This is the title of the timer std::string title_; /// This is the function that is used to format most of the timing message time_print_t time_print_; /// This is the starting point (when the timer was created) time_point start_; /// This is the number of times cycles (print divides by this number) std::size_t cycles{1}; public: /// Standard print function, this one is set by default static std::string Simple(std::string title, std::string time) { return title + ": " + time; } /// This is a fancy print function with --- headers static std::string Big(std::string title, std::string time) { return std::string("-----------------------------------------\n") + "| " + title + " | Time = " + time + "\n" + "-----------------------------------------"; } public: /// Standard constructor, can set title and print function explicit Timer(std::string title = "Timer", time_print_t time_print = Simple) : title_(std::move(title)), time_print_(std::move(time_print)), start_(clock::now()) {} /// Time a function by running it multiple times. Target time is the len to target. std::string time_it(std::function f, double target_time = 1) { time_point start = start_; double total_time; start_ = clock::now(); std::size_t n = 0; do { f(); std::chrono::duration elapsed = clock::now() - start_; total_time = elapsed.count(); } while(n++ < 100u && total_time < target_time); std::string out = make_time_str(total_time / static_cast(n)) + " for " + std::to_string(n) + " tries"; start_ = start; return out; } /// This formats the numerical value for the time string std::string make_time_str() const { time_point stop = clock::now(); std::chrono::duration elapsed = stop - start_; double time = elapsed.count() / static_cast(cycles); return make_time_str(time); } // LCOV_EXCL_START /// This prints out a time string from a time std::string make_time_str(double time) const { auto print_it = [](double x, std::string unit) { const unsigned int buffer_length = 50; std::array buffer; std::snprintf(buffer.data(), buffer_length, "%.5g", x); return buffer.data() + std::string(" ") + unit; }; if(time < .000001) return print_it(time * 1000000000, "ns"); else if(time < .001) return print_it(time * 1000000, "us"); else if(time < 1) return print_it(time * 1000, "ms"); else return print_it(time, "s"); } // LCOV_EXCL_STOP /// This is the main function, it creates a string std::string to_string() const { return time_print_(title_, make_time_str()); } /// Division sets the number of cycles to divide by (no graphical change) Timer &operator/(std::size_t val) { cycles = val; return *this; } }; /// This class prints out the time upon destruction class AutoTimer : public Timer { public: /// Reimplementing the constructor is required in GCC 4.7 explicit AutoTimer(std::string title = "Timer", time_print_t time_print = Simple) : Timer(title, time_print) {} // GCC 4.7 does not support using inheriting constructors. /// This destructor prints the string ~AutoTimer() { std::cout << to_string() << std::endl; } }; } // namespace CLI /// This prints out the time if shifted into a std::cout like stream. inline std::ostream &operator<<(std::ostream &in, const CLI::Timer &timer) { return in << timer.to_string(); }