Skip to content

Comments

Enable HW clock by default#3103

Merged
zuiderkwast merged 2 commits intovalkey-io:unstablefrom
dvkashapov:hw-clock-default
Jan 28, 2026
Merged

Enable HW clock by default#3103
zuiderkwast merged 2 commits intovalkey-io:unstablefrom
dvkashapov:hw-clock-default

Conversation

@dvkashapov
Copy link
Member

@dvkashapov dvkashapov commented Jan 23, 2026

On some Intel systems it was observed that TSC ticksPerMicrosecond derived from /proc/cpuinfo was different from calibrated ticksPerMicrosecond or kernel derived one, so it was decided to always calibrate and look for constant_tsc flag.

Resolves #2597

Signed-off-by: Daniil Kashapov <daniil.kashapov.ykt@gmail.com>
@codecov
Copy link

codecov bot commented Jan 23, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 74.86%. Comparing base (18257cd) to head (582ceb1).
⚠️ Report is 10 commits behind head on unstable.

Additional details and impacted files
@@             Coverage Diff              @@
##           unstable    #3103      +/-   ##
============================================
+ Coverage     74.30%   74.86%   +0.55%     
============================================
  Files           129      129              
  Lines         71013    71185     +172     
============================================
+ Hits          52767    53290     +523     
+ Misses        18246    17895     -351     
Files with missing lines Coverage Δ
src/monotonic.c 75.43% <100.00%> (-24.57%) ⬇️

... and 33 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@zuiderkwast zuiderkwast left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Small and simple.

@dvkashapov dvkashapov moved this to In Progress in Valkey 9.1 Jan 23, 2026
@github-actions
Copy link

Benchmark ran on this commit: a5f86e2

Benchmark Comparison: unstable vs e47cc4a (averaged) - rps metrics

Run Summary:

  • unstable: 80 total runs, 16 configurations (avg 5.00 runs per config)
  • e47cc4a: 80 total runs, 16 configurations (avg 5.00 runs per config)

Statistical Notes:

  • CI99%: 99% Confidence Interval - range where the true population mean is likely to fall
  • PI99%: 99% Prediction Interval - range where a single future observation is likely to fall
  • CV: Coefficient of Variation - relative variability (σ/μ × 100%)

Note: Values with (n=X, σ=Y, CV=Z%, CI99%=±W%, PI99%=±V%) indicate averages from X runs with standard deviation Y, coefficient of variation Z%, 99% confidence interval margin of error ±W% of the mean, and 99% prediction interval margin of error ±V% of the mean. CI bounds [A, B] and PI bounds [C, D] show the actual interval ranges.

Configuration:

  • architecture: aarch64
  • benchmark_mode: duration
  • clients: 1600
  • cluster_mode: False
  • data_size: 16
  • duration: 180
  • tls: False
  • valkey_benchmark_threads: 90
  • warmup: 30
