1 // Copyright (C) 2011  Davis E. King (davis@dlib.net)
2 // License: Boost Software License   See LICENSE.txt for the full license.
3 #ifndef DLIB_TImING_Hh_
4 #define DLIB_TImING_Hh_
5 
6 #include <chrono>
7 #include <atomic>
8 #include <cstring>
9 #include "string.h"
10 
11 #include <iostream>
12 
13 // ----------------------------------------------------------------------------------------
14 
15 /*!A timing
16 
17     This set of functions is useful for determining how much time is spent
18     executing blocks of code.  Consider the following example:
19 
20     int main()
21     {
22         using namespace dlib::timing;
23         for (int i = 0; i < 10; ++i)
24         {
25             // timing block #1
26             start(1,"block #1");
27             dlib::sleep(500);
28             stop(1);
29 
30             // timing block #2
31             start(2,"block #2");
32             dlib::sleep(1000);
33             stop(2);
34         }
35 
36         print();
37     }
38 
39     This program would output:
40         Timing report:
41             block #1: 5.0 seconds
42             block #2: 10.0 seconds
43 
44     So we spent 5 seconds in block #1 and 10 seconds in block #2
45 
46 
47 
48     Additionally, note that you can use an RAII style timing block object.  For
49     example, if we wanted to find out how much time we spent in a loop a convenient
50     way to do this would be as follows:
51 
52     int main()
53     {
54         using namespace dlib::timing;
55         for (int i = 0; i < 10; ++i)
56         {
57             block tb(1, "main loop");
58 
59             dlib::sleep(1500);
60         }
61 
62         print();
63     }
64 
65     This program would output:
66         Timing report:
67             block main loop: 15.0 seconds
68 
69 !*/
70 
71 // ----------------------------------------------------------------------------------------
72 
73 namespace dlib
74 {
75     namespace timing
76     {
77         const int TIME_SLOTS = 500;
78         const int NAME_LENGTH = 40;
79 
time_buf()80         inline std::atomic<uint64_t>* time_buf()
81         {
82             static std::atomic<uint64_t> buf[TIME_SLOTS];
83             return buf;
84         }
85 
name_buf(int i,const char * name)86         inline char* name_buf(int i, const char* name)
87         {
88             static char buf[TIME_SLOTS][NAME_LENGTH] = {{0}};
89             // if this name buffer is empty then copy name into it
90             if (buf[i][0] == '\0')
91             {
92                 using namespace std;
93                 strncpy(buf[i], name, NAME_LENGTH-1);
94                 buf[i][NAME_LENGTH-1] = '\0';
95             }
96             // return the name buffer
97             return buf[i];
98         }
99 
ts()100         inline uint64_t ts()
101         {
102             using namespace std::chrono;
103             return duration_cast<duration<double,std::nano>>(high_resolution_clock::now().time_since_epoch()).count();
104         }
105 
start(int i)106         inline void start(int i)
107         {
108             time_buf()[i] -= ts();
109         }
110 
start(int i,const char * name)111         inline void start(int i, const char* name)
112         {
113             time_buf()[i] -= ts();
114             name_buf(i,name);
115         }
116 
stop(int i)117         inline void stop(int i)
118         {
119             time_buf()[i] += ts();
120         }
121 
print()122         inline void print()
123         {
124             using namespace std;
125             cout << "Timing report: " << endl;
126 
127             // figure out how long the longest name is going to be.
128             unsigned long max_name_length = 0;
129             for (int i = 0; i < TIME_SLOTS; ++i)
130             {
131                 string name;
132                 // Check if the name buffer is empty.  Use the name it contains if it isn't.
133                 if (name_buf(i,"")[0] != '\0')
134                     name = cast_to_string(i) + ": " + name_buf(i,"");
135                 else
136                     name = cast_to_string(i);
137                 max_name_length = std::max<unsigned long>(max_name_length, name.size());
138             }
139 
140             for (int i = 0; i < TIME_SLOTS; ++i)
141             {
142                 if (time_buf()[i] != 0)
143                 {
144                     double time = time_buf()[i]/1000.0/1000.0;
145                     string name;
146                     // Check if the name buffer is empty.  Use the name it contains if it isn't.
147                     if (name_buf(i,"")[0] != '\0')
148                         name = cast_to_string(i) + ": " + name_buf(i,"");
149                     else
150                         name = cast_to_string(i);
151 
152                     // make sure the name is always the same length.  Do so by padding with spaces
153                     if (name.size() < max_name_length)
154                         name += string(max_name_length-name.size(),' ');
155 
156                     if (time < 1000)
157                         cout << "  " << name << ": " << time << " milliseconds" << endl;
158                     else if (time < 1000*60)
159                         cout << "  " << name << ": " << time/1000.0 << " seconds" << endl;
160                     else if (time < 1000*60*60)
161                         cout << "  " << name << ": " << time/1000.0/60.0 << " minutes" << endl;
162                     else
163                         cout << "  " << name << ": " << time/1000.0/60.0/60.0 << " hours" << endl;
164                 }
165             }
166         }
167 
clear()168         inline void clear()
169         {
170             for (int i = 0; i < TIME_SLOTS; ++i)
171             {
172                 // clear timing buffer
173                 time_buf()[i] = 0;
174                 // clear name buffer
175                 name_buf(i,"")[0] = '\0';
176             }
177         }
178 
179         struct block
180         {
181             /*!
182                 WHAT THIS OBJECT REPRESENTS
183                     This is an RAII tool for calling start() and stop()
184             !*/
185 
blockblock186             block(int i):idx(i) {start(idx);}
blockblock187             block(int i, const char* str):idx(i) {start(idx,str);}
~blockblock188             ~block() { stop(idx); }
189             const int idx;
190         };
191     }
192 }
193 
194 
195 #endif // DLIB_TImING_Hh_
196 
197