Enable hardware clock by default on ARM AArch64. (#14676)
Some checks are pending
CI / test-ubuntu-latest (push) Waiting to run
CI / test-sanitizer-address (push) Waiting to run
CI / build-debian-old (push) Waiting to run
CI / build-macos-latest (push) Waiting to run
CI / build-32bit (push) Waiting to run
CI / build-libc-malloc (push) Waiting to run
CI / build-centos-jemalloc (push) Waiting to run
CI / build-old-chain-jemalloc (push) Waiting to run
Codecov / code-coverage (push) Waiting to run
External Server Tests / test-external-standalone (push) Waiting to run
External Server Tests / test-external-cluster (push) Waiting to run
External Server Tests / test-external-nodebug (push) Waiting to run
Spellcheck / Spellcheck (push) Waiting to run

Redis can already use a processor-provided hardware counter as a
high-performance monotonic clock. On some architectures this must be
enabled carefully, but on ARM AArch64 the situation is different:

- The ARM Generic Timer is architecturally mandatory for all processors
that implement the AArch64 execution state.
- The system counter (`CNTVCT_EL0`) and its frequency (`CNTFRQ_EL0`) are
guaranteed to exist and provide a monotonic time source (per the *“The
Generic Timer in AArch64 state”* section of the *Arm® Architecture
Reference Manual for Armv8-A* —
https://developer.arm.com/documentation/ddi0487/latest).

Because of this architectural guarantee, it is safe to enable the
hardware clock by default on ARM AArch64.
Like detailed bellow, this gives us around 5% boost on io-thread
deployments for a simple strings benchmark.
This commit is contained in:
Filipe Oliveira (Redis) 2026-01-13 12:12:04 +00:00 committed by GitHub
parent 60a4fa2e4b
commit 3c96680cfb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 19 additions and 7 deletions

View file

@ -882,7 +882,9 @@ make MALLOC=jemalloc
By default, Redis will build using the POSIX clock_gettime function as the monotonic clock source. On most modern systems, the internal processor clock can be used to improve performance. Cautions can be found here: http://oliveryang.net/2015/09/pitfalls-of-TSC-usage/ By default, Redis will build using the POSIX clock_gettime function as the monotonic clock source. On most modern systems, the internal processor clock can be used to improve performance. Cautions can be found here: http://oliveryang.net/2015/09/pitfalls-of-TSC-usage/
To build with support for the processor's internal instruction clock, use: On ARM aarch64 systems, the hardware clock is enabled by default because the ARM Generic Timer is architecturally guaranteed to be available and monotonic on all ARMv8-A processors (see the *“The Generic Timer in AArch64 state”* section of the *Arm Architecture Reference Manual for Armv8-A*).
To build with support for the processor's internal instruction clock on other architectures, use:
```sh ```sh
make CFLAGS="-DUSE_PROCESSOR_CLOCK" make CFLAGS="-DUSE_PROCESSOR_CLOCK"

View file

@ -18,7 +18,13 @@ static char monotonic_info_string[32];
* generally safe on modern systems, this link provides additional information * 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 * 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 * On ARM aarch64 systems, the hardware clock is enabled by default because the
* ARM Generic Timer is architecturally guaranteed to be available and monotonic
* on all ARMv8-A processors (see the The Generic Timer in AArch64 state
* section of the Arm Architecture Reference Manual for Armv8-A).
*
* To use the processor clock on other architectures, either uncomment this line,
* or build with
* CFLAGS="-DUSE_PROCESSOR_CLOCK" * CFLAGS="-DUSE_PROCESSOR_CLOCK"
#define USE_PROCESSOR_CLOCK #define USE_PROCESSOR_CLOCK
*/ */
@ -92,18 +98,22 @@ static void monotonicInit_x86linux(void) {
} }
#endif #endif
#if defined(__aarch64__)
#if defined(USE_PROCESSOR_CLOCK) && defined(__aarch64__)
static long mono_ticksPerMicrosecond = 0; static long mono_ticksPerMicrosecond = 0;
/* Read the clock value. */ /* Read the clock value.
* CNTVCT_EL0 is a system counter register, that provides the monotonic
* timestamp as a 64-bit count value. */
static inline uint64_t __cntvct(void) { static inline uint64_t __cntvct(void) {
uint64_t virtual_timer_value; uint64_t virtual_timer_value;
__asm__ volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value)); __asm__ volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value));
return virtual_timer_value; return virtual_timer_value;
} }
/* Read the Count-timer Frequency. */ /* Read the Count-timer Frequency.
* CNTFRQ_EL0 is a system counter register that provides the frequency (in Hz)
* needed to convert ticks to microseconds. Together with CNTVCT_EL0, this enables
* high-performance monotonic time measurement without system calls. */
static inline uint32_t cntfrq_hz(void) { static inline uint32_t cntfrq_hz(void) {
uint64_t virtual_freq_value; uint64_t virtual_freq_value;
__asm__ volatile("mrs %0, cntfrq_el0" : "=r"(virtual_freq_value)); __asm__ volatile("mrs %0, cntfrq_el0" : "=r"(virtual_freq_value));
@ -213,7 +223,7 @@ const char * monotonicInit(void) {
if (getMonotonicUs == NULL) monotonicInit_x86linux(); if (getMonotonicUs == NULL) monotonicInit_x86linux();
#endif #endif
#if defined(USE_PROCESSOR_CLOCK) && defined(__aarch64__) #if defined(__aarch64__)
if (getMonotonicUs == NULL) monotonicInit_aarch64(); if (getMonotonicUs == NULL) monotonicInit_aarch64();
#endif #endif