Command Metric Pipeline io_threads unstable e47cc4a Diff % Change
GET rps 1 1 226859.258 (n=5, σ=3149.614, CV=1.39%, CI99%=±2.859%, PI99%=±7.002%, CI[220374.160, 233344.356], PI[210974.076, 242744.440]) 228595.032 (n=5, σ=656.999, CV=0.29%, CI99%=±0.592%, PI99%=±1.450%, CI[227242.261, 229947.803], PI[225281.434, 231908.630]) 1735.774 +0.765%
GET rps 1 9 1540990.726 (n=5, σ=14838.480, CV=0.96%, CI99%=±1.983%, PI99%=±4.857%, CI[1510438.091, 1571543.361], PI[1466152.360, 1615829.092]) 1542821.476 (n=5, σ=13601.579, CV=0.88%, CI99%=±1.815%, PI99%=±4.446%, CI[1514815.636, 1570827.316], PI[1474221.459, 1611421.493]) 1830.750 +0.119%
GET rps 10 1 1186805.400 (n=5, σ=6913.430, CV=0.58%, CI99%=±1.199%, PI99%=±2.938%, CI[1172570.552, 1201040.248], PI[1151937.286, 1221673.514]) 1227036.124 (n=5, σ=2257.421, CV=0.18%, CI99%=±0.379%, PI99%=±0.928%, CI[1222388.064, 1231684.184], PI[1215650.748, 1238421.500]) 40230.724 +3.390%
GET rps 10 9 2478615.800 (n=5, σ=22801.899, CV=0.92%, CI99%=±1.894%, PI99%=±4.640%, CI[2431666.376, 2525565.224], PI[2363613.667, 2593617.933]) 2656141.200 (n=5, σ=8382.101, CV=0.32%, CI99%=±0.650%, PI99%=±1.592%, CI[2638882.338, 2673400.062], PI[2613865.794, 2698416.606]) 177525.400 +7.162%
SET rps 1 1 220775.876 (n=5, σ=1702.273, CV=0.77%, CI99%=±1.588%, PI99%=±3.889%, CI[217270.873, 224280.879], PI[212190.406, 229361.346]) 221330.784 (n=5, σ=1393.103, CV=0.63%, CI99%=±1.296%, PI99%=±3.175%, CI[218462.365, 224199.203], PI[214304.621, 228356.947]) 554.908 +0.251%
SET rps 1 9 1452571.224 (n=5, σ=10073.194, CV=0.69%, CI99%=±1.428%, PI99%=±3.498%, CI[1431830.377, 1473312.071], PI[1401766.733, 1503375.715]) 1485389.300 (n=5, σ=6884.641, CV=0.46%, CI99%=±0.954%, PI99%=±2.338%, CI[1471213.729, 1499564.871], PI[1450666.384, 1520112.216]) 32818.076 +2.259%
SET rps 10 1 1017296.276 (n=5, σ=8821.739, CV=0.87%, CI99%=±1.786%, PI99%=±4.374%, CI[999132.194, 1035460.358], PI[972803.543, 1061789.009]) 1039511.310 (n=5, σ=3536.538, CV=0.34%, CI99%=±0.701%, PI99%=±1.716%, CI[1032229.530, 1046793.090], PI[1021674.665, 1057347.955]) 22215.034 +2.184%
SET rps 10 9 1854988.476 (n=5, σ=20963.871, CV=1.13%, CI99%=±2.327%, PI99%=±5.700%, CI[1811823.575, 1898153.377], PI[1749256.494, 1960720.458]) 1930034.924 (n=5, σ=12460.938, CV=0.65%, CI99%=±1.329%, PI99%=±3.256%, CI[1904377.680, 1955692.168], PI[1867187.767, 1992882.081]) 75046.448 +4.046%

Configuration:

  • architecture: aarch64
  • benchmark_mode: duration
  • clients: 1600
  • cluster_mode: False
  • data_size: 96
  • duration: 180
  • tls: False
  • valkey_benchmark_threads: 90
  • warmup: 30
Command Metric Pipeline io_threads unstable e47cc4a Diff % Change
GET rps 1 1 220067.486 (n=5, σ=970.747, CV=0.44%, CI99%=±0.908%, PI99%=±2.225%, CI[218068.705, 222066.267], PI[215171.494, 224963.478]) 220286.628 (n=5, σ=2195.725, CV=1.00%, CI99%=±2.052%, PI99%=±5.027%, CI[215765.600, 224807.656], PI[209212.417, 231360.839]) 219.142 +0.100%
GET rps 1 9 1473027.426 (n=5, σ=11416.504, CV=0.78%, CI99%=±1.596%, PI99%=±3.909%, CI[1449520.687, 1496534.165], PI[1415447.911, 1530606.941]) 1505518.476 (n=5, σ=3652.458, CV=0.24%, CI99%=±0.500%, PI99%=±1.224%, CI[1497998.015, 1513038.937], PI[1487097.184, 1523939.768]) 32491.050 +2.206%
GET rps 10 1 1126717.002 (n=5, σ=8092.281, CV=0.72%, CI99%=±1.479%, PI99%=±3.622%, CI[1110054.884, 1143379.120], PI[1085903.316, 1167530.688]) 1150473.324 (n=5, σ=3264.875, CV=0.28%, CI99%=±0.584%, PI99%=±1.431%, CI[1143750.901, 1157195.747], PI[1134006.817, 1166939.831]) 23756.322 +2.108%
GET rps 10 9 1999997.824 (n=5, σ=13226.920, CV=0.66%, CI99%=±1.362%, PI99%=±3.336%, CI[1972763.413, 2027232.235], PI[1933287.413, 2066708.235]) 2141217.350 (n=5, σ=19857.084, CV=0.93%, CI99%=±1.909%, PI99%=±4.677%, CI[2100331.340, 2182103.360], PI[2041067.488, 2241367.212]) 141219.526 +7.061%
SET rps 1 1 211759.908 (n=5, σ=467.697, CV=0.22%, CI99%=±0.455%, PI99%=±1.114%, CI[210796.913, 212722.903], PI[209401.062, 214118.754]) 211661.686 (n=5, σ=2206.528, CV=1.04%, CI99%=±2.146%, PI99%=±5.258%, CI[207118.414, 216204.958], PI[200532.988, 222790.384]) -98.222 -0.046%
SET rps 1 9 1470519.598 (n=5, σ=6081.216, CV=0.41%, CI99%=±0.851%, PI99%=±2.086%, CI[1457998.290, 1483040.906], PI[1439848.783, 1501190.413]) 1487413.626 (n=5, σ=4914.275, CV=0.33%, CI99%=±0.680%, PI99%=±1.666%, CI[1477295.066, 1497532.186], PI[1462628.317, 1512198.935]) 16894.028 +1.149%
SET rps 10 1 1008830.076 (n=5, σ=5009.395, CV=0.50%, CI99%=±1.022%, PI99%=±2.504%, CI[998515.662, 1019144.490], PI[983565.024, 1034095.128]) 1022825.588 (n=5, σ=5580.085, CV=0.55%, CI99%=±1.123%, PI99%=±2.752%, CI[1011336.117, 1034315.059], PI[994682.246, 1050968.930]) 13995.512 +1.387%
SET rps 10 9 1768675.626 (n=5, σ=23700.001, CV=1.34%, CI99%=±2.759%, PI99%=±6.758%, CI[1719876.997, 1817474.255], PI[1649143.885, 1888207.367]) 1829266.950 (n=5, σ=14880.914, CV=0.81%, CI99%=±1.675%, PI99%=±4.103%, CI[1798626.943, 1859906.957], PI[1754214.566, 1904319.334]) 60591.324 +3.426%

