Skip to content

Commit 19f735c

Browse files
committed
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?
1 parent 0b0f872 commit 19f735c

File tree

1 file changed

+43
-7
lines changed

1 file changed

+43
-7
lines changed

src/object.c

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,33 @@ robj *createRawStringObject(char *ptr, size_t len) {
5252

5353
/* Create a string object with encoding REDIS_ENCODING_EMBSTR, that is
5454
* an object where the sds string is actually an unmodifiable string
55-
* allocated in the same chunk as the object itself. */
55+
* allocated in the same chunk as the object itself.
56+
*
57+
* We try to cache and reuse EMBSTR allocated objects according to
58+
* size classes modeled after the jemalloc size classes. */
59+
#define REDIS_ENCODING_EMBSTR_SIZE_LIMIT 39
60+
#define EMB_OBJ_CACHE_CLASSES 3 /* 32, 48, 64 */
61+
#define EMB_OBJ_CACHE_SIZE 1024
62+
robj *emb_obj_cache[EMB_OBJ_CACHE_CLASSES][EMB_OBJ_CACHE_SIZE];
63+
unsigned long emb_obj_cache_len[EMB_OBJ_CACHE_CLASSES];
64+
5665
robj *createEmbeddedStringObject(char *ptr, size_t len) {
57-
robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr)+len+1);
58-
struct sdshdr *sh = (void*)(o+1);
66+
robj *o;
67+
struct sdshdr *sh;
68+
int alloc_size = sizeof(robj)+sizeof(struct sdshdr)+len+1;
69+
int cache_class = (((alloc_size+15)-((alloc_size+15)&15))-32)/16;
70+
71+
/* Try to reuse a cached object. */
72+
if (cache_class < EMB_OBJ_CACHE_CLASSES && emb_obj_cache_len[cache_class]) {
73+
emb_obj_cache_len[cache_class]--;
74+
o = emb_obj_cache[cache_class][emb_obj_cache_len[cache_class]];
75+
} else {
76+
o = zmalloc(alloc_size);
77+
o->type = REDIS_STRING;
78+
o->encoding = REDIS_ENCODING_EMBSTR;
79+
}
5980

60-
o->type = REDIS_STRING;
61-
o->encoding = REDIS_ENCODING_EMBSTR;
81+
sh = (void*)(o+1);
6282
o->ptr = sh+1;
6383
o->refcount = 1;
6484
o->lru = LRU_CLOCK();
@@ -80,7 +100,6 @@ robj *createEmbeddedStringObject(char *ptr, size_t len) {
80100
*
81101
* The current limit of 39 is chosen so that the biggest string object
82102
* we allocate as EMBSTR will still fit into the 64 byte arena of jemalloc. */
83-
#define REDIS_ENCODING_EMBSTR_SIZE_LIMIT 39
84103
robj *createStringObject(char *ptr, size_t len) {
85104
if (len <= REDIS_ENCODING_EMBSTR_SIZE_LIMIT)
86105
return createEmbeddedStringObject(ptr,len);
@@ -215,7 +234,21 @@ robj *createZsetZiplistObject(void) {
215234
void freeStringObject(robj *o) {
216235
if (o->encoding == REDIS_ENCODING_RAW) {
217236
sdsfree(o->ptr);
237+
} else if (o->encoding == REDIS_ENCODING_EMBSTR) {
238+
struct sdshdr *sh = (void*)(o+1);
239+
int alloc_size = sizeof(robj)+sizeof(struct sdshdr)+sh->len+1;
240+
int cache_class = (((alloc_size+15)-((alloc_size+15)&15))-32)/16;
241+
242+
/* Try to cache the object instead of freeing it. */
243+
if (cache_class < EMB_OBJ_CACHE_CLASSES &&
244+
emb_obj_cache_len[cache_class] != EMB_OBJ_CACHE_SIZE)
245+
{
246+
emb_obj_cache[cache_class][emb_obj_cache_len[cache_class]] = o;
247+
emb_obj_cache_len[cache_class]++;
248+
return; /* Don't free the object. */
249+
}
218250
}
251+
zfree(o);
219252
}
220253

221254
void freeListObject(robj *o) {
@@ -229,6 +262,7 @@ void freeListObject(robj *o) {
229262
default:
230263
redisPanic("Unknown list encoding type");
231264
}
265+
zfree(o);
232266
}
233267

234268
void freeSetObject(robj *o) {
@@ -242,6 +276,7 @@ void freeSetObject(robj *o) {
242276
default:
243277
redisPanic("Unknown set encoding type");
244278
}
279+
zfree(o);
245280
}
246281

247282
void freeZsetObject(robj *o) {
@@ -259,6 +294,7 @@ void freeZsetObject(robj *o) {
259294
default:
260295
redisPanic("Unknown sorted set encoding");
261296
}
297+
zfree(o);
262298
}
263299

264300
void freeHashObject(robj *o) {
@@ -273,6 +309,7 @@ void freeHashObject(robj *o) {
273309
redisPanic("Unknown hash encoding type");
274310
break;
275311
}
312+
zfree(o);
276313
}
277314

278315
void incrRefCount(robj *o) {
@@ -290,7 +327,6 @@ void decrRefCount(robj *o) {
290327
case REDIS_HASH: freeHashObject(o); break;
291328
default: redisPanic("Unknown object type"); break;
292329
}
293-
zfree(o);
294330
} else {
295331
o->refcount--;
296332
}

0 commit comments

Comments
 (0)