Branch data Line data Source code
1 : : /*
2 : : * Copyright (C) 2026 by Thun Lu. All rights reserved.
3 : : * Author: Thun Lu <thun.lu@zohomail.cn>
4 : : * Repo: https://github.com/thun-res/vlink
5 : : * _ __ __ _ __
6 : : * | | / / / / (_) ____ / /__
7 : : * | | / / / / / / / __ \ / //_/
8 : : * | |/ / / /___ / / / / / / / ,<
9 : : * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10 : : *
11 : : * Licensed under the Apache License, Version 2.0 (the "License");
12 : : * you may not use this file except in compliance with the License.
13 : : * You may obtain a copy of the License at
14 : : *
15 : : * http://www.apache.org/licenses/LICENSE-2.0
16 : : *
17 : : * Unless required by applicable law or agreed to in writing, software
18 : : * distributed under the License is distributed on an "AS IS" BASIS,
19 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 : : * See the License for the specific language governing permissions and
21 : : * limitations under the License.
22 : : */
23 : :
24 : : #include "./base/elapsed_timer.h"
25 : :
26 : : #include <chrono>
27 : :
28 : : #ifdef _WIN32
29 : : #include <Windows.h>
30 : : #else
31 : : #include <sys/resource.h>
32 : : #include <sys/time.h>
33 : : #endif
34 : :
35 : : namespace vlink {
36 : :
37 : : #ifndef _WIN32
38 : 198 : [[maybe_unused]] static uint64_t get_high_resolution_sys_timestamp() {
39 : : ::timespec ts;
40 : :
41 : 198 : ::clock_gettime(CLOCK_REALTIME, &ts);
42 : :
43 : 198 : return (ts.tv_sec * 1000'000'000ULL) + ts.tv_nsec;
44 : : }
45 : :
46 : 39 : [[maybe_unused]] static uint64_t get_high_resolution_cpu_timestamp() {
47 : : ::timespec ts;
48 : :
49 : : #ifdef __linux__
50 : 39 : ::clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
51 : : #else
52 : : ::clock_gettime(CLOCK_MONOTONIC, &ts);
53 : : #endif
54 : :
55 : 39 : return (ts.tv_sec * 1000'000'000ULL) + ts.tv_nsec;
56 : : }
57 : : #endif
58 : :
59 : : template <typename TypeT, typename TimeT, typename ReturnT>
60 : 2189 : static ReturnT get_current_time() noexcept {
61 : 2189 : return static_cast<ReturnT>(std::chrono::duration_cast<TimeT>(TypeT::now().time_since_epoch()).count());
62 : : }
63 : :
64 : : // ElapsedTimer
65 : 404 : ElapsedTimer::ElapsedTimer() noexcept = default;
66 : :
67 : 1 : ElapsedTimer::ElapsedTimer(Method method) noexcept : method_(method) {}
68 : :
69 : 792 : ElapsedTimer::ElapsedTimer(Accuracy accuracy) noexcept : accuracy_(accuracy) {}
70 : :
71 : 38 : ElapsedTimer::ElapsedTimer(Method method, Accuracy accuracy) noexcept : method_(method), accuracy_(accuracy) {}
72 : :
73 : 1 : ElapsedTimer::ElapsedTimer(const ElapsedTimer& target) noexcept
74 : 2 : : start_time_(target.start_time_.load(std::memory_order_acquire)),
75 : 1 : method_(target.method_),
76 : 1 : accuracy_(target.accuracy_) {}
77 : :
78 : 114 : ElapsedTimer::ElapsedTimer(ElapsedTimer&& target) noexcept
79 : 228 : : start_time_(target.start_time_.load(std::memory_order_acquire)),
80 : 114 : method_(target.method_),
81 : 114 : accuracy_(target.accuracy_) {}
82 : :
83 : 1350 : ElapsedTimer::~ElapsedTimer() noexcept = default;
84 : :
85 : 1 : ElapsedTimer& ElapsedTimer::operator=(const ElapsedTimer& target) noexcept {
86 [ - + ]: 1 : if VUNLIKELY (this == &target) {
87 : : return *this; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
88 : : }
89 : :
90 : 1 : method_ = target.method_;
91 : 1 : accuracy_ = target.accuracy_;
92 : 2 : start_time_.store(target.start_time_.load(std::memory_order_acquire), std::memory_order_release);
93 : :
94 : 1 : return *this;
95 : : }
96 : :
97 : 0 : ElapsedTimer& ElapsedTimer::operator=(ElapsedTimer&& target) noexcept {
98 : : if VUNLIKELY (this == &target) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE
99 : : return *this; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
100 : : }
101 : :
102 : 0 : method_ = target.method_;
103 : 0 : accuracy_ = target.accuracy_;
104 : 0 : start_time_.store(target.start_time_.load(std::memory_order_acquire), std::memory_order_release);
105 : :
106 : 0 : return *this;
107 : : }
108 : :
109 : 234 : uint64_t ElapsedTimer::get_sys_timestamp(Accuracy accuracy, bool high_resolution) noexcept {
110 : : #ifdef _WIN32
111 : : (void)high_resolution;
112 : :
113 : : switch (accuracy) {
114 : : case kMilli:
115 : : return get_current_time<std::chrono::system_clock, std::chrono::milliseconds, uint64_t>();
116 : : case kMicro:
117 : : return get_current_time<std::chrono::system_clock, std::chrono::microseconds, uint64_t>();
118 : : case kNano:
119 : : return get_current_time<std::chrono::system_clock, std::chrono::nanoseconds, uint64_t>();
120 : : default:
121 : : return 0;
122 : : }
123 : : #else
124 : :
125 [ + + ]: 234 : if (high_resolution) {
126 [ + + + - ]: 198 : switch (accuracy) {
127 : 3 : case kMilli:
128 : 3 : return get_high_resolution_sys_timestamp() / 1000'000U;
129 : 194 : case kMicro:
130 : 194 : return get_high_resolution_sys_timestamp() / 1000U;
131 : 1 : case kNano:
132 : 1 : return get_high_resolution_sys_timestamp();
133 : : default: // LCOV_EXCL_LINE GCOVR_EXCL_LINE
134 : : return 0; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
135 : : }
136 : : } else {
137 [ + - + - ]: 36 : switch (accuracy) {
138 : 1 : case kMilli:
139 : 1 : return get_current_time<std::chrono::system_clock, std::chrono::milliseconds, uint64_t>();
140 : 0 : case kMicro:
141 : 0 : return get_current_time<std::chrono::system_clock, std::chrono::microseconds, uint64_t>();
142 : 35 : case kNano:
143 : 35 : return get_current_time<std::chrono::system_clock, std::chrono::nanoseconds, uint64_t>();
144 : 0 : default:
145 : 0 : return 0;
146 : : }
147 : : }
148 : : #endif
149 : : }
150 : :
151 : 2192 : uint64_t ElapsedTimer::get_cpu_timestamp(Accuracy accuracy, bool high_resolution) noexcept {
152 : : #ifdef _WIN32
153 : : (void)high_resolution;
154 : :
155 : : switch (accuracy) {
156 : : case kMilli:
157 : : return get_current_time<std::chrono::steady_clock, std::chrono::milliseconds, uint64_t>();
158 : : case kMicro:
159 : : return get_current_time<std::chrono::steady_clock, std::chrono::microseconds, uint64_t>();
160 : : case kNano:
161 : : return get_current_time<std::chrono::steady_clock, std::chrono::nanoseconds, uint64_t>();
162 : : default:
163 : : return 0;
164 : : }
165 : : #else
166 : :
167 [ + + ]: 2192 : if (high_resolution) {
168 [ + + + - ]: 39 : switch (accuracy) {
169 : 29 : case kMilli:
170 : 29 : return get_high_resolution_cpu_timestamp() / 1000'000U;
171 : 6 : case kMicro:
172 : 6 : return get_high_resolution_cpu_timestamp() / 1000U;
173 : 4 : case kNano:
174 : 4 : return get_high_resolution_cpu_timestamp();
175 : : default: // LCOV_EXCL_LINE GCOVR_EXCL_LINE
176 : : return 0; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
177 : : }
178 : : } else {
179 [ + + + - ]: 2153 : switch (accuracy) {
180 : 1562 : case kMilli:
181 : 1562 : return get_current_time<std::chrono::steady_clock, std::chrono::milliseconds, uint64_t>();
182 : 555 : case kMicro:
183 : 555 : return get_current_time<std::chrono::steady_clock, std::chrono::microseconds, uint64_t>();
184 : 36 : case kNano:
185 : 36 : return get_current_time<std::chrono::steady_clock, std::chrono::nanoseconds, uint64_t>();
186 : 0 : default:
187 : 0 : return 0;
188 : : }
189 : : }
190 : : #endif
191 : : }
192 : :
193 : 48 : uint64_t ElapsedTimer::get_cpu_active_time(Accuracy accuracy) noexcept {
194 : : #ifdef _WIN32
195 : : FILETIME creation_time;
196 : : FILETIME exit_time;
197 : : FILETIME kernel_time;
198 : : FILETIME user_time;
199 : :
200 : : if VUNLIKELY (!GetProcessTimes(GetCurrentProcess(), &creation_time, &exit_time, &kernel_time, &user_time)) {
201 : : return 0;
202 : : }
203 : :
204 : : static constexpr int kFileTimeToNano = 100;
205 : :
206 : : uint64_t user_time_ns =
207 : : (static_cast<uint64_t>(user_time.dwHighDateTime) << 32 | user_time.dwLowDateTime) * kFileTimeToNano;
208 : : uint64_t kernel_time_ns =
209 : : (static_cast<uint64_t>(kernel_time.dwHighDateTime) << 32 | kernel_time.dwLowDateTime) * kFileTimeToNano;
210 : : uint64_t total_time_ns = user_time_ns + kernel_time_ns;
211 : :
212 : : switch (accuracy) {
213 : : case kMilli:
214 : : return total_time_ns / 1000'000U;
215 : : case kMicro:
216 : : return total_time_ns / 1000U;
217 : : case kNano:
218 : : return total_time_ns;
219 : : default:
220 : : return total_time_ns / 1000'000U;
221 : : }
222 : : #else
223 : : struct rusage usage;
224 : :
225 [ - + ]: 48 : if VUNLIKELY (::getrusage(RUSAGE_SELF, &usage) != 0) {
226 : : return 0; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
227 : : }
228 : :
229 : 48 : uint64_t user_time = (static_cast<uint64_t>(usage.ru_utime.tv_sec) * 1000'000U) + usage.ru_utime.tv_usec;
230 : 48 : uint64_t system_time = (static_cast<uint64_t>(usage.ru_stime.tv_sec) * 1000'000U) + usage.ru_stime.tv_usec;
231 : 48 : uint64_t total_time = user_time + system_time;
232 : :
233 [ + + + - ]: 48 : switch (accuracy) {
234 : 4 : case kMilli:
235 : 4 : return total_time / 1000U;
236 : 1 : case kMicro:
237 : 1 : return total_time;
238 : 43 : case kNano:
239 : 43 : return total_time * 1000U;
240 : : default: // LCOV_EXCL_LINE GCOVR_EXCL_LINE
241 : : return total_time / 1000U; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
242 : : }
243 : : #endif
244 : : }
245 : :
246 : 8 : ElapsedTimer::Method ElapsedTimer::get_method() const noexcept { return method_; }
247 : :
248 : 11 : ElapsedTimer::Accuracy ElapsedTimer::get_accuracy() const noexcept { return accuracy_; }
249 : :
250 : 364 : void ElapsedTimer::start() noexcept {
251 : : int64_t new_value;
252 : 364 : int64_t expected = -1;
253 : :
254 [ + + ]: 364 : if (method_ == kCpuTimestamp) {
255 : 363 : new_value = get_cpu_timestamp(accuracy_, false);
256 : : } else {
257 : 1 : new_value = get_cpu_active_time(accuracy_);
258 : : }
259 : :
260 : 364 : start_time_.compare_exchange_strong(expected, new_value, std::memory_order_acq_rel, std::memory_order_acquire);
261 : 364 : }
262 : :
263 : 375 : void ElapsedTimer::stop() noexcept { start_time_.store(-1, std::memory_order_release); }
264 : :
265 : 821 : int64_t ElapsedTimer::restart() noexcept {
266 : : int64_t new_time;
267 : :
268 [ + + ]: 821 : if (method_ == kCpuTimestamp) {
269 : 779 : new_time = get_cpu_timestamp(accuracy_, false);
270 : : } else {
271 : 42 : new_time = get_cpu_active_time(accuracy_);
272 : : }
273 : :
274 : 821 : int64_t old_time = start_time_.exchange(new_time, std::memory_order_release);
275 : :
276 [ + + ]: 821 : if (old_time < 0) {
277 : 536 : return old_time;
278 : : } else {
279 : 285 : return new_time - old_time;
280 : : }
281 : : }
282 : :
283 : 968 : bool ElapsedTimer::is_active() const noexcept { return start_time_.load(std::memory_order_acquire) >= 0; }
284 : :
285 : 1020 : int64_t ElapsedTimer::get() const noexcept {
286 : 1020 : int64_t start_time = start_time_.load(std::memory_order_acquire);
287 : :
288 [ + + ]: 1020 : if (start_time < 0) {
289 : 9 : return start_time;
290 : : }
291 : :
292 [ + + ]: 1011 : if (method_ == kCpuTimestamp) {
293 : 1010 : return get_cpu_timestamp(accuracy_, false) - start_time;
294 : : } else {
295 : 1 : return get_cpu_active_time(accuracy_) - start_time;
296 : : }
297 : : }
298 : :
299 : : } // namespace vlink
|