@dvkashapov
Copy link
Member Author

dvkashapov commented Jan 23, 2026

I think awesome results for such change, definitely worth it!
Not sure why one test failed though, it passes locally on ARM:

*** [err]: LATENCY GRAPH can output the event graph in tests/unit/latency-monitor.tcl
Expected '498' to be more than or equal to '500' (context: type eval line 12 cmd {assert_morethan_equal $high 500} proc ::test)

@zuiderkwast
Copy link
Contributor

I think awesome results for such change, definitely worth it!

Yes, it's very impressive.

Not sure why one test failed though

I have a guess: We "calibrate" the TSC clock by counting clock ticks and comparing it with a sleep and the monotonic clock, like this:

                /* Calibrate TSC against CLOCK_MONOTONIC */
                struct timespec start, end;
                uint64_t tsc_start, tsc_end;

                clock_gettime(CLOCK_MONOTONIC, &start);
                tsc_start = __rdtsc();
                usleep(10000); /* Sleep for 10ms */
                tsc_end = __rdtsc();
                clock_gettime(CLOCK_MONOTONIC, &end);

                uint64_t elapsed_us = (end.tv_sec - start.tv_sec) * 1000000ULL + (end.tv_nsec - start.tv_nsec) / 1000;
                uint64_t tsc_elapsed = tsc_end - tsc_start;
                long sample_ticksPerMicrosecond = tsc_elapsed / elapsed_us;

                /* Use the maximum out of TSC_CALIBRATION_ITERATIONS iterations for accuracy */
                if (sample_ticksPerMicrosecond > mono_ticksPerMicrosecond) {
                    mono_ticksPerMicrosecond = sample_ticksPerMicrosecond;
                }

This calibration might be inexact, so we get a too high value for mono_ticksPerMicrosecond and that means our latency measurements become too low. The failing test case did a DEBUG SLEEP 0.5 and TSC clock says it took 0.498 seconds.

The calibration code above compares it to clock_gettime, but clock_gettime takes some time itself to run. Perhaps we should instead:

  • compare the rdtsc delta to the usleep time instead of to the clock_gettime delta (if usleep is faster, but I guess usleep may be a syscall too)
  • or compare over a longer time period

To compare it over a very long time, we could do asyncronously after the server has started, for exaple in the serverCron. We can start up the server using posix clock and after a long calibration of one minute or so, compare to clock_gettime and then switch to TSC clock...

@zuiderkwast
Copy link
Contributor

It may also the that /proc/cpuinfo reported a model name with a frequency but the precision of this frequency is not great. (We use this if it contains a frequency in GHz, such as "Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz"). Maybe using the calibration code would gives us a more exact value than the reported frequency in the model name. (?)

@dvkashapov
Copy link
Member Author

It may also the that /proc/cpuinfo reported a model name with a frequency but the precision of this frequency is not great. (We use this if it contains a frequency in GHz, such as "Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz"). Maybe using the calibration code would gives us a more exact value than the reported frequency in the model name. (?)

Because x86 runner was most likely Intel, somehow I think this is closer to the truth, I will investigate how both methods compare against each other for AMD/Intel

@JimB123
Copy link
Member

JimB123 commented Jan 26, 2026

