Sharing SDS strings
If you are writing a program in which it is advantageous to share the same SDS string across different data structures, it is absolutely advised to encapsulate SDS strings into structures that remember the number of references of the string, with functions to increment and decrement the number of references.
This approach is a memory management technique called reference counting and in the context of SDS has two advantages:
- It is less likely that you'll create memory leaks or bugs due to non freeing SDS strings or freeing already freed strings.
- You'll not need to update every reference to an SDS string when you modify it (since the new SDS string may point to a different memory location).
While this is definitely a very common programming technique I'll outline the basic ideas here. You create a structure like that:
struct mySharedStrings {
int refcount;
sds string;
}
When new strings are created, the structure is allocated and returned with
refcount set to 1. The you have two functions to change the reference count
of the shared string:
incrementStringRefCountwill simply incrementrefcountof 1 in the structure. It will be called every time you add a reference to the string on some new data structure, variable, or whatever.decrementStringRefCountis used when you remove a reference. This function is however special since when therefcountdrops to zero, it automatically frees the SDS string, and themySharedStringstructure as well.
Interactions with heap checkers
Because SDS returns pointers into the middle of memory chunks allocated with
malloc, heap checkers may have issues, however:
- The popular Valgrind program will detect SDS strings are possibly lost memory and never as definitely lost, so it is easy to tell if there is a leak or not. I used Valgrind with Redis for years and every real leak was consistently detected as "definitely lost".
- OSX instrumentation tools don't detect SDS strings as leaks but are able to correctly handle pointers pointing to the middle of memory chunks.
Zero copy append from syscalls
At this point you should have all the tools to dig more inside the SDS library by reading the source code, however there is an interesting pattern you can mount using the low level API exported, that is used inside Redis in order to improve performances of the networking code.
Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the
following schema, to cat bytes coming from the kernel to the end of an
sds string without copying into an intermediate buffer:
oldlen = sdslen(s);
s = sdsMakeRoomFor(s, BUFFER_SIZE);
nread = read(fd, s+oldlen, BUFFER_SIZE);
... check for nread <= 0 and handle it ...
sdsIncrLen(s, nread);
sdsIncrLen is documented inside the source code of sds.c.