Skip to content

Commit 8110ba8

Browse files
oranagraantirez
authored andcommitted
optimize memory usage of deferred replies
When deffered reply is added the previous reply node cannot be used so all the extra space we allocated in it is wasted. in case someone uses deffered replies in a loop, each time adding a small reply, each of these reply nodes (the small string reply) would have consumed a 16k block. now when we add anther diferred reply node, we trim the unused portion of the previous reply block. see #7123
1 parent e4d2bb6 commit 8110ba8

File tree

1 file changed

+31
-0
lines changed

1 file changed

+31
-0
lines changed

src/networking.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,13 +436,44 @@ void addReplyStatusFormat(client *c, const char *fmt, ...) {
436436
sdsfree(s);
437437
}
438438

439+
/* Sometimes we are forced to create a new reply node, and we can't append to
440+
* the previous one, when that happens, we wanna try to trim the unused space
441+
* at the end of the last reply node which we won't use anymore. */
442+
void trimReplyUnusedTailSpace(client *c) {
443+
listNode *ln = listLast(c->reply);
444+
clientReplyBlock *tail = ln? listNodeValue(ln): NULL;
445+
446+
/* Note that 'tail' may be NULL even if we have a tail node, becuase when
447+
* addDeferredMultiBulkLength() is used */
448+
if (!tail) return;
449+
450+
/* We only try to trim the space is relatively high (more than a 1/4 of the
451+
* allocation), otherwise there's a high chance realloc will NOP.
452+
* Also, to avoid large memmove which happens as part of realloc, we only do
453+
* that if the used part is small. */
454+
if (tail->size - tail->used > tail->size / 4 &&
455+
tail->used < PROTO_REPLY_CHUNK_BYTES)
456+
{
457+
size_t old_size = tail->size;
458+
tail = zrealloc(tail, tail->used + sizeof(clientReplyBlock));
459+
/* If realloc was a NOP, we got the same value which has internal frag */
460+
if (tail == listNodeValue(ln)) return;
461+
/* take over the allocation's internal fragmentation (at least for
462+
* memory usage tracking) */
463+
tail->size = zmalloc_usable(tail) - sizeof(clientReplyBlock);
464+
c->reply_bytes += tail->size - old_size;
465+
listNodeValue(ln) = tail;
466+
}
467+
}
468+
439469
/* Adds an empty object to the reply list that will contain the multi bulk
440470
* length, which is not known when this function is called. */
441471
void *addReplyDeferredLen(client *c) {
442472
/* Note that we install the write event here even if the object is not
443473
* ready to be sent, since we are sure that before returning to the
444474
* event loop setDeferredAggregateLen() will be called. */
445475
if (prepareClientToWrite(c) != C_OK) return NULL;
476+
trimReplyUnusedTailSpace(c);
446477
listAddNodeTail(c->reply,NULL); /* NULL is our placeholder. */
447478
return listLast(c->reply);
448479
}

0 commit comments

Comments
 (0)