Optimize number of realloc syscall during multi/exec flow (#10921)

## Issue
During the MULTI/EXEC flow, each command gets queued until the `EXEC`
command is received and during this phase on every command queue, a
`realloc` is being invoked. This could be expensive based on the realloc
behavior (if copy to a new memory location). 


## Solution
In order to reduce the no. of syscall, couple of optimization I've used.

1. By default, reserve memory for atleast two commands. `MULTI/EXEC` for a
single command doesn't have any significance. Hence, I believe customer wouldn't use it.
2. For further reservation, increase the memory allocation in exponent growth (power of 2).
This reduces the no. of `realloc` call from `N` to `log(N)` times.

## Other changes:

* Include multi exec queued command array in client memory consumption calculation
(affects client eviction too)
This commit is contained in:
Harkrishn Patro 2022-07-03 23:47:34 -07:00 committed by GitHub
parent 33b7ff387c
commit a3704d4e87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 14 additions and 3 deletions

View File

@ -38,6 +38,7 @@ void initClientMultiState(client *c) {
c->mstate.cmd_flags = 0; c->mstate.cmd_flags = 0;
c->mstate.cmd_inv_flags = 0; c->mstate.cmd_inv_flags = 0;
c->mstate.argv_len_sums = 0; c->mstate.argv_len_sums = 0;
c->mstate.alloc_count = 0;
} }
/* Release all the resources associated with MULTI/EXEC state */ /* Release all the resources associated with MULTI/EXEC state */
@ -65,9 +66,16 @@ void queueMultiCommand(client *c, uint64_t cmd_flags) {
* aborted. */ * aborted. */
if (c->flags & (CLIENT_DIRTY_CAS|CLIENT_DIRTY_EXEC)) if (c->flags & (CLIENT_DIRTY_CAS|CLIENT_DIRTY_EXEC))
return; return;
if (c->mstate.count == 0) {
c->mstate.commands = zrealloc(c->mstate.commands, /* If a client is using multi/exec, assuming it is used to execute at least
sizeof(multiCmd)*(c->mstate.count+1)); * two commands. Hence, creating by default size of 2. */
c->mstate.commands = zmalloc(sizeof(multiCmd)*2);
c->mstate.alloc_count = 2;
}
if (c->mstate.count == c->mstate.alloc_count) {
c->mstate.alloc_count = c->mstate.alloc_count < INT_MAX/2 ? c->mstate.alloc_count*2 : INT_MAX;
c->mstate.commands = zrealloc(c->mstate.commands, sizeof(multiCmd)*(c->mstate.alloc_count*2));
}
mc = c->mstate.commands+c->mstate.count; mc = c->mstate.commands+c->mstate.count;
mc->cmd = c->cmd; mc->cmd = c->cmd;
mc->argc = c->argc; mc->argc = c->argc;
@ -466,5 +474,7 @@ size_t multiStateMemOverhead(client *c) {
size_t mem = c->mstate.argv_len_sums; size_t mem = c->mstate.argv_len_sums;
/* Add watched keys overhead, Note: this doesn't take into account the watched keys themselves, because they aren't managed per-client. */ /* Add watched keys overhead, Note: this doesn't take into account the watched keys themselves, because they aren't managed per-client. */
mem += listLength(c->watched_keys) * (sizeof(listNode) + sizeof(watchedKey)); mem += listLength(c->watched_keys) * (sizeof(listNode) + sizeof(watchedKey));
/* Reserved memory for queued multi commands. */
mem += c->mstate.alloc_count * sizeof(multiCmd);
return mem; return mem;
} }

View File

@ -959,6 +959,7 @@ typedef struct multiState {
is possible to know if all the commands have a is possible to know if all the commands have a
certain flag. */ certain flag. */
size_t argv_len_sums; /* mem used by all commands arguments */ size_t argv_len_sums; /* mem used by all commands arguments */
int alloc_count; /* total number of multiCmd struct memory reserved. */
} multiState; } multiState;
/* This structure holds the blocking operation state for a client. /* This structure holds the blocking operation state for a client.