From 3aeca5533eb5b1cb2e1cba478ce2fdbf9609a41b Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Tue, 2 Jun 2026 21:16:25 -0400 Subject: [PATCH] perf: Fuse redis_cmd_append_sstr into a single reserve and write redis_cmd_append_sstr frames one RESP bulk argument and runs for every argument of every command (the hottest packing symbol; all the typed _int/_long/_dbl/_zval/_zstr/_key appenders funnel through it). It did five separately bounds-checked smart_string appends per argument. Format the length prefix up front so the whole frame size is known, reserve it with one smart_string_alloc, then write the frame directly. Same bytes on the wire; one capacity check instead of five. A ZEND_ASSERT documents the existing non-negative-length precondition (a negative length would mean a caller truncated a >INT_MAX size_t into the int parameter; that int-length contract is pervasive in the packing layer and unchanged here). Verified on PHP 8.4: value lengths across page boundaries (0, 4095/96/97, 65535/36, 1MB), large/negative integer args, float scores, binary key/value with NUL+CRLF, 500-arg MGET, 300-field HSET, no leaks under report_memleaks, and 33/34 project suite methods (the one failure, testXRange, fails identically on develop and is unrelated). --- library.c | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/library.c b/library.c index 2c036be3a8..9c7a69ca73 100644 --- a/library.c +++ b/library.c @@ -1111,11 +1111,37 @@ int redis_cmd_init_sstr(smart_string *str, int num_args, char *keyword, int keyw * Append a command sequence to a smart_string */ int redis_cmd_append_sstr(smart_string *str, char *append, int append_len) { - smart_string_appendc(str, '$'); - smart_string_append_long(str, append_len); - smart_string_appendl(str, _NL, sizeof(_NL) - 1); - smart_string_appendl(str, append, append_len); - smart_string_appendl(str, _NL, sizeof(_NL) - 1); + /* Frame one RESP bulk argument ($\r\n\r\n). We format the + * length prefix first so the whole frame size is known, reserve it with a + * single capacity check, then write it directly. This is the per-argument + * hot path for every command, so it avoids the five separate + * bounds-checked smart_string appends the naive form would perform. */ + char nbuf[32]; + char *digits; + size_t dlen, need; + char *p; + + /* A negative length means a caller truncated a >INT_MAX size_t into our + * int parameter; the int-length contract is pervasive in the packing + * layer, so assert it rather than emit a malformed frame. */ + ZEND_ASSERT(append_len >= 0); + + digits = zend_print_long_to_buf(nbuf + sizeof(nbuf) - 1, append_len); + dlen = (nbuf + sizeof(nbuf) - 1) - digits; + need = 1 + dlen + 2 + (size_t)append_len + 2; + + smart_string_alloc(str, need, 0); + p = str->c + str->len; + *p++ = '$'; + memcpy(p, digits, dlen); + p += dlen; + *p++ = '\r'; + *p++ = '\n'; + memcpy(p, append, append_len); + p += append_len; + *p++ = '\r'; + *p++ = '\n'; + str->len += need; /* Return our new length */ return str->len;