├── LICENSE ├── README.md └── GetTimeSinceProcessStart.h /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Max Liani 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | GetTimeSinceProcessStart v1.0.0 2 | ===== 3 | 4 | Single header implementation of a function to determine the time in seconds that 5 | passed since the start of the current process. This differs from measuring time 6 | starting at main() because GetTimeSinceProcessStart() accounts for computations 7 | performed after the process creation, including OS loader activities, standard 8 | library initialization, memory allocation, DLL/DSO loading, and static symbol 9 | construction. 10 | This library is primarily designed to be called at the very first line of main() 11 | to measure the time spent initializing the process.. After that, add that time 12 | to other time measurement you retrieve using your favorite timer implementation. 13 | Do not use this library as a general purpose timer. 14 | 15 | ### Intended audience 16 | Developers focused on high-performance software who want to 17 | benchmark and optimize process startup times, for precise execution statistics 18 | reporting measured from the program itself. 19 | Typically, the process startup time is a few milliseconds quick, but sometimes 20 | your code, or some library, executes complex initialization, like the allocation 21 | of memory pools, or the computation of tables, or complex global objects... 22 | You don't want performance degradation to creep into the init of the executable, 23 | unaccounted for. Measure the initialization time, keep a record of it, benchmark 24 | it as your program evolves. If the measurement changes over time, profile the 25 | program form the start with your sampling profiler of choice, bisect the changes 26 | and figure out what can be done about it. 27 | 28 | GetTimeSinceProcessStart currently has specific implementations for the main OSs 29 | including: Windows, Linux and Mac OSX. It can be called from C and C++ modules. 30 | 31 | ### Usage 32 | To use this library, define GTSPS_IMPLEMENTATION in a single C or C++ 33 | file before including this header to generate the implementation. 34 | 35 | ```cpp 36 | // Example: Including and using the library in a C++ file 37 | #include 38 | #define GTSPS_IMPLEMENTATION 39 | #include "GetTimeSinceProcessStart.h" 40 | ``` 41 | 42 | In a C++ project you can place the content of the library in a namespace of your 43 | choice: 44 | ```cpp 45 | #define GTSPS_NAMESPACE YourCoolNamespace 46 | ``` 47 | 48 | The implementation outputs any error to stderr using fprintf, but if you prefer 49 | to disable any logging define the following (the library returns 0.0 in case of 50 | error): 51 | ```cpp 52 | #define GTSPS_DONT_LOG_ERRORS 53 | ``` 54 | 55 | If instead you prefer to divert the errors logging to something else: 56 | ```cpp 57 | #define GTSPS_LOG_ERROR(str) YourCoolLoggingFunction(str) 58 | // The default definition is: 59 | // #define GTSPS_LOG_ERROR(str) fprintf(stderr, str) 60 | ``` 61 | 62 | To test if the library works, temporarily insert some known wait time in the 63 | creation of a global symbol and compare against a normal run. For example: 64 | 65 | ```cpp 66 | #define GTSPS_IMPLEMENTATION 67 | #include "GetTimeSinceProcessStart.h" 68 | 69 | #warning remove me!!! 70 | int sooooTiredAlready = usleep(5000000); //< Sleep fot 5 seconds 71 | 72 | int main() { 73 | double timeInSeconds = GetTimeSinceProcessStart(); 74 | printf("Startup time %f seconds\n", timeInSeconds); 75 | 76 | [...] 77 | } 78 | ``` 79 | 80 | Beware the first measurement after recompiling an executable tends to be longer, 81 | due to caching, security scanning, and other OS checks. So, run the test several 82 | times. 83 | 84 | Credits 85 | ------- 86 | Developed by [Max Liani](https://maxliani.wordpress.com/) 87 | -------------------------------------------------------------------------------- /GetTimeSinceProcessStart.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2024 Max Liani 2 | SPDX-License-Identifier: MIT 3 | 4 | GetTimeSinceProcessStart v1.0.0 5 | Single header implementation of a function to determine the time in seconds that 6 | passed since the start of the current process. This differs from measuring time 7 | starting at main() because GetTimeSinceProcessStart() accounts for computations 8 | performed after the process creation, including OS loader activities, standard 9 | library initialization, memory allocation, DLL/DSO loading, and static symbol 10 | construction. 11 | This library is primarily designed to be called at the very first line of main() 12 | to measure the time spent initializing the process.. After that, add that time 13 | to other time measurement you retrieve using your favorite timer implementation. 14 | Do not use this library as a general purpose timer. 15 | 16 | Intended audience: Developers focused on high-performance software who want to 17 | benchmark and optimize process startup times, for precise execution statistics 18 | reporting measured from the program itself. 19 | Typically, the process startup time is a few milliseconds quick, but sometimes 20 | your code, or some library, executes complex initialization, like the allocation 21 | of memory pools, or the computation of tables, or complex global objects... 22 | You don't want performance degradation to creep into the init of the executable, 23 | unaccounted for. Measure the initialization time, keep a record of it, benchmark 24 | it as your program evolves. If the measurement changes over time, profile the 25 | program form the start with your sampling profiler of choice, bisect the changes 26 | and figure out what can be done about it. 27 | 28 | GetTimeSinceProcessStart currently has specific implementations for the main OSs 29 | including: Windows, Linux and Mac OSX. It can be called from C and C++ modules. 30 | 31 | To use this library, declare "#define GTSPS_IMPLEMENTATION" in a single C or C++ 32 | file before including this header to generate the implementation. 33 | 34 | // Example: Including and using the library in a C++ file 35 | #include 36 | #define GTSPS_IMPLEMENTATION 37 | #include "GetTimeSinceProcessStart.h" 38 | 39 | In a C++ project you can place the content of the library in a namespace of your 40 | choice: 41 | #define GTSPS_NAMESPACE YourCoolNamespace 42 | 43 | The implementation outputs any error to stderr using fprintf, but if you prefer 44 | to disable any logging define the following (the library returns 0.0 in case of 45 | error): 46 | #define GTSPS_DONT_LOG_ERRORS 47 | 48 | If instead you prefer to divert the errors logging to something else: 49 | #define GTSPS_LOG_ERROR(str) YourCoolLoggingFunction(str) 50 | The default definition is: 51 | #define GTSPS_LOG_ERROR(str) fprintf(stderr, str) 52 | 53 | To test if the library works, temporarily insert some known wait time in the 54 | creation of a global symbol and compare against a normal run. For example: 55 | 56 | #define GTSPS_IMPLEMENTATION 57 | #include "GetTimeSinceProcessStart.h" 58 | 59 | #warning remove me!!! 60 | int sooooTiredAlready = usleep(5000000); //< Sleep fot 5 seconds 61 | 62 | int main() { 63 | double timeInSeconds = GetTimeSinceProcessStart(); 64 | printf("Startup time %f seconds\n", timeInSeconds); 65 | 66 | [...] 67 | } 68 | 69 | Beware the first measurement after recompiling an executable tends to be longer, 70 | due to caching, security scanning, and other OS checks. So, run the test several 71 | times. 72 | */ 73 | 74 | #pragma once 75 | 76 | #ifdef GTSPS_NAMESPACE 77 | #define GTSPS_NAMESPACE_BEGIN namespace GTSPS_NAMESPACE { 78 | #define GTSPS_NAMESPACE_END } 79 | #else 80 | #define GTSPS_NAMESPACE_BEGIN 81 | #define GTSPS_NAMESPACE_END 82 | #endif 83 | 84 | #ifndef GTSPS_IMPLEMENTATION 85 | GTSPS_NAMESPACE_BEGIN 86 | 87 | // @brief Measures the time passed since the process start, this accounts for any 88 | // time spends loading and configuring the address space, global symbols, 89 | // the standard library and any other library the executable links against. 90 | // Call this as you first line of main(...) to get the exact measurement 91 | // up to that point. Do not use this as a general-purpose timer, because 92 | // the system calls required to obtain the measurement can be slower than 93 | // the alternatives. 94 | // 95 | // @return time in seconds, or 0.0 in case of error. 96 | double GetTimeSinceProcessStart(); 97 | 98 | GTSPS_NAMESPACE_END 99 | 100 | #else 101 | /////////////////////////////////////////////////////////////////////////////// 102 | // Implementation 103 | 104 | #if defined(_WIN32) 105 | # ifndef WIN32_LEAN_AND_MEAN 106 | # define WIN32_LEAN_AND_MEAN 107 | # endif 108 | # ifndef NOMINMAX 109 | # define NOMINMAX 110 | # endif 111 | # include 112 | #elif defined(linux) || defined(__linux__) || defined(__LINUX__) 113 | # include //< for sysconf() 114 | # include 115 | # include 116 | #elif defined(__APPLE__) || defined(MACOSX) || defined(__MACOSX__) 117 | # include //< for getpid() 118 | # include 119 | #endif 120 | 121 | #ifdef GTSPS_DONT_LOG_ERRORS 122 | # define GTSPS_LOG_ERROR(str) 123 | #elif ! defined(GTSPS_LOG_ERROR) 124 | # define GTSPS_LOG_ERROR(str) fprintf(stderr, str) 125 | # include 126 | #endif 127 | 128 | GTSPS_NAMESPACE_BEGIN 129 | 130 | double GetTimeSinceProcessStart() 131 | { 132 | #if defined(_WIN32) 133 | double startTime = 0.0; 134 | { 135 | FILETIME creationTime, exitTime, kernelTime, userTime; 136 | if (!GetProcessTimes(GetCurrentProcess(), &creationTime, &exitTime, &kernelTime, &userTime)) 137 | { 138 | GTSPS_LOG_ERROR("Error: Failed to call GetProcessTimes\n"); 139 | return 0.0; 140 | } 141 | 142 | ULARGE_INTEGER converter; 143 | converter.LowPart = creationTime.dwLowDateTime; 144 | converter.HighPart = creationTime.dwHighDateTime; 145 | startTime = (double)converter.QuadPart / 10000000.0; // Convert 100-ns intervals to seconds 146 | } 147 | 148 | double currentTime = 0.0; 149 | { 150 | FILETIME systemTime; 151 | GetSystemTimeAsFileTime(&systemTime); // Current time 152 | 153 | ULARGE_INTEGER converter; 154 | converter.LowPart = systemTime.dwLowDateTime; 155 | converter.HighPart = systemTime.dwHighDateTime; 156 | currentTime = (double)converter.QuadPart / 10000000.0; // Convert 100-ns intervals to seconds 157 | } 158 | 159 | double timeInSeconds = currentTime - startTime; 160 | return timeInSeconds; 161 | 162 | #elif defined(linux) || defined(__linux__) || defined(__LINUX__) 163 | double processStartTimeInSeconds = 0.0; 164 | if (FILE* file = fopen("/proc/self/stat", "r")) 165 | { 166 | // Start time is the 22nd field (https://man7.org/linux/man-pages/man5/proc_pid_stat.5.html) 167 | uint64_t startTime = 0; 168 | int decoded = fscanf(file, "%*s (%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s " 169 | "%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %zu", 170 | &startTime); 171 | fclose(file); 172 | if (decoded != 1) 173 | { 174 | GTSPS_LOG_ERROR("Error: Failed decoding /proc/self/stat.\n"); 175 | return 0.0; 176 | } 177 | 178 | // Thypically this is 100Hz, it has nothing to do with CPU clock, rather with 179 | // interrupts and how the OS probes the process and increment timers. It gives 180 | // this measurement a resolution of 10 ms. 181 | double clockTicksPerSecond = (double)sysconf(_SC_CLK_TCK); 182 | 183 | // Read start time in clock ticks (since kernel start time), convert it to seconds 184 | processStartTimeInSeconds = (double)startTime / clockTicksPerSecond; 185 | } 186 | else 187 | { 188 | GTSPS_LOG_ERROR("Error: Failed to open /proc/self/stat.\n"); 189 | return 0.0; 190 | } 191 | 192 | double kernelUpTimeInSeconds = 0.0; 193 | if (FILE* file = fopen("/proc/uptime", "r")) 194 | { 195 | // Total kernel uptime in seconds is the first field 196 | int decoded = fscanf(file, "%lf", &kernelUpTimeInSeconds); 197 | fclose(file); 198 | if (decoded != 1) 199 | { 200 | GTSPS_LOG_ERROR("Error: Failed decoding /proc/uptime.\n"); 201 | return 0.0; 202 | } 203 | } 204 | else 205 | { 206 | GTSPS_LOG_ERROR("Error: Failed to open /proc/uptime.\n"); 207 | return 0.0; 208 | } 209 | 210 | double timeInSeconds = kernelUpTimeInSeconds - processStartTimeInSeconds; 211 | return timeInSeconds; 212 | 213 | #elif defined(__APPLE__) || defined(MACOSX) || defined(__MACOSX__) 214 | struct timeval startTime = {}; 215 | { 216 | // Get current process info, including the startup time 217 | pid_t pid = getpid(); 218 | struct proc_bsdinfo task_info = {}; 219 | if (proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &task_info, sizeof(task_info)) <= 0) 220 | { 221 | GTSPS_LOG_ERROR("Error: proc_pidinfo failed"); 222 | return 0.0; 223 | } 224 | startTime.tv_sec = task_info.pbi_start_tvsec; 225 | startTime.tv_usec = task_info.pbi_start_tvusec; 226 | } 227 | 228 | struct timeval currentTime = {}; 229 | gettimeofday(¤tTime, NULL); 230 | 231 | double timeInSeconds = (double)(currentTime.tv_sec - startTime.tv_sec ) + //< Seconds 232 | (double)(currentTime.tv_usec - startTime.tv_usec) / 1000000.0; //< Microseconds 233 | return timeInSeconds; 234 | #else 235 | #warning unsupported platform 236 | return 0.0; 237 | #endif 238 | } 239 | 240 | GTSPS_NAMESPACE_END 241 | #undef GTSPS_NAMESPACE_BEGIN 242 | #undef GTSPS_NAMESPACE_END 243 | #undef GTSPS_LOG_ERROR 244 | 245 | #endif 246 | --------------------------------------------------------------------------------