Reuse lookupCommand data on consecutive same command calls on main thread (#13764)

We can see that on fast commands and fast pipeline use-cases,
lookupCommand() takes 1.9% to 3.4% of total cpu cyles (depending on
pipeline). In cases in which consecutives commands are the same we can
avoid the call to lookupCommand() completely without changing or adding
new fields to the client struct (we simply reuse the info already
avaiable in lastcmd). This change can represent an improvement of around
4.4% in QPS on the high pipeline use-cases.

---------

Co-authored-by: debing.sun <debing.sun@redis.com>
This commit is contained in:
Filipe Oliveira (Redis)
2025-02-24 12:33:14 +08:00
committed by GitHub
co-authored by debing.sun
parent ee933d9e2b
commit 3f06ddfb7b
+19 -2
View File
@@ -80,12 +80,25 @@ struct redisServer server; /* Server global state */
/*============================ Internal prototypes ========================== */
static inline int isShutdownInitiated(void);
static inline int isCommandReusable(struct redisCommand *cmd, robj *commandArg);
int isReadyToShutdown(void);
int finishShutdown(void);
const char *replstateToString(int replstate);
/*============================ Utility functions ============================ */
/* Check if a given command can be reused without performing a lookup.
* A command is reusable if:
* - It is not NULL.
* - It does not have subcommands (subcommands_dict == NULL).
* This preserves simplicity on the check and accounts for the majority of the use cases.
* - Its full name matches the provided command argument. */
static inline int isCommandReusable(struct redisCommand *cmd, robj *commandArg) {
return cmd != NULL &&
cmd->subcommands_dict == NULL &&
strcasecmp(cmd->fullname, commandArg->ptr) == 0;
}
/* This macro tells if we are in the context of loading an AOF. */
#define isAOFLoadingContext() \
((server.current_client && server.current_client->id == CLIENT_ID_AOF) ? 1 : 0)
@@ -3979,8 +3992,12 @@ int processCommand(client *c) {
* In case we are reprocessing a command after it was blocked,
* we do not have to repeat the same checks */
if (!client_reprocessing_command) {
struct redisCommand *cmd = c->iolookedcmd ? c->iolookedcmd : lookupCommand(c->argv, c->argc);
/* check if we can reuse the last command instead of looking up if we already have that info */
struct redisCommand *cmd = NULL;
if (isCommandReusable(c->lastcmd, c->argv[0]))
cmd = c->lastcmd;
else
cmd = c->iolookedcmd ? c->iolookedcmd : lookupCommand(c->argv, c->argc);
if (!cmd) {
/* Handle possible security attacks. */
if (!strcasecmp(c->argv[0]->ptr,"host:") || !strcasecmp(c->argv[0]->ptr,"post")) {