mirror of
https://github.com/redis/redis.git
synced 2026-02-18 18:20:31 -05:00
Reduce per command syscalls by reusing cached time when HW monotic clock is available (#14713)
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
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
This PR reduces per-command `ustime()` syscalls in `call()` by reusing cached time and batching wall-clock updates when HW monotonic time is available. ### What changed - Pass `server.ustime` to `enterExecutionUnit()` instead of calling `ustime()`. - Use HW monotonic clock to measure duration and accumulate it across commands. - Refresh cached time with `ustime()` only when accumulated duration > **10µs** or after **25 commands**. - Fallback to direct `ustime()` when HW monotonic clock isn’t available. ### Impact - `ustime` CPU: **4.58% → 0.25%**, which leads to ~4% boost on max QPS ### Notes - Time drift is bounded (≤10µs or 25 commands). - No behavior change on non-HW-monotonic systems. --------- Co-authored-by: Yuan Wang <yuan.wang@redis.com>
This commit is contained in:
parent
d099c10581
commit
3ff37ea815
2 changed files with 30 additions and 6 deletions
34
src/server.c
34
src/server.c
|
|
@ -3030,6 +3030,8 @@ void initServer(void) {
|
|||
server.last_sig_received = 0;
|
||||
memset(server.io_threads_clients_num, 0, sizeof(server.io_threads_clients_num));
|
||||
atomicSetWithSync(server.running, 0);
|
||||
server.accum_call_count_since_ustime = 0;
|
||||
server.monotonic_us_when_ustime = 0;
|
||||
|
||||
/* Initiate acl info struct */
|
||||
server.acl_info.invalid_cmd_accesses = 0;
|
||||
|
|
@ -3856,7 +3858,31 @@ void call(client *c, int flags) {
|
|||
long long old_master_repl_offset = server.master_repl_offset;
|
||||
incrCommandStatsOnError(NULL, 0);
|
||||
|
||||
const long long call_timer = ustime();
|
||||
/* Use monotonic clock if available, and update cached time if needed */
|
||||
const int use_hw_clock = monotonicGetType() == MONOTONIC_CLOCK_HW;
|
||||
monotime monotonic_start = 0;
|
||||
if (use_hw_clock) {
|
||||
monotonic_start = getMonotonicUs();
|
||||
if (server.execution_nesting == 0) {
|
||||
server.accum_call_count_since_ustime++;
|
||||
/* Sync cached time when monotonic clock moves more than 10us
|
||||
* or after 25 commands */
|
||||
if (monotonic_start - server.monotonic_us_when_ustime > 10 ||
|
||||
server.accum_call_count_since_ustime > 25)
|
||||
{
|
||||
updateCachedTime(0);
|
||||
/* Recalculate monotonic_start after time update as ustime()
|
||||
* in updateCachedTime() might have taken some time */
|
||||
monotonic_start = getMonotonicUs();
|
||||
server.monotonic_us_when_ustime = monotonic_start;
|
||||
server.accum_call_count_since_ustime = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Pass current server.ustime to avoid ustime() call if monotonic clock is used
|
||||
* and time will be updated before command execution based on monotonic clock. */
|
||||
const long long call_timer = use_hw_clock ? server.ustime : ustime();
|
||||
enterExecutionUnit(1, call_timer);
|
||||
|
||||
/* setting the CLIENT_EXECUTING_COMMAND flag so we will avoid
|
||||
|
|
@ -3865,10 +3891,6 @@ void call(client *c, int flags) {
|
|||
* re-processing and unblock the client.*/
|
||||
c->flags |= CLIENT_EXECUTING_COMMAND;
|
||||
|
||||
monotime monotonic_start = 0;
|
||||
if (monotonicGetType() == MONOTONIC_CLOCK_HW)
|
||||
monotonic_start = getMonotonicUs();
|
||||
|
||||
c->cmd->proc(c);
|
||||
|
||||
exitExecutionUnit();
|
||||
|
|
@ -3880,7 +3902,7 @@ void call(client *c, int flags) {
|
|||
/* In order to avoid performance implication due to querying the clock using a system call 3 times,
|
||||
* we use a monotonic clock, when we are sure its cost is very low, and fall back to non-monotonic call otherwise. */
|
||||
ustime_t duration;
|
||||
if (monotonicGetType() == MONOTONIC_CLOCK_HW)
|
||||
if (use_hw_clock)
|
||||
duration = getMonotonicUs() - monotonic_start;
|
||||
else
|
||||
duration = ustime() - call_timer;
|
||||
|
|
|
|||
|
|
@ -2391,6 +2391,8 @@ struct redisServer {
|
|||
redisAtomic int daylight_active; /* Currently in daylight saving time. */
|
||||
mstime_t mstime; /* 'unixtime' in milliseconds. */
|
||||
ustime_t ustime; /* 'unixtime' in microseconds. */
|
||||
int accum_call_count_since_ustime; /* Command count since last ustime update */
|
||||
monotime monotonic_us_when_ustime; /* Monotonic time when last ustime update */
|
||||
mstime_t cmd_time_snapshot; /* Time snapshot of the root execution nesting. */
|
||||
size_t blocking_op_nesting; /* Nesting level of blocking operation, used to reset blocked_last_cron. */
|
||||
long long blocked_last_cron; /* Indicate the mstime of the last time we did cron jobs from a blocking operation */
|
||||
|
|
|
|||
Loading…
Reference in a new issue