1 //  (C) Copyright Raffi Enficiaud 2019.
2 //  Distributed under the Boost Software License, Version 1.0.
3 //  (See accompanying file LICENSE_1_0.txt or copy at
4 //  http://www.boost.org/LICENSE_1_0.txt)
5 
6 //  See http://www.boost.org/libs/test for the library home page.
7 //
8 //  Description : timer and elapsed types
9 // ***************************************************************************
10 
11 #ifndef BOOST_TEST_UTILS_TIMER_HPP
12 #define BOOST_TEST_UTILS_TIMER_HPP
13 
14 #include <boost/config.hpp>
15 #include <boost/cstdint.hpp>
16 #include <utility>
17 #include <ctime>
18 
19 # if defined(_WIN32) || defined(__CYGWIN__)
20 #   define BOOST_TEST_TIMER_WINDOWS_API
21 # elif defined(__MACH__) && defined(__APPLE__)// && !defined(CLOCK_MONOTONIC)
22 #   // we compile for all macs the same, CLOCK_MONOTONIC introduced in 10.12
23 #   define BOOST_TEST_TIMER_MACH_API
24 # else
25 #   define BOOST_TEST_TIMER_POSIX_API
26 #   if !defined(CLOCK_MONOTONIC)
27 #     error "CLOCK_MONOTONIC not defined"
28 #   endif
29 # endif
30 
31 # if defined(BOOST_TEST_TIMER_WINDOWS_API)
32 #   include <windows.h>
33 # elif defined(BOOST_TEST_TIMER_MACH_API)
34 #   include <mach/mach_time.h>
35 //#   include <mach/mach.h>      /* host_get_clock_service, mach_... */
36 # else
37 #   include <sys/time.h>
38 # endif
39 
40 # ifdef BOOST_NO_STDC_NAMESPACE
41   namespace std { using ::clock_t; using ::clock; }
42 # endif
43 
44 namespace boost {
45 namespace unit_test {
46 namespace timer {
47 
48   struct elapsed_time
49   {
50     typedef boost::int_least64_t nanosecond_type;
51 
52     nanosecond_type wall;
53     nanosecond_type system;
clearboost::unit_test::timer::elapsed_time54     void clear() {
55       wall = 0;
56       system = 0;
57     }
58   };
59 
60   inline double
microsecond_wall_time(elapsed_time const & elapsed)61   microsecond_wall_time( elapsed_time const& elapsed )
62   {
63       return elapsed.wall / 1E3;
64   }
65 
66   inline double
second_wall_time(elapsed_time const & elapsed)67   second_wall_time( elapsed_time const& elapsed )
68   {
69       return elapsed.wall / 1E9;
70   }
71 
72   namespace details {
73     #if defined(BOOST_TEST_TIMER_WINDOWS_API)
get_tick_freq()74     elapsed_time::nanosecond_type get_tick_freq() {
75         LARGE_INTEGER freq;
76         ::QueryPerformanceFrequency( &freq );
77         return static_cast<elapsed_time::nanosecond_type>(freq.QuadPart);
78     }
79     #elif defined(BOOST_TEST_TIMER_MACH_API)
80     std::pair<elapsed_time::nanosecond_type, elapsed_time::nanosecond_type> get_time_base() {
81         mach_timebase_info_data_t timebase;
82         if(mach_timebase_info(&timebase) == 0)
83             return std::pair<elapsed_time::nanosecond_type, elapsed_time::nanosecond_type>(timebase.numer, timebase.denom);
84         return std::pair<elapsed_time::nanosecond_type, elapsed_time::nanosecond_type>(0, 1);
85     }
86     #endif
87   }
88 
89   //! Simple timing class
90   //!
91   //! This class measures the wall clock time.
92   class timer
93   {
94   public:
timer()95     timer()
96     {
97         restart();
98     }
restart()99     void restart()
100     {
101         _start_time_clock = std::clock();
102     #if defined(BOOST_TEST_TIMER_WINDOWS_API)
103         ::QueryPerformanceCounter(&_start_time_wall);
104     #elif defined(BOOST_TEST_TIMER_MACH_API)
105         _start_time_wall = mach_absolute_time();
106     #else
107         if( ::clock_gettime( CLOCK_MONOTONIC, &_start_time_wall ) != 0 )
108         {
109             _start_time_wall.tv_nsec = -1;
110             _start_time_wall.tv_sec = -1;
111         }
112     #endif
113     }
114 
115     // return elapsed time in seconds
elapsed() const116     elapsed_time elapsed() const
117     {
118       typedef elapsed_time::nanosecond_type nanosecond_type;
119       static const double clock_to_nano_seconds = 1E9 / CLOCKS_PER_SEC;
120       elapsed_time return_value;
121 
122       // processor / system time
123       return_value.system = static_cast<nanosecond_type>(double(std::clock() - _start_time_clock) * clock_to_nano_seconds);
124 
125 #if defined(BOOST_TEST_TIMER_WINDOWS_API)
126       static const nanosecond_type tick_per_sec = details::get_tick_freq();
127       LARGE_INTEGER end_time;
128       ::QueryPerformanceCounter(&end_time);
129       return_value.wall = static_cast<nanosecond_type>(((end_time.QuadPart - _start_time_wall.QuadPart) * 1E9) / tick_per_sec);
130 #elif defined(BOOST_TEST_TIMER_MACH_API)
131       static std::pair<nanosecond_type, nanosecond_type> timebase = details::get_time_base();
132       nanosecond_type clock = mach_absolute_time() - _start_time_wall;
133       return_value.wall = static_cast<nanosecond_type>((clock * timebase.first) / timebase.second);
134 #else
135       struct timespec end_time;
136       return_value.wall = 0;
137       if( ::clock_gettime( CLOCK_MONOTONIC, &end_time ) == 0 )
138       {
139           return_value.wall = static_cast<nanosecond_type>((end_time.tv_sec - _start_time_wall.tv_sec) * 1E9 + (end_time.tv_nsec - _start_time_wall.tv_nsec));
140       }
141 #endif
142 
143       return return_value;
144     }
145 
146    private:
147       std::clock_t _start_time_clock;
148     #if defined(BOOST_TEST_TIMER_WINDOWS_API)
149       LARGE_INTEGER _start_time_wall;
150     #elif defined(BOOST_TEST_TIMER_MACH_API)
151       elapsed_time::nanosecond_type _start_time_wall;
152     #else
153       struct timespec _start_time_wall;
154     #endif
155   };
156 
157 
158 //____________________________________________________________________________//
159 
160 } // namespace timer
161 } // namespace unit_test
162 } // namespace boost
163 
164 #endif // BOOST_TEST_UTILS_TIMER_HPP
165 
166