aof: write directly to server.aof_buf in feedAppendOnlyFile

Eliminate the per-call temporary sds buffer in feedAppendOnlyFile by
appending the timestamp annotation, SELECT command, and command body
directly into server.aof_buf. The previous code allocated an empty sds,
built the payload, then memcpy'd it into aof_buf and freed the temp,
costing one alloc + one copy + one free per propagated command.

Also move the AOF-active gate to the top of the function as an early
return. As a side effect, server.aof_selected_db is no longer updated
when the function would have discarded its output anyway, which is
actually more correct: stopAppendOnly() and the post-rewrite path both
reset aof_selected_db to -1, so the next live write naturally re-emits
SELECT when needed.

Microbenchmark (10M iterations of a 3-arg SET, median of 5 runs,
measured via a local DEBUG subcommand looping feedAppendOnlyFile):

  before: 163.51 ns/op  (~6.12M ops/sec)
  after :  73.03 ns/op  (~13.69M ops/sec)

About 2.2x faster on this hot path. Larger commands amplify the win
since the eliminated copy scaled with payload size.

Signed-off-by: charsyam <charsyam@gmail.com>
This commit is contained in:
charsyam 2026-04-13 21:26:52 +09:00
parent e1d35aca01
commit c80f6f438e

View file

@ -1407,15 +1407,19 @@ sds genAofTimestampAnnotationIfNeeded(int force) {
* argc - Number of values in argv
*/
void feedAppendOnlyFile(int dictid, robj **argv, int argc) {
sds buf = sdsempty();
serverAssert(dictid == -1 || (dictid >= 0 && dictid < server.dbnum));
if (!(server.aof_state == AOF_ON ||
(server.aof_state == AOF_WAIT_REWRITE && server.child_type == CHILD_TYPE_AOF)))
{
return;
}
/* Feed timestamp if needed */
if (server.aof_timestamp_enabled) {
sds ts = genAofTimestampAnnotationIfNeeded(0);
if (ts != NULL) {
buf = sdscatsds(buf, ts);
server.aof_buf = sdscatsds(server.aof_buf, ts);
sdsfree(ts);
}
}
@ -1426,25 +1430,15 @@ void feedAppendOnlyFile(int dictid, robj **argv, int argc) {
char seldb[64];
snprintf(seldb,sizeof(seldb),"%d",dictid);
buf = sdscatprintf(buf,"*2\r\n$6\r\nSELECT\r\n$%lu\r\n%s\r\n",
server.aof_buf = sdscatprintf(server.aof_buf,
"*2\r\n$6\r\nSELECT\r\n$%lu\r\n%s\r\n",
(unsigned long)strlen(seldb),seldb);
server.aof_selected_db = dictid;
}
/* All commands should be propagated the same way in AOF as in replication.
* No need for AOF-specific translation. */
buf = catAppendOnlyGenericCommand(buf,argc,argv);
/* Append to the AOF buffer. This will be flushed on disk just before
* of re-entering the event loop, so before the client will get a
* positive reply about the operation performed. */
if (server.aof_state == AOF_ON ||
(server.aof_state == AOF_WAIT_REWRITE && server.child_type == CHILD_TYPE_AOF))
{
server.aof_buf = sdscatlen(server.aof_buf, buf, sdslen(buf));
}
sdsfree(buf);
server.aof_buf = catAppendOnlyGenericCommand(server.aof_buf,argc,argv);
}
/* ----------------------------------------------------------------------------