diff options
author | antirez <antirez@gmail.com> | 2014-05-08 12:04:35 +0200 |
---|---|---|
committer | antirez <antirez@gmail.com> | 2014-05-08 12:36:34 +0200 |
commit | 19f735ce42a0612e6425c3872a1c2de70a063af7 (patch) | |
tree | 255e559da2cd9c75ab23865b8be83a74c3b4718c | |
parent | 0b0f872f3f1536e30b2fab0b4297ea960d8247e1 (diff) | |
download | redis-19f735ce42a0612e6425c3872a1c2de70a063af7.tar.gz |
Use a free list for EMBSTR objects creation / release.
To my surprise, mantaining a small poll of objects provides a very big
speedup even when the jemalloc allocator is used, which is supposed to
have arenas doing mostly the same work.
Probably this is a combination of different factors:
1) No need to manage zmalloc total memory used.
2) No locking of any kind because we can assume single thread, so
maintaining the free list may be cheaper for us compared to
jemalloc.
3) More locality because of a different cache/reuse pattern?
-rw-r--r-- | src/object.c | 50 |
1 files changed, 43 insertions, 7 deletions
diff --git a/src/object.c b/src/object.c index 46f1f3f8b..c65c58156 100644 --- a/src/object.c +++ b/src/object.c @@ -52,13 +52,33 @@ robj *createRawStringObject(char *ptr, size_t len) { /* Create a string object with encoding REDIS_ENCODING_EMBSTR, that is * an object where the sds string is actually an unmodifiable string - * allocated in the same chunk as the object itself. */ + * allocated in the same chunk as the object itself. + * + * We try to cache and reuse EMBSTR allocated objects according to + * size classes modeled after the jemalloc size classes. */ +#define REDIS_ENCODING_EMBSTR_SIZE_LIMIT 39 +#define EMB_OBJ_CACHE_CLASSES 3 /* 32, 48, 64 */ +#define EMB_OBJ_CACHE_SIZE 1024 +robj *emb_obj_cache[EMB_OBJ_CACHE_CLASSES][EMB_OBJ_CACHE_SIZE]; +unsigned long emb_obj_cache_len[EMB_OBJ_CACHE_CLASSES]; + robj *createEmbeddedStringObject(char *ptr, size_t len) { - robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr)+len+1); - struct sdshdr *sh = (void*)(o+1); + robj *o; + struct sdshdr *sh; + int alloc_size = sizeof(robj)+sizeof(struct sdshdr)+len+1; + int cache_class = (((alloc_size+15)-((alloc_size+15)&15))-32)/16; + + /* Try to reuse a cached object. */ + if (cache_class < EMB_OBJ_CACHE_CLASSES && emb_obj_cache_len[cache_class]) { + emb_obj_cache_len[cache_class]--; + o = emb_obj_cache[cache_class][emb_obj_cache_len[cache_class]]; + } else { + o = zmalloc(alloc_size); + o->type = REDIS_STRING; + o->encoding = REDIS_ENCODING_EMBSTR; + } - o->type = REDIS_STRING; - o->encoding = REDIS_ENCODING_EMBSTR; + sh = (void*)(o+1); o->ptr = sh+1; o->refcount = 1; o->lru = LRU_CLOCK(); @@ -80,7 +100,6 @@ robj *createEmbeddedStringObject(char *ptr, size_t len) { * * The current limit of 39 is chosen so that the biggest string object * we allocate as EMBSTR will still fit into the 64 byte arena of jemalloc. */ -#define REDIS_ENCODING_EMBSTR_SIZE_LIMIT 39 robj *createStringObject(char *ptr, size_t len) { if (len <= REDIS_ENCODING_EMBSTR_SIZE_LIMIT) return createEmbeddedStringObject(ptr,len); @@ -215,7 +234,21 @@ robj *createZsetZiplistObject(void) { void freeStringObject(robj *o) { if (o->encoding == REDIS_ENCODING_RAW) { sdsfree(o->ptr); + } else if (o->encoding == REDIS_ENCODING_EMBSTR) { + struct sdshdr *sh = (void*)(o+1); + int alloc_size = sizeof(robj)+sizeof(struct sdshdr)+sh->len+1; + int cache_class = (((alloc_size+15)-((alloc_size+15)&15))-32)/16; + + /* Try to cache the object instead of freeing it. */ + if (cache_class < EMB_OBJ_CACHE_CLASSES && + emb_obj_cache_len[cache_class] != EMB_OBJ_CACHE_SIZE) + { + emb_obj_cache[cache_class][emb_obj_cache_len[cache_class]] = o; + emb_obj_cache_len[cache_class]++; + return; /* Don't free the object. */ + } } + zfree(o); } void freeListObject(robj *o) { @@ -229,6 +262,7 @@ void freeListObject(robj *o) { default: redisPanic("Unknown list encoding type"); } + zfree(o); } void freeSetObject(robj *o) { @@ -242,6 +276,7 @@ void freeSetObject(robj *o) { default: redisPanic("Unknown set encoding type"); } + zfree(o); } void freeZsetObject(robj *o) { @@ -259,6 +294,7 @@ void freeZsetObject(robj *o) { default: redisPanic("Unknown sorted set encoding"); } + zfree(o); } void freeHashObject(robj *o) { @@ -273,6 +309,7 @@ void freeHashObject(robj *o) { redisPanic("Unknown hash encoding type"); break; } + zfree(o); } void incrRefCount(robj *o) { @@ -290,7 +327,6 @@ void decrRefCount(robj *o) { case REDIS_HASH: freeHashObject(o); break; default: redisPanic("Unknown object type"); break; } - zfree(o); } else { o->refcount--; } |