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