To compare it over a very long time, we could do asyncronously after the server has started, for exaple in the serverCron. We can start up the server using posix clock and after a long calibration of one minute or so, compare to clock_gettime and then switch to TSC clock...

This is problematic. If someone has started a timer, and is currently timing something, we can't just change the clock asynchronously.

Signed-off-by: Daniil Kashapov <daniil.kashapov.ykt@gmail.com>
@dvkashapov dvkashapov added the run-extra-tests Run extra tests on this PR (Runs all tests from daily except valgrind and RESP) label Jan 28, 2026
@dvkashapov
Copy link
Member Author

On some Intel systems it was observed that TSC ticksPerMicrosecond derived from /proc/cpuinfo was different from calibrated ticksPerMicrosecond or kernel derived one, so it was decided to always calibrate and look for constant_tsc flag

@github-actions github-actions bot removed the run-extra-tests Run extra tests on this PR (Runs all tests from daily except valgrind and RESP) label Jan 28, 2026
@zuiderkwast zuiderkwast merged commit 0422fc6 into valkey-io:unstable Jan 28, 2026
64 of 65 checks passed
@github-project-automation github-project-automation bot moved this from In Progress to Done in Valkey 9.1 Jan 28, 2026
@zuiderkwast zuiderkwast added the release-notes This issue should get a line item in the release notes label Jan 28, 2026
@dvkashapov dvkashapov deleted the hw-clock-default branch January 29, 2026 02:48
zuiderkwast pushed a commit that referenced this pull request Jan 30, 2026
After #3103 time sensitive `test-ubuntu-reclaim-cache` started to fail
because now startup always includes 30ms of calibration of HW clock,
that's why we get this output:

```
Run echo "test SAVE doesn't increase cache"
test SAVE doesn't increase cache
2460491776
Could not connect to Valkey at 127.0.0.1:8080: Connection refused
```

Added waits for server to start, locally run, it helps

---------

Signed-off-by: Daniil Kashapov <daniil.kashapov.ykt@gmail.com>
Copy link
Member

@JimB123 JimB123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. It was interesting to find some of the results of this research.

immangat pushed a commit to immangat/valkey that referenced this pull request Feb 1, 2026
After valkey-io#3103 time sensitive `test-ubuntu-reclaim-cache` started to fail
because now startup always includes 30ms of calibration of HW clock,
that's why we get this output:

```
Run echo "test SAVE doesn't increase cache"
test SAVE doesn't increase cache
2460491776
Could not connect to Valkey at 127.0.0.1:8080: Connection refused
```

Added waits for server to start, locally run, it helps

---------

Signed-off-by: Daniil Kashapov <daniil.kashapov.ykt@gmail.com>
roshkhatri pushed a commit to roshkhatri/valkey that referenced this pull request Feb 4, 2026
After valkey-io#3103 time sensitive `test-ubuntu-reclaim-cache` started to fail
because now startup always includes 30ms of calibration of HW clock,
that's why we get this output:

```
Run echo "test SAVE doesn't increase cache"
test SAVE doesn't increase cache
2460491776
Could not connect to Valkey at 127.0.0.1:8080: Connection refused
```

Added waits for server to start, locally run, it helps

---------

Signed-off-by: Daniil Kashapov <daniil.kashapov.ykt@gmail.com>
Signed-off-by: Roshan Khatri <rvkhatri@amazon.com>
roshkhatri pushed a commit to roshkhatri/valkey that referenced this pull request Feb 4, 2026
After valkey-io#3103 time sensitive `test-ubuntu-reclaim-cache` started to fail
because now startup always includes 30ms of calibration of HW clock,
that's why we get this output:

```
Run echo "test SAVE doesn't increase cache"
test SAVE doesn't increase cache
2460491776
Could not connect to Valkey at 127.0.0.1:8080: Connection refused
```

Added waits for server to start, locally run, it helps

---------

Signed-off-by: Daniil Kashapov <daniil.kashapov.ykt@gmail.com>
Signed-off-by: Roshan Khatri <rvkhatri@amazon.com>
roshkhatri pushed a commit to roshkhatri/valkey that referenced this pull request Feb 4, 2026
After valkey-io#3103 time sensitive `test-ubuntu-reclaim-cache` started to fail
because now startup always includes 30ms of calibration of HW clock,
that's why we get this output:

```
Run echo "test SAVE doesn't increase cache"
test SAVE doesn't increase cache
2460491776
Could not connect to Valkey at 127.0.0.1:8080: Connection refused
```

Added waits for server to start, locally run, it helps

---------

