mirror of
https://github.com/fluencelabs/redis
synced 2025-04-02 15:51:05 +00:00
Defrag: activate it only if running modified version of Jemalloc.
This commit also includes minor aesthetic changes like removal of trailing spaces.
This commit is contained in:
parent
5ab6a54cc6
commit
173d692bc2
@ -100,3 +100,7 @@
|
|||||||
# define JEMALLOC_RESTRICT_RETURN
|
# define JEMALLOC_RESTRICT_RETURN
|
||||||
# define JEMALLOC_ALLOCATOR
|
# define JEMALLOC_ALLOCATOR
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* This version of Jemalloc, modified for Redis, has the je_get_defrag_hint()
|
||||||
|
* function. */
|
||||||
|
#define JEMALLOC_FRAG_HINT
|
||||||
|
44
src/defrag.c
44
src/defrag.c
@ -39,14 +39,14 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#if defined(USE_JEMALLOC) && defined(MALLOCX_TCACHE_NONE)
|
#ifdef HAVE_DEFRAG
|
||||||
|
|
||||||
/* this method was added to jemalloc in order to help us understand which
|
/* this method was added to jemalloc in order to help us understand which
|
||||||
* pointers are worthwhile moving and which aren't */
|
* pointers are worthwhile moving and which aren't */
|
||||||
int je_get_defrag_hint(void* ptr, int *bin_util, int *run_util);
|
int je_get_defrag_hint(void* ptr, int *bin_util, int *run_util);
|
||||||
|
|
||||||
/* Defrag helper for generic allocations.
|
/* Defrag helper for generic allocations.
|
||||||
*
|
*
|
||||||
* returns NULL in case the allocatoin wasn't moved.
|
* returns NULL in case the allocatoin wasn't moved.
|
||||||
* when it returns a non-null value, the old pointer was already released
|
* when it returns a non-null value, the old pointer was already released
|
||||||
* and should NOT be accessed. */
|
* and should NOT be accessed. */
|
||||||
@ -58,13 +58,13 @@ void* activeDefragAlloc(void *ptr) {
|
|||||||
server.stat_active_defrag_misses++;
|
server.stat_active_defrag_misses++;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
/* if this run is more utilized than the average utilization in this bin (or it is full), skip it.
|
/* if this run is more utilized than the average utilization in this bin (or it is full), skip it.
|
||||||
* this will eventually move all the allocations from relatively empty runs into relatively full runs. */
|
* this will eventually move all the allocations from relatively empty runs into relatively full runs. */
|
||||||
if (run_util > bin_util || run_util == 1<<16) {
|
if (run_util > bin_util || run_util == 1<<16) {
|
||||||
server.stat_active_defrag_misses++;
|
server.stat_active_defrag_misses++;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
/* move this allocation to a new allocation.
|
/* move this allocation to a new allocation.
|
||||||
* make sure not to use the thread cache. so that we don't get back the same pointers we try to free */
|
* make sure not to use the thread cache. so that we don't get back the same pointers we try to free */
|
||||||
size = zmalloc_size(ptr);
|
size = zmalloc_size(ptr);
|
||||||
newptr = zmalloc_no_tcache(size);
|
newptr = zmalloc_no_tcache(size);
|
||||||
@ -74,7 +74,7 @@ void* activeDefragAlloc(void *ptr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*Defrag helper for sds strings
|
/*Defrag helper for sds strings
|
||||||
*
|
*
|
||||||
* returns NULL in case the allocatoin wasn't moved.
|
* returns NULL in case the allocatoin wasn't moved.
|
||||||
* when it returns a non-null value, the old pointer was already released
|
* when it returns a non-null value, the old pointer was already released
|
||||||
* and should NOT be accessed. */
|
* and should NOT be accessed. */
|
||||||
@ -90,7 +90,7 @@ sds activeDefragSds(sds sdsptr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Defrag helper for robj and/or string objects
|
/* Defrag helper for robj and/or string objects
|
||||||
*
|
*
|
||||||
* returns NULL in case the allocatoin wasn't moved.
|
* returns NULL in case the allocatoin wasn't moved.
|
||||||
* when it returns a non-null value, the old pointer was already released
|
* when it returns a non-null value, the old pointer was already released
|
||||||
* and should NOT be accessed. */
|
* and should NOT be accessed. */
|
||||||
@ -221,7 +221,7 @@ double *zslDefrag(zskiplist *zsl, double score, sds oldele, sds newele) {
|
|||||||
x = x->level[i].forward;
|
x = x->level[i].forward;
|
||||||
update[i] = x;
|
update[i] = x;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* update the robj pointer inside the skip list record. */
|
/* update the robj pointer inside the skip list record. */
|
||||||
x = x->level[0].forward;
|
x = x->level[0].forward;
|
||||||
serverAssert(x && score == x->score && x->ele==oldele);
|
serverAssert(x && score == x->score && x->ele==oldele);
|
||||||
@ -243,7 +243,7 @@ double *zslDefrag(zskiplist *zsl, double score, sds oldele, sds newele) {
|
|||||||
* newkey may be null if the key pointer wasn't moved.
|
* newkey may be null if the key pointer wasn't moved.
|
||||||
* return value is the the dictEntry if found, or NULL if not found.
|
* return value is the the dictEntry if found, or NULL if not found.
|
||||||
* NOTE: this is very ugly code, but it let's us avoid the complication of doing a scan on another dict. */
|
* NOTE: this is very ugly code, but it let's us avoid the complication of doing a scan on another dict. */
|
||||||
dictEntry* replaceSateliteDictKeyPtrAndOrDifragDictEntry(dict *d, sds oldkey, sds newkey, unsigned int hash, int *defragged) {
|
dictEntry* replaceSateliteDictKeyPtrAndOrDefragDictEntry(dict *d, sds oldkey, sds newkey, unsigned int hash, int *defragged) {
|
||||||
dictEntry **deref = dictFindEntryRefByPtrAndHash(d, oldkey, hash);
|
dictEntry **deref = dictFindEntryRefByPtrAndHash(d, oldkey, hash);
|
||||||
if (deref) {
|
if (deref) {
|
||||||
dictEntry *de = *deref;
|
dictEntry *de = *deref;
|
||||||
@ -269,7 +269,7 @@ int defargKey(redisDb *db, dictEntry *de) {
|
|||||||
dictIterator *di;
|
dictIterator *di;
|
||||||
int defragged = 0;
|
int defragged = 0;
|
||||||
sds newsds;
|
sds newsds;
|
||||||
|
|
||||||
/* try to defrag the key name */
|
/* try to defrag the key name */
|
||||||
newsds = activeDefragSds(keysds);
|
newsds = activeDefragSds(keysds);
|
||||||
if (newsds)
|
if (newsds)
|
||||||
@ -279,7 +279,7 @@ int defargKey(redisDb *db, dictEntry *de) {
|
|||||||
* i can't search in db->expires for that key after i already released the pointer it holds
|
* i can't search in db->expires for that key after i already released the pointer it holds
|
||||||
* it won't be able to do the string compare */
|
* it won't be able to do the string compare */
|
||||||
unsigned int hash = dictGetHash(db->dict, de->key);
|
unsigned int hash = dictGetHash(db->dict, de->key);
|
||||||
replaceSateliteDictKeyPtrAndOrDifragDictEntry(db->expires, keysds, newsds, hash, &defragged);
|
replaceSateliteDictKeyPtrAndOrDefragDictEntry(db->expires, keysds, newsds, hash, &defragged);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* try to defrag robj and / or string value */
|
/* try to defrag robj and / or string value */
|
||||||
@ -334,7 +334,7 @@ int defargKey(redisDb *db, dictEntry *de) {
|
|||||||
} else if (ob->encoding == OBJ_ENCODING_INTSET) {
|
} else if (ob->encoding == OBJ_ENCODING_INTSET) {
|
||||||
intset *is = ob->ptr;
|
intset *is = ob->ptr;
|
||||||
intset *newis = activeDefragAlloc(is);
|
intset *newis = activeDefragAlloc(is);
|
||||||
if (newis)
|
if (newis)
|
||||||
defragged++, ob->ptr = newis;
|
defragged++, ob->ptr = newis;
|
||||||
} else {
|
} else {
|
||||||
serverPanic("Unknown set encoding");
|
serverPanic("Unknown set encoding");
|
||||||
@ -407,7 +407,7 @@ void defragScanCallback(void *privdata, const dictEntry *de) {
|
|||||||
if(defragged)
|
if(defragged)
|
||||||
server.stat_active_defrag_key_hits++;
|
server.stat_active_defrag_key_hits++;
|
||||||
else
|
else
|
||||||
server.stat_active_defrag_key_misses++;
|
server.stat_active_defrag_key_misses++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* defrag scan callback for for each hash table bicket,
|
/* defrag scan callback for for each hash table bicket,
|
||||||
@ -439,8 +439,8 @@ float getAllocatorFragmentation(size_t *out_frag_bytes) {
|
|||||||
size_t rss_bytes = resident - allocated;
|
size_t rss_bytes = resident - allocated;
|
||||||
if(out_frag_bytes)
|
if(out_frag_bytes)
|
||||||
*out_frag_bytes = frag_bytes;
|
*out_frag_bytes = frag_bytes;
|
||||||
serverLog(LL_DEBUG,
|
serverLog(LL_DEBUG,
|
||||||
"allocated=%zu, active=%zu, resident=%zu, frag=%.0f%% (%.0f%% rss), frag_bytes=%zu (%zu%% rss)",
|
"allocated=%zu, active=%zu, resident=%zu, frag=%.0f%% (%.0f%% rss), frag_bytes=%zu (%zu%% rss)",
|
||||||
allocated, active, resident, frag_pct, rss_pct, frag_bytes, rss_bytes);
|
allocated, active, resident, frag_pct, rss_pct, frag_bytes, rss_bytes);
|
||||||
return frag_pct;
|
return frag_pct;
|
||||||
}
|
}
|
||||||
@ -459,10 +459,10 @@ void activeDefragCycle(void) {
|
|||||||
unsigned int iterations = 0;
|
unsigned int iterations = 0;
|
||||||
unsigned long long defragged = server.stat_active_defrag_hits;
|
unsigned long long defragged = server.stat_active_defrag_hits;
|
||||||
long long start, timelimit;
|
long long start, timelimit;
|
||||||
|
|
||||||
if (server.aof_child_pid!=-1 || server.rdb_child_pid!=-1)
|
if (server.aof_child_pid!=-1 || server.rdb_child_pid!=-1)
|
||||||
return; /* defragging memory while there's a fork will just do damage. */
|
return; /* defragging memory while there's a fork will just do damage. */
|
||||||
|
|
||||||
/* once a second, check if we the fragmentation justfies starting a scan or making it more aggressive */
|
/* once a second, check if we the fragmentation justfies starting a scan or making it more aggressive */
|
||||||
run_with_period(1000) {
|
run_with_period(1000) {
|
||||||
size_t frag_bytes;
|
size_t frag_bytes;
|
||||||
@ -472,16 +472,16 @@ void activeDefragCycle(void) {
|
|||||||
if(frag_pct < server.active_defrag_threshold_lower || frag_bytes < server.active_defrag_ignore_bytes)
|
if(frag_pct < server.active_defrag_threshold_lower || frag_bytes < server.active_defrag_ignore_bytes)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* calculate the adaptive aggressiveness of the defrag */
|
/* calculate the adaptive aggressiveness of the defrag */
|
||||||
int cpu_pct = INTERPOLATE(frag_pct, server.active_defrag_threshold_lower, server.active_defrag_threshold_upper,
|
int cpu_pct = INTERPOLATE(frag_pct, server.active_defrag_threshold_lower, server.active_defrag_threshold_upper,
|
||||||
server.active_defrag_cycle_min, server.active_defrag_cycle_max);
|
server.active_defrag_cycle_min, server.active_defrag_cycle_max);
|
||||||
cpu_pct = LIMIT(cpu_pct, server.active_defrag_cycle_min, server.active_defrag_cycle_max);
|
cpu_pct = LIMIT(cpu_pct, server.active_defrag_cycle_min, server.active_defrag_cycle_max);
|
||||||
/* we allow increasing the aggressiveness during a scan, but don't reduce it */
|
/* we allow increasing the aggressiveness during a scan, but don't reduce it */
|
||||||
if (!server.active_defrag_running || cpu_pct > server.active_defrag_running) {
|
if (!server.active_defrag_running || cpu_pct > server.active_defrag_running) {
|
||||||
server.active_defrag_running = cpu_pct;
|
server.active_defrag_running = cpu_pct;
|
||||||
serverLog(LL_VERBOSE,
|
serverLog(LL_VERBOSE,
|
||||||
"Starting active defrag, frag=%.0f%%, frag_bytes=%zu, cpu=%d%%",
|
"Starting active defrag, frag=%.0f%%, frag_bytes=%zu, cpu=%d%%",
|
||||||
frag_pct, frag_bytes, cpu_pct);
|
frag_pct, frag_bytes, cpu_pct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -500,7 +500,7 @@ void activeDefragCycle(void) {
|
|||||||
long long now = ustime();
|
long long now = ustime();
|
||||||
size_t frag_bytes;
|
size_t frag_bytes;
|
||||||
float frag_pct = getAllocatorFragmentation(&frag_bytes);
|
float frag_pct = getAllocatorFragmentation(&frag_bytes);
|
||||||
serverLog(LL_VERBOSE,
|
serverLog(LL_VERBOSE,
|
||||||
"Active defrag done in %dms, reallocated=%d, frag=%.0f%%, frag_bytes=%zu",
|
"Active defrag done in %dms, reallocated=%d, frag=%.0f%%, frag_bytes=%zu",
|
||||||
(int)((now - start_scan)/1000), (int)(server.stat_active_defrag_hits - start_stat), frag_pct, frag_bytes);
|
(int)((now - start_scan)/1000), (int)(server.stat_active_defrag_hits - start_stat), frag_pct, frag_bytes);
|
||||||
|
|
||||||
@ -536,7 +536,7 @@ void activeDefragCycle(void) {
|
|||||||
} while(1);
|
} while(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else /* USE_JEMALLOC */
|
#else /* HAVE_DEFRAG */
|
||||||
|
|
||||||
void activeDefragCycle(void) {
|
void activeDefragCycle(void) {
|
||||||
/* not implemented yet*/
|
/* not implemented yet*/
|
||||||
|
@ -119,8 +119,8 @@ void *zmalloc(size_t size) {
|
|||||||
|
|
||||||
/* Allocation and free functions that bypass the thread cache
|
/* Allocation and free functions that bypass the thread cache
|
||||||
* and go straight to the allocator arena bins.
|
* and go straight to the allocator arena bins.
|
||||||
* Currently implemented only for jemalloc */
|
* Currently implemented only for jemalloc. Used for online defragmentation. */
|
||||||
#if defined(USE_JEMALLOC) && defined(MALLOCX_TCACHE_NONE)
|
#ifdef HAVE_DEFRAG
|
||||||
void *zmalloc_no_tcache(size_t size) {
|
void *zmalloc_no_tcache(size_t size) {
|
||||||
void *ptr = mallocx(size+PREFIX_SIZE, MALLOCX_TCACHE_NONE);
|
void *ptr = mallocx(size+PREFIX_SIZE, MALLOCX_TCACHE_NONE);
|
||||||
if (!ptr) zmalloc_oom_handler(size);
|
if (!ptr) zmalloc_oom_handler(size);
|
||||||
|
@ -65,12 +65,17 @@
|
|||||||
#define ZMALLOC_LIB "libc"
|
#define ZMALLOC_LIB "libc"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* We can enable the Redis defrag capabilities only if we are using Jemalloc
|
||||||
|
* and the version used is our special version modified for Redis having
|
||||||
|
* the ability to return per-allocation fragmentation hints. */
|
||||||
|
#if defined(USE_JEMALLOC) && defined(JEMALLOC_FRAG_HINT)
|
||||||
|
#define HAVE_DEFRAG
|
||||||
|
#endif
|
||||||
|
|
||||||
void *zmalloc(size_t size);
|
void *zmalloc(size_t size);
|
||||||
void *zcalloc(size_t size);
|
void *zcalloc(size_t size);
|
||||||
void *zrealloc(void *ptr, size_t size);
|
void *zrealloc(void *ptr, size_t size);
|
||||||
void zfree(void *ptr);
|
void zfree(void *ptr);
|
||||||
void zfree_no_tcache(void *ptr);
|
|
||||||
void *zmalloc_no_tcache(size_t size);
|
|
||||||
char *zstrdup(const char *s);
|
char *zstrdup(const char *s);
|
||||||
size_t zmalloc_used_memory(void);
|
size_t zmalloc_used_memory(void);
|
||||||
void zmalloc_enable_thread_safeness(void);
|
void zmalloc_enable_thread_safeness(void);
|
||||||
@ -82,6 +87,11 @@ size_t zmalloc_get_smap_bytes_by_field(char *field, long pid);
|
|||||||
size_t zmalloc_get_memory_size(void);
|
size_t zmalloc_get_memory_size(void);
|
||||||
void zlibc_free(void *ptr);
|
void zlibc_free(void *ptr);
|
||||||
|
|
||||||
|
#ifdef HAVE_DEFRAG
|
||||||
|
void zfree_no_tcache(void *ptr);
|
||||||
|
void *zmalloc_no_tcache(size_t size);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef HAVE_MALLOC_SIZE
|
#ifndef HAVE_MALLOC_SIZE
|
||||||
size_t zmalloc_size(void *ptr);
|
size_t zmalloc_size(void *ptr);
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user