LCOV - code coverage report
Current view: top level - src/base - elapsed_timer.cc (source / functions) Hit Total Coverage
Test: vlink Lines: 95 106 89.6 %
Date: 2026-06-27 19:56:04 Functions: 25 27 92.6 %
Branches: 30 38 78.9 %

           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

Generated by: LCOV version 1.14