Signed-off-by: Daniil Kashapov <daniil.kashapov.ykt@gmail.com>
Signed-off-by: Roshan Khatri <rvkhatri@amazon.com>
roshkhatri pushed a commit to roshkhatri/valkey that referenced this pull request Feb 4, 2026
After valkey-io#3103 time sensitive `test-ubuntu-reclaim-cache` started to fail
because now startup always includes 30ms of calibration of HW clock,
that's why we get this output:

```
Run echo "test SAVE doesn't increase cache"
test SAVE doesn't increase cache
2460491776
Could not connect to Valkey at 127.0.0.1:8080: Connection refused
```

Added waits for server to start, locally run, it helps

---------

Signed-off-by: Daniil Kashapov <daniil.kashapov.ykt@gmail.com>
Signed-off-by: Roshan Khatri <rvkhatri@amazon.com>
roshkhatri pushed a commit to roshkhatri/valkey that referenced this pull request Feb 17, 2026
After valkey-io#3103 time sensitive `test-ubuntu-reclaim-cache` started to fail
because now startup always includes 30ms of calibration of HW clock,
that's why we get this output:

```
Run echo "test SAVE doesn't increase cache"
test SAVE doesn't increase cache
2460491776
Could not connect to Valkey at 127.0.0.1:8080: Connection refused
```

Added waits for server to start, locally run, it helps

---------

Signed-off-by: Daniil Kashapov <daniil.kashapov.ykt@gmail.com>
Signed-off-by: Roshan Khatri <rvkhatri@amazon.com>
roshkhatri pushed a commit to roshkhatri/valkey that referenced this pull request Feb 18, 2026
After valkey-io#3103 time sensitive `test-ubuntu-reclaim-cache` started to fail
because now startup always includes 30ms of calibration of HW clock,
that's why we get this output:

```
Run echo "test SAVE doesn't increase cache"
test SAVE doesn't increase cache
2460491776
Could not connect to Valkey at 127.0.0.1:8080: Connection refused
```

Added waits for server to start, locally run, it helps

---------

Signed-off-by: Daniil Kashapov <daniil.kashapov.ykt@gmail.com>
Signed-off-by: Roshan Khatri <rvkhatri@amazon.com>
roshkhatri pushed a commit to roshkhatri/valkey that referenced this pull request Feb 19, 2026
After valkey-io#3103 time sensitive `test-ubuntu-reclaim-cache` started to fail
because now startup always includes 30ms of calibration of HW clock,
that's why we get this output:

```
Run echo "test SAVE doesn't increase cache"
test SAVE doesn't increase cache
2460491776
Could not connect to Valkey at 127.0.0.1:8080: Connection refused
```

Added waits for server to start, locally run, it helps

---------

Signed-off-by: Daniil Kashapov <daniil.kashapov.ykt@gmail.com>
Signed-off-by: Roshan Khatri <rvkhatri@amazon.com>
harrylin98 pushed a commit to harrylin98/valkey_forked that referenced this pull request Feb 19, 2026
On some Intel systems it was observed that TSC `ticksPerMicrosecond`
derived from `/proc/cpuinfo` was different from calibrated
`ticksPerMicrosecond` or kernel derived one, so it was decided to always
calibrate and look for `constant_tsc` flag.

Resolves valkey-io#2597

---------

Signed-off-by: Daniil Kashapov <daniil.kashapov.ykt@gmail.com>
harrylin98 pushed a commit to harrylin98/valkey_forked that referenced this pull request Feb 19, 2026
After valkey-io#3103 time sensitive `test-ubuntu-reclaim-cache` started to fail
because now startup always includes 30ms of calibration of HW clock,
that's why we get this output:

```
Run echo "test SAVE doesn't increase cache"
test SAVE doesn't increase cache
2460491776
Could not connect to Valkey at 127.0.0.1:8080: Connection refused
```

Added waits for server to start, locally run, it helps

---------

Signed-off-by: Daniil Kashapov <daniil.kashapov.ykt@gmail.com>
roshkhatri pushed a commit to roshkhatri/valkey that referenced this pull request Feb 20, 2026
After valkey-io#3103 time sensitive `test-ubuntu-reclaim-cache` started to fail
because now startup always includes 30ms of calibration of HW clock,
that's why we get this output:

```
Run echo "test SAVE doesn't increase cache"
test SAVE doesn't increase cache
2460491776
Could not connect to Valkey at 127.0.0.1:8080: Connection refused
```

Added waits for server to start, locally run, it helps

---------

Signed-off-by: Daniil Kashapov <daniil.kashapov.ykt@gmail.com>
Signed-off-by: Roshan Khatri <rvkhatri@amazon.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-notes This issue should get a line item in the release notes

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Set the default to enable CPU clock for monotonic time tracking

3 participants