xref: /aosp_15_r20/external/lz4/programs/timefn.c (revision 27162e4e17433d5aa7cb38e7b6a433a09405fc7f)
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