Skip to content

Commit 0ee4234

Browse files
authored
Remove internal server object pointer overhead in small strings (valkey-io#2516)
Achieved 20-30% reduction in overhead. This was accomplished by reusing the 8B robj->ptr memory to store embedded data. For 16B key and 16B value, this reduces the per-item memory overhead by ~20%. Overall performance was not measurably changed. Raising the threshold for embedding strings produces ~30% reduction in per-item overhead for affected value sizes. Here's what I did: 1. Modify all `robj->ptr` access to use get/set methods. Most of this was done with a script (code listed below), with a few manual touches. Manual and programmatic changes are in separate commits to make review easier. 2. Next I changed `objectGetVal()` to calculate the location instead of using `robj->ptr`, and modified the embedding code to start writing embedded data 8B earlier, overwriting the ptr location. I changed `objectSetVal()` to assert that no value is embedded - all of the code that assigned `o->ptr` would have been incorrect for embedded strings anyway. (Except for one instance in module.c which I made a separate method for.) --------- Signed-off-by: Rain Valentine <rsg000@gmail.com>
1 parent 9f7cfc7 commit 0ee4234

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1918
-1692
lines changed

src/acl.c

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1516,7 +1516,7 @@ void ACLInit(void) {
15161516
* ENOENT: if the specified user does not exist at all.
15171517
*/
15181518
int ACLCheckUserCredentials(robj *username, robj *password) {
1519-
user *u = ACLGetUserByName(username->ptr, sdslen(username->ptr));
1519+
user *u = ACLGetUserByName(objectGetVal(username), sdslen(objectGetVal(username)));
15201520
if (u == NULL) {
15211521
errno = ENOENT;
15221522
return C_ERR;
@@ -1536,7 +1536,7 @@ int ACLCheckUserCredentials(robj *username, robj *password) {
15361536
listIter li;
15371537
listNode *ln;
15381538
listRewind(u->passwords, &li);
1539-
sds hashed = ACLHashPassword(password->ptr, sdslen(password->ptr));
1539+
sds hashed = ACLHashPassword(objectGetVal(password), sdslen(objectGetVal(password)));
15401540
while ((ln = listNext(&li))) {
15411541
sds thispass = listNodeValue(ln);
15421542
if (!time_independent_strcmp(hashed, thispass, HASH_PASSWORD_LEN)) {
@@ -1564,7 +1564,7 @@ void addAuthErrReply(client *c, robj *err) {
15641564
addReplyError(c, "-WRONGPASS invalid username-password pair or user is disabled.");
15651565
return;
15661566
}
1567-
addReplyError(c, err->ptr);
1567+
addReplyError(c, objectGetVal(err));
15681568
}
15691569

15701570
/* This is like ACLCheckUserCredentials(), however if the user/pass
@@ -1576,18 +1576,18 @@ static int checkPasswordBasedAuth(client *c, robj *username, robj *password) {
15761576
int result;
15771577

15781578
if (ACLCheckUserCredentials(username, password) == C_OK) {
1579-
user *user = ACLGetUserByName(username->ptr, sdslen(username->ptr));
1579+
user *user = ACLGetUserByName(objectGetVal(username), sdslen(objectGetVal(username)));
15801580
clientSetUser(c, user, 1);
15811581
moduleNotifyUserChanged(c);
15821582
result = AUTH_OK;
15831583
} else {
1584-
addACLLogEntry(c, ACL_DENIED_AUTH, (c->flag.multi) ? ACL_LOG_CTX_MULTI : ACL_LOG_CTX_TOPLEVEL, 0, username->ptr,
1584+
addACLLogEntry(c, ACL_DENIED_AUTH, (c->flag.multi) ? ACL_LOG_CTX_MULTI : ACL_LOG_CTX_TOPLEVEL, 0, objectGetVal(username),
15851585
NULL);
15861586
result = AUTH_ERR;
15871587
}
15881588

15891589
moduleFireAuthenticationEvent(c->id,
1590-
username->ptr,
1590+
objectGetVal(username),
15911591
NULL,
15921592
result == AUTH_OK);
15931593

@@ -1861,7 +1861,7 @@ static int ACLSelectorCheckCmd(aclSelector *selector,
18611861
while (1) {
18621862
if (selector->allowed_firstargs[id][subid] == NULL) return ACL_DENIED_CMD;
18631863
int idx = cmd->parent ? 2 : 1;
1864-
if (!strcasecmp(argv[idx]->ptr, selector->allowed_firstargs[id][subid]))
1864+
if (!strcasecmp(objectGetVal(argv[idx]), selector->allowed_firstargs[id][subid]))
18651865
break; /* First argument match found. Stop here. */
18661866
subid++;
18671867
}
@@ -1880,7 +1880,7 @@ static int ACLSelectorCheckCmd(aclSelector *selector,
18801880
keyReference *resultidx = result->keys;
18811881
for (int j = 0; j < result->numkeys; j++) {
18821882
int idx = resultidx[j].pos;
1883-
ret = ACLSelectorCheckKey(selector, argv[idx]->ptr, sdslen(argv[idx]->ptr), resultidx[j].flags, false);
1883+
ret = ACLSelectorCheckKey(selector, objectGetVal(argv[idx]), sdslen(objectGetVal(argv[idx])), resultidx[j].flags, false);
18841884
if (ret != ACL_OK) {
18851885
if (keyidxptr) *keyidxptr = resultidx[j].pos;
18861886
return ret;
@@ -1901,7 +1901,7 @@ static int ACLSelectorCheckCmd(aclSelector *selector,
19011901
if (!(channelref[j].flags & channel_flags)) continue;
19021902
int is_pattern = channelref[j].flags & CMD_CHANNEL_PATTERN;
19031903
int ret =
1904-
ACLCheckChannelAgainstList(selector->channels, argv[idx]->ptr, sdslen(argv[idx]->ptr), is_pattern);
1904+
ACLCheckChannelAgainstList(selector->channels, objectGetVal(argv[idx]), sdslen(objectGetVal(argv[idx])), is_pattern);
19051905
if (ret != ACL_OK) {
19061906
if (keyidxptr) *keyidxptr = channelref[j].pos;
19071907
getKeysFreeResult(&channels);
@@ -2132,7 +2132,7 @@ static int ACLShouldKillPubsubClient(client *c, list *upcoming) {
21322132
void *next;
21332133
while (!kill && hashtableNext(&iter, &next)) {
21342134
robj *o = next;
2135-
int res = ACLCheckChannelAgainstList(upcoming, o->ptr, sdslen(o->ptr), 1);
2135+
int res = ACLCheckChannelAgainstList(upcoming, objectGetVal(o), sdslen(objectGetVal(o)), 1);
21362136
kill = (res == ACL_DENIED_CHANNEL);
21372137
}
21382138
hashtableCleanupIterator(&iter);
@@ -2145,7 +2145,7 @@ static int ACLShouldKillPubsubClient(client *c, list *upcoming) {
21452145
void *next;
21462146
while (!kill && hashtableNext(&iter, &next)) {
21472147
robj *o = next;
2148-
int res = ACLCheckChannelAgainstList(upcoming, o->ptr, sdslen(o->ptr), 0);
2148+
int res = ACLCheckChannelAgainstList(upcoming, objectGetVal(o), sdslen(objectGetVal(o)), 0);
21492149
kill = (res == ACL_DENIED_CHANNEL);
21502150
}
21512151
hashtableCleanupIterator(&iter);
@@ -2157,7 +2157,7 @@ static int ACLShouldKillPubsubClient(client *c, list *upcoming) {
21572157
void *next;
21582158
while (!kill && hashtableNext(&iter, &next)) {
21592159
robj *o = next;
2160-
int res = ACLCheckChannelAgainstList(upcoming, o->ptr, sdslen(o->ptr), 0);
2160+
int res = ACLCheckChannelAgainstList(upcoming, objectGetVal(o), sdslen(objectGetVal(o)), 0);
21612161
kill = (res == ACL_DENIED_CHANNEL);
21622162
}
21632163
hashtableCleanupIterator(&iter);
@@ -2659,7 +2659,7 @@ static int ACLSaveToFile(const char *filename) {
26592659
user = sdscatsds(user, u->name);
26602660
user = sdscatlen(user, " ", 1);
26612661
robj *descr = ACLDescribeUser(u);
2662-
user = sdscatsds(user, descr->ptr);
2662+
user = sdscatsds(user, objectGetVal(descr));
26632663
decrRefCount(descr);
26642664
acl = sdscatsds(acl, user);
26652665
acl = sdscatlen(acl, "\n", 1);
@@ -2855,10 +2855,10 @@ void addACLLogEntry(client *c, int reason, int context, int argpos, sds username
28552855
} else {
28562856
switch (reason) {
28572857
case ACL_DENIED_CMD: le->object = sdsdup(c->cmd->fullname); break;
2858-
case ACL_DENIED_KEY: le->object = sdsdup(c->argv[argpos]->ptr); break;
2859-
case ACL_DENIED_CHANNEL: le->object = sdsdup(c->argv[argpos]->ptr); break;
2860-
case ACL_DENIED_DB: le->object = argpos ? sdsdup(c->argv[argpos]->ptr) : sdsdup(c->cmd->fullname); break;
2861-
case ACL_DENIED_AUTH: le->object = sdsdup(c->argv[0]->ptr); break;
2858+
case ACL_DENIED_KEY: le->object = sdsdup(objectGetVal(c->argv[argpos])); break;
2859+
case ACL_DENIED_CHANNEL: le->object = sdsdup(objectGetVal(c->argv[argpos])); break;
2860+
case ACL_DENIED_DB: le->object = argpos ? sdsdup(objectGetVal(c->argv[argpos])) : sdsdup(c->cmd->fullname); break;
2861+
case ACL_DENIED_AUTH: le->object = sdsdup(objectGetVal(c->argv[0])); break;
28622862
default: le->object = sdsempty();
28632863
}
28642864
}
@@ -3061,15 +3061,15 @@ static int aclAddReplySelectorDescription(client *c, aclSelector *s) {
30613061
* ACL LOG [<count> | RESET]
30623062
*/
30633063
void aclCommand(client *c) {
3064-
char *sub = c->argv[1]->ptr;
3064+
char *sub = objectGetVal(c->argv[1]);
30653065
if (!strcasecmp(sub, "setuser") && c->argc >= 3) {
30663066
/* Initially redact all of the arguments to not leak any information
30673067
* about the user. */
30683068
for (int j = 2; j < c->argc; j++) {
30693069
redactClientCommandArgument(c, j);
30703070
}
30713071

3072-
sds username = c->argv[2]->ptr;
3072+
sds username = objectGetVal(c->argv[2]);
30733073
/* Check username validity. */
30743074
if (ACLStringHasSpaces(username, sdslen(username))) {
30753075
addReplyError(c, "Usernames can't contain spaces or null characters");
@@ -3079,7 +3079,7 @@ void aclCommand(client *c) {
30793079
user *u = ACLGetUserByName(username, sdslen(username));
30803080

30813081
sds *temp_argv = zmalloc(c->argc * sizeof(sds));
3082-
for (int i = 3; i < c->argc; i++) temp_argv[i - 3] = c->argv[i]->ptr;
3082+
for (int i = 3; i < c->argc; i++) temp_argv[i - 3] = objectGetVal(c->argv[i]);
30833083

30843084
sds error = ACLStringSetUser(u, username, temp_argv, c->argc - 3);
30853085
zfree(temp_argv);
@@ -3096,15 +3096,15 @@ void aclCommand(client *c) {
30963096

30973097
int deleted = 0;
30983098
for (int j = 2; j < c->argc; j++) {
3099-
sds username = c->argv[j]->ptr;
3099+
sds username = objectGetVal(c->argv[j]);
31003100
if (!strcmp(username, "default")) {
31013101
addReplyError(c, "The 'default' user cannot be removed");
31023102
return;
31033103
}
31043104
}
31053105

31063106
for (int j = 2; j < c->argc; j++) {
3107-
sds username = c->argv[j]->ptr;
3107+
sds username = objectGetVal(c->argv[j]);
31083108
user *u;
31093109
if (raxRemove(Users, (unsigned char *)username, sdslen(username), (void **)&u)) {
31103110
ACLFreeUserAndKillClients(u);
@@ -3116,7 +3116,7 @@ void aclCommand(client *c) {
31163116
/* Redact the username to not leak any information about the user. */
31173117
redactClientCommandArgument(c, 2);
31183118

3119-
user *u = ACLGetUserByName(c->argv[2]->ptr, sdslen(c->argv[2]->ptr));
3119+
user *u = ACLGetUserByName(objectGetVal(c->argv[2]), sdslen(objectGetVal(c->argv[2])));
31203120
if (u == NULL) {
31213121
addReplyNull(c);
31223122
return;
@@ -3177,7 +3177,7 @@ void aclCommand(client *c) {
31773177
config = sdscatsds(config, u->name);
31783178
config = sdscatlen(config, " ", 1);
31793179
robj *descr = ACLDescribeUser(u);
3180-
config = sdscatsds(config, descr->ptr);
3180+
config = sdscatsds(config, objectGetVal(descr));
31813181
decrRefCount(descr);
31823182
addReplyBulkSds(c, config);
31833183
}
@@ -3216,9 +3216,9 @@ void aclCommand(client *c) {
32163216
for (j = 0; ACLCommandCategories[j].flag != 0; j++) addReplyBulkCString(c, ACLCommandCategories[j].name);
32173217
setDeferredArrayLen(c, dl, j);
32183218
} else if (!strcasecmp(sub, "cat") && c->argc == 3) {
3219-
uint64_t cflag = ACLGetCommandCategoryFlagByName(c->argv[2]->ptr);
3219+
uint64_t cflag = ACLGetCommandCategoryFlagByName(objectGetVal(c->argv[2]));
32203220
if (cflag == 0) {
3221-
addReplyErrorFormat(c, "Unknown category '%.128s'", (char *)c->argv[2]->ptr);
3221+
addReplyErrorFormat(c, "Unknown category '%.128s'", (char *)objectGetVal(c->argv[2]));
32223222
return;
32233223
}
32243224
int arraylen = 0;
@@ -3251,7 +3251,7 @@ void aclCommand(client *c) {
32513251
* the number of entries the user wants to display, or alternatively
32523252
* the "RESET" command in order to flush the old entries. */
32533253
if (c->argc == 3) {
3254-
if (!strcasecmp(c->argv[2]->ptr, "reset")) {
3254+
if (!strcasecmp(objectGetVal(c->argv[2]), "reset")) {
32553255
listSetFreeMethod(ACLLog, ACLFreeLogEntry);
32563256
listEmpty(ACLLog);
32573257
listSetFreeMethod(ACLLog, NULL);
@@ -3320,14 +3320,14 @@ void aclCommand(client *c) {
33203320
}
33213321
} else if (!strcasecmp(sub, "dryrun") && c->argc >= 4) {
33223322
struct serverCommand *cmd;
3323-
user *u = ACLGetUserByName(c->argv[2]->ptr, sdslen(c->argv[2]->ptr));
3323+
user *u = ACLGetUserByName(objectGetVal(c->argv[2]), sdslen(objectGetVal(c->argv[2])));
33243324
if (u == NULL) {
3325-
addReplyErrorFormat(c, "User '%s' not found", (char *)c->argv[2]->ptr);
3325+
addReplyErrorFormat(c, "User '%s' not found", (char *)objectGetVal(c->argv[2]));
33263326
return;
33273327
}
33283328

33293329
if ((cmd = lookupCommand(c->argv + 3, c->argc - 3)) == NULL) {
3330-
addReplyErrorFormat(c, "Command '%s' not found", (char *)c->argv[3]->ptr);
3330+
addReplyErrorFormat(c, "Command '%s' not found", (char *)objectGetVal(c->argv[3]));
33313331
return;
33323332
}
33333333

@@ -3340,7 +3340,7 @@ void aclCommand(client *c) {
33403340
int dbid = (c->flag.multi) ? c->mstate->transaction_db_id : c->db->id;
33413341
int result = ACLCheckAllUserCommandPerm(u, cmd, c->argv + 3, c->argc - 3, dbid, &idx);
33423342
if (result != ACL_OK) {
3343-
sds err = getAclErrorMessage(result, u, cmd, c->argv[idx + 3]->ptr, 1);
3343+
sds err = getAclErrorMessage(result, u, cmd, objectGetVal(c->argv[idx + 3]), 1);
33443344
addReplyBulkSds(c, err);
33453345
return;
33463346
}

src/aof.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1280,11 +1280,11 @@ sds catAppendOnlyGenericCommand(sds dst, int argc, robj **argv) {
12801280
for (j = 0; j < argc; j++) {
12811281
o = getDecodedObject(argv[j]);
12821282
buf[0] = '$';
1283-
len = 1 + ll2string(buf + 1, sizeof(buf) - 1, sdslen(o->ptr));
1283+
len = 1 + ll2string(buf + 1, sizeof(buf) - 1, sdslen(objectGetVal(o)));
12841284
buf[len++] = '\r';
12851285
buf[len++] = '\n';
12861286
dst = sdscatlen(dst, buf, len);
1287-
dst = sdscatlen(dst, o->ptr, sdslen(o->ptr));
1287+
dst = sdscatlen(dst, objectGetVal(o), sdslen(objectGetVal(o)));
12881288
dst = sdscatlen(dst, "\r\n", 2);
12891289
decrRefCount(o);
12901290
}
@@ -1778,9 +1778,9 @@ int rioWriteBulkObject(rio *r, robj *obj) {
17781778
/* Avoid using getDecodedObject to help copy-on-write (we are often
17791779
* in a child process when this function is called). */
17801780
if (obj->encoding == OBJ_ENCODING_INT) {
1781-
return rioWriteBulkLongLong(r, (long)obj->ptr);
1781+
return rioWriteBulkLongLong(r, (long)objectGetVal(obj));
17821782
} else if (sdsEncodedObject(obj)) {
1783-
return rioWriteBulkString(r, obj->ptr, sdslen(obj->ptr));
1783+
return rioWriteBulkString(r, objectGetVal(obj), sdslen(objectGetVal(obj)));
17841784
} else {
17851785
serverPanic("Unknown string encoding");
17861786
}
@@ -1860,7 +1860,7 @@ int rewriteSortedSetObject(rio *r, robj *key, robj *o) {
18601860
long long count = 0, items = zsetLength(o);
18611861

18621862
if (o->encoding == OBJ_ENCODING_LISTPACK) {
1863-
unsigned char *zl = o->ptr;
1863+
unsigned char *zl = objectGetVal(o);
18641864
unsigned char *eptr, *sptr;
18651865
unsigned char *vstr;
18661866
unsigned int vlen;
@@ -1895,7 +1895,7 @@ int rewriteSortedSetObject(rio *r, robj *key, robj *o) {
18951895
items--;
18961896
}
18971897
} else if (o->encoding == OBJ_ENCODING_SKIPLIST) {
1898-
zset *zs = o->ptr;
1898+
zset *zs = objectGetVal(o);
18991899
hashtableIterator iter;
19001900
hashtableInitIterator(&iter, zs->ht, 0);
19011901
void *next;
@@ -2069,7 +2069,7 @@ int rioWriteStreamEmptyConsumer(rio *r,
20692069
/* Emit the commands needed to rebuild a stream object.
20702070
* The function returns 0 on error, 1 on success. */
20712071
int rewriteStreamObject(rio *r, robj *key, robj *o) {
2072-
stream *s = o->ptr;
2072+
stream *s = objectGetVal(o);
20732073
streamIterator si;
20742074
streamIteratorStart(&si, s, NULL, NULL, 0);
20752075
streamID id;
@@ -2190,7 +2190,7 @@ int rewriteStreamObject(rio *r, robj *key, robj *o) {
21902190
* The function returns 0 on error, 1 on success. */
21912191
int rewriteModuleObject(rio *r, robj *key, robj *o, int dbid) {
21922192
ValkeyModuleIO io;
2193-
moduleValue *mv = o->ptr;
2193+
moduleValue *mv = objectGetVal(o);
21942194
moduleType *mt = mv->type;
21952195
moduleInitIOContext(&io, mt, r, key, dbid);
21962196
mt->aof_rewrite(&io, key, mv->value);

0 commit comments

Comments
 (0)