1 | #include "monotonic.h" |
2 | #include <stddef.h> |
3 | #include <stdlib.h> |
4 | #include <stdio.h> |
5 | #include <time.h> |
6 | |
7 | #undef NDEBUG |
8 | #include <assert.h> |
9 | |
10 | |
11 | /* The function pointer for clock retrieval. */ |
12 | monotime (*getMonotonicUs)(void) = NULL; |
13 | |
14 | static char monotonic_info_string[32]; |
15 | |
16 | |
17 | /* Using the processor clock (aka TSC on x86) can provide improved performance |
18 | * throughout Redis wherever the monotonic clock is used. The processor clock |
19 | * is significantly faster than calling 'clock_getting' (POSIX). While this is |
20 | * generally safe on modern systems, this link provides additional information |
21 | * about use of the x86 TSC: http://oliveryang.net/2015/09/pitfalls-of-TSC-usage |
22 | * |
23 | * To use the processor clock, either uncomment this line, or build with |
24 | * CFLAGS="-DUSE_PROCESSOR_CLOCK" |
25 | #define USE_PROCESSOR_CLOCK |
26 | */ |
27 | |
28 | |
29 | #if defined(USE_PROCESSOR_CLOCK) && defined(__x86_64__) && defined(__linux__) |
30 | #include <regex.h> |
31 | #include <x86intrin.h> |
32 | |
33 | static long mono_ticksPerMicrosecond = 0; |
34 | |
35 | static monotime getMonotonicUs_x86() { |
36 | return __rdtsc() / mono_ticksPerMicrosecond; |
37 | } |
38 | |
39 | static void monotonicInit_x86linux() { |
40 | const int bufflen = 256; |
41 | char buf[bufflen]; |
42 | regex_t cpuGhzRegex, constTscRegex; |
43 | const size_t nmatch = 2; |
44 | regmatch_t pmatch[nmatch]; |
45 | int constantTsc = 0; |
46 | int rc; |
47 | |
48 | /* Determine the number of TSC ticks in a micro-second. This is |
49 | * a constant value matching the standard speed of the processor. |
50 | * On modern processors, this speed remains constant even though |
51 | * the actual clock speed varies dynamically for each core. */ |
52 | rc = regcomp(&cpuGhzRegex, "^model name\\s+:.*@ ([0-9.]+)GHz" , REG_EXTENDED); |
53 | assert(rc == 0); |
54 | |
55 | /* Also check that the constant_tsc flag is present. (It should be |
56 | * unless this is a really old CPU. */ |
57 | rc = regcomp(&constTscRegex, "^flags\\s+:.* constant_tsc" , REG_EXTENDED); |
58 | assert(rc == 0); |
59 | |
60 | FILE *cpuinfo = fopen("/proc/cpuinfo" , "r" ); |
61 | if (cpuinfo != NULL) { |
62 | while (fgets(buf, bufflen, cpuinfo) != NULL) { |
63 | if (regexec(&cpuGhzRegex, buf, nmatch, pmatch, 0) == 0) { |
64 | buf[pmatch[1].rm_eo] = '\0'; |
65 | double ghz = atof(&buf[pmatch[1].rm_so]); |
66 | mono_ticksPerMicrosecond = (long)(ghz * 1000); |
67 | break; |
68 | } |
69 | } |
70 | while (fgets(buf, bufflen, cpuinfo) != NULL) { |
71 | if (regexec(&constTscRegex, buf, nmatch, pmatch, 0) == 0) { |
72 | constantTsc = 1; |
73 | break; |
74 | } |
75 | } |
76 | |
77 | fclose(cpuinfo); |
78 | } |
79 | regfree(&cpuGhzRegex); |
80 | regfree(&constTscRegex); |
81 | |
82 | if (mono_ticksPerMicrosecond == 0) { |
83 | fprintf(stderr, "monotonic: x86 linux, unable to determine clock rate" ); |
84 | return; |
85 | } |
86 | if (!constantTsc) { |
87 | fprintf(stderr, "monotonic: x86 linux, 'constant_tsc' flag not present" ); |
88 | return; |
89 | } |
90 | |
91 | snprintf(monotonic_info_string, sizeof(monotonic_info_string), |
92 | "X86 TSC @ %ld ticks/us" , mono_ticksPerMicrosecond); |
93 | getMonotonicUs = getMonotonicUs_x86; |
94 | } |
95 | #endif |
96 | |
97 | |
98 | #if defined(USE_PROCESSOR_CLOCK) && defined(__aarch64__) |
99 | static long mono_ticksPerMicrosecond = 0; |
100 | |
101 | /* Read the clock value. */ |
102 | static inline uint64_t __cntvct() { |
103 | uint64_t virtual_timer_value; |
104 | __asm__ volatile("mrs %0, cntvct_el0" : "=r" (virtual_timer_value)); |
105 | return virtual_timer_value; |
106 | } |
107 | |
108 | /* Read the Count-timer Frequency. */ |
109 | static inline uint32_t cntfrq_hz() { |
110 | uint64_t virtual_freq_value; |
111 | __asm__ volatile("mrs %0, cntfrq_el0" : "=r" (virtual_freq_value)); |
112 | return (uint32_t)virtual_freq_value; /* top 32 bits are reserved */ |
113 | } |
114 | |
115 | static monotime getMonotonicUs_aarch64() { |
116 | return __cntvct() / mono_ticksPerMicrosecond; |
117 | } |
118 | |
119 | static void monotonicInit_aarch64() { |
120 | mono_ticksPerMicrosecond = (long)cntfrq_hz() / 1000L / 1000L; |
121 | if (mono_ticksPerMicrosecond == 0) { |
122 | fprintf(stderr, "monotonic: aarch64, unable to determine clock rate" ); |
123 | return; |
124 | } |
125 | |
126 | snprintf(monotonic_info_string, sizeof(monotonic_info_string), |
127 | "ARM CNTVCT @ %ld ticks/us" , mono_ticksPerMicrosecond); |
128 | getMonotonicUs = getMonotonicUs_aarch64; |
129 | } |
130 | #endif |
131 | |
132 | |
133 | static monotime getMonotonicUs_posix() { |
134 | /* clock_gettime() is specified in POSIX.1b (1993). Even so, some systems |
135 | * did not support this until much later. CLOCK_MONOTONIC is technically |
136 | * optional and may not be supported - but it appears to be universal. |
137 | * If this is not supported, provide a system-specific alternate version. */ |
138 | struct timespec ts; |
139 | clock_gettime(CLOCK_MONOTONIC, &ts); |
140 | return ((uint64_t)ts.tv_sec) * 1000000 + ts.tv_nsec / 1000; |
141 | } |
142 | |
143 | static void monotonicInit_posix() { |
144 | /* Ensure that CLOCK_MONOTONIC is supported. This should be supported |
145 | * on any reasonably current OS. If the assertion below fails, provide |
146 | * an appropriate alternate implementation. */ |
147 | struct timespec ts; |
148 | int rc = clock_gettime(CLOCK_MONOTONIC, &ts); |
149 | assert(rc == 0); |
150 | |
151 | snprintf(monotonic_info_string, sizeof(monotonic_info_string), |
152 | "POSIX clock_gettime" ); |
153 | getMonotonicUs = getMonotonicUs_posix; |
154 | } |
155 | |
156 | |
157 | |
158 | const char * monotonicInit() { |
159 | #if defined(USE_PROCESSOR_CLOCK) && defined(__x86_64__) && defined(__linux__) |
160 | if (getMonotonicUs == NULL) monotonicInit_x86linux(); |
161 | #endif |
162 | |
163 | #if defined(USE_PROCESSOR_CLOCK) && defined(__aarch64__) |
164 | if (getMonotonicUs == NULL) monotonicInit_aarch64(); |
165 | #endif |
166 | |
167 | if (getMonotonicUs == NULL) monotonicInit_posix(); |
168 | |
169 | return monotonic_info_string; |
170 | } |
171 | |
172 | const char *monotonicInfoString() { |
173 | return monotonic_info_string; |
174 | } |
175 | |
176 | monotonic_clock_type monotonicGetType() { |
177 | if (getMonotonicUs == getMonotonicUs_posix) |
178 | return MONOTONIC_CLOCK_POSIX; |
179 | return MONOTONIC_CLOCK_HW; |
180 | } |
181 | |