1 /*
2 timefn.c - portable time measurement functions
3 Copyright (C) Yann Collet 2023
4
5 GPL v2 License
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21 You can contact the author at :
22 - LZ4 source repository : https://github.com/lz4/lz4
23 - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
24 */
25
26 /* === Dependencies === */
27
28 #include "timefn.h"
29 #include <time.h> /* CLOCK_MONOTONIC, TIME_UTC */
30
31 /*-****************************************
32 * Time functions
33 ******************************************/
34
35 #if defined(_WIN32) /* Windows */
36
37 # include <stdio.h> /* perror */
38 # include <stdlib.h> /* abort */
39 # include <windows.h> /* LARGE_INTEGER */
40
TIME_getTime(void)41 TIME_t TIME_getTime(void)
42 {
43 static LARGE_INTEGER ticksPerSecond;
44 static int init = 0;
45 if (!init) {
46 if (!QueryPerformanceFrequency(&ticksPerSecond)) {
47 perror("timefn::QueryPerformanceFrequency");
48 abort();
49 }
50 init = 1;
51 }
52 {
53 TIME_t r;
54 LARGE_INTEGER x;
55 QueryPerformanceCounter(&x);
56 r.t = (Duration_ns)(x.QuadPart * 1000000000ULL / ticksPerSecond.QuadPart);
57 return r;
58 }
59 }
60
61 #elif defined(__APPLE__) && defined(__MACH__)
62
63 # include <mach/mach_time.h> /* mach_timebase_info_data_t, mach_timebase_info, mach_absolute_time */
64
TIME_getTime(void)65 TIME_t TIME_getTime(void)
66 {
67 static mach_timebase_info_data_t rate;
68 static int init = 0;
69 if (!init) {
70 mach_timebase_info(&rate);
71 init = 1;
72 }
73 {
74 TIME_t r;
75 r.t = mach_absolute_time() * (Duration_ns)rate.numer
76 / (Duration_ns)rate.denom;
77 return r;
78 }
79 }
80
81
82 /* POSIX.1-2001 (optional) */
83 #elif defined(CLOCK_MONOTONIC)
84
85 #include <stdlib.h> /* abort */
86 #include <stdio.h> /* perror */
87
TIME_getTime(void)88 TIME_t TIME_getTime(void)
89 {
90 /* time must be initialized, othersize it may fail msan test.
91 * No good reason, likely a limitation of timespec_get() for some target */
92 struct timespec time = { 0, 0 };
93 if (clock_gettime(CLOCK_MONOTONIC, &time) != 0) {
94 perror("timefn::clock_gettime(CLOCK_MONOTONIC)");
95 abort();
96 }
97 { TIME_t r;
98 r.t = (Duration_ns)time.tv_sec * 1000000000ULL
99 + (Duration_ns)time.tv_nsec;
100 return r;
101 }
102 }
103
104
105 /* C11 requires support of timespec_get().
106 * However, FreeBSD 11 claims C11 compliance while lacking timespec_get().
107 * Double confirm timespec_get() support by checking the definition of TIME_UTC.
108 * However, some versions of Android manage to simultaneously define TIME_UTC
109 * and lack timespec_get() support... */
110 #elif (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* C11 */) \
111 && defined(TIME_UTC) && !defined(__ANDROID__)
112
113 # include <stdio.h> /* perror */
114 # include <stdlib.h> /* abort */
115
TIME_getTime(void)116 TIME_t TIME_getTime(void)
117 {
118 /* time must be initialized, othersize it may fail msan test */
119 struct timespec time = { 0, 0 };
120 if (timespec_get(&time, TIME_UTC) != TIME_UTC) {
121 perror("timefn::timespec_get(TIME_UTC)");
122 abort();
123 }
124 {
125 TIME_t r;
126 r.t = (Duration_ns)time.tv_sec * 1000000000ULL
127 + (Duration_ns)time.tv_nsec;
128 return r;
129 }
130 }
131
132 #else /* relies on standard C90 (note : clock_t produces wrong measurements \
133 for multi-threaded workloads) */
134
TIME_getTime(void)135 TIME_t TIME_getTime(void)
136 {
137 TIME_t r;
138 r.t = (Duration_ns)clock() * 1000000000ULL / CLOCKS_PER_SEC;
139 return r;
140 }
141
142 # define TIME_MT_MEASUREMENTS_NOT_SUPPORTED
143
144 #endif
145
146 /* ==== Common functions, valid for all time API ==== */
147
TIME_span_ns(TIME_t clockStart,TIME_t clockEnd)148 Duration_ns TIME_span_ns(TIME_t clockStart, TIME_t clockEnd)
149 {
150 return clockEnd.t - clockStart.t;
151 }
152
TIME_clockSpan_ns(TIME_t clockStart)153 Duration_ns TIME_clockSpan_ns(TIME_t clockStart)
154 {
155 TIME_t const clockEnd = TIME_getTime();
156 return TIME_span_ns(clockStart, clockEnd);
157 }
158
TIME_waitForNextTick(void)159 void TIME_waitForNextTick(void)
160 {
161 TIME_t const clockStart = TIME_getTime();
162 TIME_t clockEnd;
163 do {
164 clockEnd = TIME_getTime();
165 } while (TIME_span_ns(clockStart, clockEnd) == 0);
166 }
167
TIME_support_MT_measurements(void)168 int TIME_support_MT_measurements(void)
169 {
170 #if defined(TIME_MT_MEASUREMENTS_NOT_SUPPORTED)
171 return 0;
172 #else
173 return 1;
174 #endif
175 }
176