1 | /******************************************************************************* |
2 | Copyright (c) The Taichi Authors (2016- ). All Rights Reserved. |
3 | The use of this software is governed by the LICENSE file. |
4 | *******************************************************************************/ |
5 | |
6 | #include "taichi/system/timer.h" |
7 | |
8 | #ifndef _WIN64 |
9 | |
10 | #include <unistd.h> |
11 | |
12 | #endif |
13 | |
14 | namespace taichi { |
15 | |
16 | using namespace std; |
17 | |
18 | std::map<std::string, std::pair<double, int>> Time::Timer::memo; |
19 | |
20 | std::map<std::string, double> Time::FPSCounter::last_refresh; |
21 | std::map<std::string, int> Time::FPSCounter::counter; |
22 | |
23 | #if defined(TI_PLATFORM_UNIX) |
24 | |
25 | double Time::get_time() { |
26 | struct timeval tv; |
27 | gettimeofday(&tv, nullptr); |
28 | return tv.tv_sec + 1e-6 * tv.tv_usec; |
29 | } |
30 | |
31 | #else |
32 | #include <intrin.h> |
33 | #pragma intrinsic(__rdtsc) |
34 | double Time::get_time() { |
35 | // https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx |
36 | LARGE_INTEGER EndingTime, ElapsedMicroseconds; |
37 | LARGE_INTEGER Frequency; |
38 | |
39 | QueryPerformanceFrequency(&Frequency); |
40 | |
41 | // Activity to be timed |
42 | |
43 | QueryPerformanceCounter(&EndingTime); |
44 | ElapsedMicroseconds.QuadPart = EndingTime.QuadPart; |
45 | |
46 | ElapsedMicroseconds.QuadPart *= 1000000; |
47 | ElapsedMicroseconds.QuadPart /= Frequency.QuadPart; |
48 | return (double)ElapsedMicroseconds.QuadPart / 1000000.0; |
49 | |
50 | /* |
51 | FILETIME tm; |
52 | GetSystemTimeAsFileTime(&tm); |
53 | unsigned long long t = ((ULONGLONG)tm.dwHighDateTime << 32) | |
54 | (ULONGLONG)tm.dwLowDateTime; |
55 | return (double)t / 10000000.0; |
56 | */ |
57 | } |
58 | #endif |
59 | |
60 | #ifdef _WIN64 |
61 | #include <Windows.h> |
62 | |
63 | namespace { |
64 | void win_usleep(double us) { |
65 | using us_t = chrono::duration<double, std::micro>; |
66 | auto start = chrono::high_resolution_clock::now(); |
67 | do { |
68 | // still little possible to release cpu. |
69 | // Note: |
70 | // https://docs.microsoft.com/zh-cn/windows/win32/api/synchapi/nf-synchapi-sleep |
71 | Sleep(0); |
72 | } while ((us_t(chrono::high_resolution_clock::now() - start).count()) < us); |
73 | } |
74 | |
75 | void win_msleep(DWORD ms) { |
76 | if (ms == 0) |
77 | Sleep(0); |
78 | else { |
79 | HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); |
80 | timeSetEvent(ms, 1, (LPTIMECALLBACK)hEvent, 0, |
81 | TIME_ONESHOT | TIME_CALLBACK_EVENT_SET); |
82 | WaitForSingleObject(hEvent, INFINITE); |
83 | CloseHandle(hEvent); |
84 | } |
85 | } |
86 | } // namespace |
87 | #endif |
88 | |
89 | void Time::usleep(double us) { |
90 | #ifdef _WIN64 |
91 | // use win_usleep for accuracy. |
92 | if (us < 999) |
93 | win_usleep(us); |
94 | // use win_msleep to release cpu, precision < 1ms |
95 | else |
96 | win_msleep(DWORD(us * 1e-3)); |
97 | #else |
98 | ::usleep(us); |
99 | #endif |
100 | } |
101 | |
102 | void Time::msleep(double ms) { |
103 | #ifdef _WIN64 |
104 | win_msleep(DWORD(ms)); |
105 | #else |
106 | ::usleep(ms * 1e3_f64); |
107 | #endif |
108 | } |
109 | |
110 | void Time::sleep(double s) { |
111 | Time::usleep(s * 1e6_f64); |
112 | } |
113 | |
114 | void Time::wait_until(double t) { |
115 | // microsecond (us) sleep on Windows... sadly. |
116 | double dt; |
117 | if (t < Time::get_time()) { |
118 | return; |
119 | } |
120 | do { // use system-provided sleep for large scale sleeping: |
121 | dt = t - Time::get_time(); |
122 | if (dt <= 0) { |
123 | return; |
124 | } |
125 | #ifdef _WIN64 |
126 | Time::sleep(dt * 0.5); |
127 | #else |
128 | Time::sleep(dt * (dt < 4e-2_f64 ? 0.02 : 0.4)); |
129 | #endif |
130 | } while (dt > 2e-4_f64); // until dt <= 200us |
131 | |
132 | // use an EBFE loop for small scale waiting: |
133 | while (Time::get_time() < t - 1e-6_f64) |
134 | ; // until dt <= 1us |
135 | } |
136 | |
137 | double Time::Timer::get_time() { |
138 | return Time::get_time(); |
139 | } |
140 | |
141 | void Time::Timer::print_record(const char *left, |
142 | double elapsed, |
143 | double average) { |
144 | if (elapsed < 1e-3) { |
145 | printf("%s ==> %6.3f us ~ %6.3f us\n" , left, elapsed * 1e6, average * 1e6); |
146 | } else { |
147 | printf("%s ==> %6.3f ms ~ %6.3f ms\n" , left, elapsed * 1e3, average * 1e3); |
148 | } |
149 | } |
150 | |
151 | void Time::Timer::output() { |
152 | if (have_output) { |
153 | return; |
154 | } else { |
155 | have_output = true; |
156 | } |
157 | double elapsed = get_time() - this->start_time; |
158 | std::string left = this->name; |
159 | if (left.size() < 60) { |
160 | left += std::string(60 - left.size(), '-'); |
161 | } |
162 | if (memo.find(name) == memo.end()) { |
163 | memo.insert(make_pair(name, make_pair(0.0, 0))); |
164 | } |
165 | pair<double, int> memo_record = memo[name]; |
166 | memo_record.first += elapsed; |
167 | memo_record.second += 1; |
168 | memo[name] = memo_record; |
169 | double avg = memo_record.first / memo_record.second; |
170 | this->print_record(left.c_str(), elapsed, avg); |
171 | } |
172 | |
173 | double Time::TickTimer::get_time() { |
174 | return Time::get_time(); |
175 | } |
176 | |
177 | void Time::TickTimer::print_record(const char *left, |
178 | double elapsed, |
179 | double average) { |
180 | string unit; |
181 | double measurement; |
182 | if (elapsed < 1e3) { |
183 | measurement = 1.0; |
184 | unit = "cycles" ; |
185 | } else if (elapsed < 1e6) { |
186 | measurement = 1e3; |
187 | unit = "K cycles" ; |
188 | } else if (elapsed < 1e9) { |
189 | measurement = 1e6; |
190 | unit = "M cycles" ; |
191 | } else { |
192 | measurement = 1e9; |
193 | unit = "G cycles" ; |
194 | } |
195 | printf("%s ==> %4.2f %s ~ %4.2f %s\n" , left, elapsed / measurement, |
196 | unit.c_str(), average / measurement, unit.c_str()); |
197 | } |
198 | |
199 | Time::Timer::Timer(std::string name) { |
200 | this->name = name; |
201 | this->start_time = get_time(); |
202 | this->have_output = false; |
203 | } |
204 | |
205 | Time::TickTimer::TickTimer(std::string name) { |
206 | this->name = name; |
207 | this->start_time = get_time(); |
208 | this->have_output = false; |
209 | } |
210 | |
211 | // Windows |
212 | #ifdef _WIN32 |
213 | |
214 | #include <intrin.h> |
215 | uint64 Time::get_cycles() { |
216 | return __rdtsc(); |
217 | } |
218 | |
219 | // Linux/GCC |
220 | #else |
221 | |
222 | uint64 Time::get_cycles() { |
223 | #if defined(TI_ARCH_x64) && !(defined(__arm64__) || defined(__aarch64__)) |
224 | unsigned int lo, hi; |
225 | __asm__ __volatile__("rdtsc" : "=a" (lo), "=d" (hi)); |
226 | return ((uint64)hi << 32) | lo; |
227 | #else |
228 | TI_WARN("get_cycles is not implemented in this platform. Returning 0." ); |
229 | return 0; |
230 | #endif |
231 | } |
232 | |
233 | #endif |
234 | |
235 | } // namespace taichi |
236 | |