1、内存对齐
1.1 对齐原因
CPU和总线是64位的,所以CPU每次可以从内存里面拿出8个字节,而这8个字节是对齐的,也就是说只能是0-7,8-15, 16-23这样的,不能从中间拿。这个时候你要读一个double,如果double的地址可以被8整除,那你就只需要跟内存要一次数据。如果不能整除,那你就得跟内存要2次数据。
1.2 对齐规则
- 结构体变量的起始地址能够被其最宽的成员大小整除
- 结构体每个成员相对于起始地址的偏移能够被其自身大小整除,如果不能则在前一个成员后面补充字节
- 结构体总体大小能够被最宽的成员的大小整除,如不能则在后面补充字节
如该结构体占用空间为12字节
struct A{
char a;
int b;
short c;
};
2、数据结构
struct __attribute__((__packed_) ) sdshdr8 {
uint8_t len; /* 已使用长度,用1字节存储*/
uint8_t alloc ; /*总长度,用1字节存储*/
unsigned char flags;/*低3位存储类型,高5位预留*/
char buf[];/*柔性数组,存放实际内容*/
};
struct _attribute__( (_packed__) ) sdshdr16 {
uint16_t len; /*已使用长度,用2字节存储*/
uint16_t alloc; /*总长度,用2字节存储*/
unsigned char flags;/*低3位存储类型,高5位预留*/
char buf[];/*柔性数组,存放实际内容*/
};
...
- len:表示 buf中已占用字节数。
- alloc:表示 buf中已分配字节数
- flags:标识当前结构体的类型,低3位用作标识位,高5位预留。
- buf:柔性数组,真正存储字符串的数据空间。
3. sds特性
1、二进制安全
- C 字符串内容 必须是 类似 ASCII 一样的编码,并且除了字符串结尾 不能有 空字符,否则会被提前当做 结尾,这样的话就没法 存储 图片、音频、视频、压缩文件等 二进制数据
- 而SDS 就不同了,依赖自带 的 len 属性,不会按照 字符串内容里的 空字符串 判断结尾。所以这一特性,使得 Redis 不仅支持 文本存储,还支持 多样性的二进制数据存储
2、获取长度时间复杂度O(1)
典型的空间换取时间
3、取消内存对齐
- 通过
_attribute__( (_packed__) )
取消内存对齐,节省空间 - sds返回给上层的不是结构体地址,而是指向内容的buf地址,无论是
sdshdr8
、sdshdr16
,都能通过buf[-1]定位到flag地址,同样定位到len、aloc地址
4、interesting code
#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (void*)((s)-(sizeof(struct sdshdr##T)));
#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
## 代表字符串拼接