SDS(Simple Dynamic Strings)是一个C语言字符串库,设计中增加了从堆上分配内存的字符串,来扩充有限的libc字符处理的功能,使得:
这和在你的项目中拷贝sds.c和sds.h文件一样简单。代码很小,每个C99编译器应该都不带任何问题地搞定。
使用更简便
二进制安全
计算更有效率
而且仍旧…兼容一般的C字符串功能
https://github.com/antirez/sds
便于使用。我们可以像使用C语言中字符串一样接受和使用SDS字符串。
二进制安全。SDS字符串在头部使用len字段表示了buf中被使用了的空间长度,也就是说buf空间内容可以不用以NULL结尾。这种设计可以让SDS字符串承载包括NULL在内的一些二进制数据。
执行高效。在字符串连接过程中,如果每次连接都要重新分配内存以承载更多的数据,会导致效率下降。而我们在SDS字符串头结构中看到有用于保存已分配空间的长度和已使用的空间的长度。
当然它也有相应的缺陷:
可能要经常分配空间。一般一个字符串在第一次执行连接操作时,会发生原来字符串空间被释放,新空间被申请的过程。虽然作者做了优化,但是这个操作还是在所难免的。
非线程安全的。我们在代码中没有看到任何线程安全性的辅助操作,所以它是线程非安全的。
用到的结构体:
struct sdshdr {
int len;
int free;
char buf[];};
如你所见,这个结构体可能与某个传统的字符串库类似,但是结构体的buf域是不同的,因为它不是一个指针,而是一个没有声明任何长度的数组,
所以buf实际上指向了紧跟叫free的整数后的第一个字节。
所以为了创建一个SDS字符串,我们只要分配一片内存,其大小为sdshdr结构体加上我们的字符串长度,外加一个额外的字节,这是为了所有SDS字符串硬性需要的空字符。
结构体的len域显而易见,就是当前的SDS字符串的长度,每当字符串被通过SDS函数调用修改时,总是会被重新计算。
而free域表示了在当前分配空间中的空闲内存的数量,可以被用来存储更多的字符。
因为SDS致力于高效,它负担不起在每次添加新数据时,重新分配字符串,因为这会非常的低效,所以会使用每次你扩大字符串时,预分配一些空闲空间。
所使用的预分配算法如下:每次字符串为了保存更多的字节而被重新分配时,实际进行分配的大小是最小需求的两倍。
例如,如果字符串现在保存了30个字节,我们多连接2个字节,SDS总共会分配64个字节,而非32个。
然而,可进行分配的空间有一个硬性限制,被定义为SDS_MAX_PREALLOC。SDS绝不会分配超过1MB的额外空间(默认的,你可以修改这个默认值)。
对堆检查工具(Heap Checker)的影响
因为SDS返回一个指向由malloc分配的内存块的中间,堆检查工具可能会有些问题,但是:
常用的Valgrind程序会发现SDS字符串是可能丢失的内存,但并不是确定丢失,所以还是可以容易知道是否有泄露。我用Valgrind和Redis许多年,每一个真的泄露都会被检测为“确定丢失”。
OSX工具不会把SDS字符串检测为泄露,并能够正确操作指向内存块中间的指针。
系统调用的零复制
此时,通过阅读代码,你应该已经拥有所有挖掘更多SDS库内幕的工具了。
然而,还有一个有趣的模式,你可以应用导出的底层API,它在Redis内部使用过,用来改进网络代码性能。