summaryrefslogtreecommitdiffstats
path: root/src/third-party/CLI/Timer.hpp
blob: e97b17cf494bc3e94c92ed05a1b05efbece50763 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Copyright (c) 2017-2022, 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 <array>
#include <chrono>
#include <functional>
#include <iostream>
#include <string>
#include <utility>

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<clock>;

    /// This is the type of a printing function, you can make your own
    using time_print_t = std::function<std::string(std::string, std::string)>;

    /// 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<void()> 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<double> 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<double>(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<double> elapsed = stop - start_;
        double time = elapsed.count() / static_cast<double>(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<char, buffer_length> 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(); }