mirror of
https://github.com/fluencelabs/redis
synced 2025-03-19 17:10:50 +00:00
LFU: Use the LRU pool for the LFU algorithm.
Verified to have better real world performances with power-law access patterns because of the data accumulated across calls.
This commit is contained in:
parent
dbce190ad0
commit
6416ab19d0
59
src/evict.c
59
src/evict.c
@ -43,11 +43,15 @@
|
|||||||
* Entries inside the eviciton pool are taken ordered by idle time, putting
|
* Entries inside the eviciton pool are taken ordered by idle time, putting
|
||||||
* greater idle times to the right (ascending order).
|
* greater idle times to the right (ascending order).
|
||||||
*
|
*
|
||||||
|
* When an LFU policy is used instead, a reverse frequency indication is used
|
||||||
|
* instead of the idle time, so that we still evict by larger value (larger
|
||||||
|
* inverse frequency means to evict keys with the least frequent accesses).
|
||||||
|
*
|
||||||
* Empty entries have the key pointer set to NULL. */
|
* Empty entries have the key pointer set to NULL. */
|
||||||
#define EVPOOL_SIZE 16
|
#define EVPOOL_SIZE 16
|
||||||
#define EVPOOL_CACHED_SDS_SIZE 255
|
#define EVPOOL_CACHED_SDS_SIZE 255
|
||||||
struct evictionPoolEntry {
|
struct evictionPoolEntry {
|
||||||
unsigned long long idle; /* Object idle time. */
|
unsigned long long idle; /* Object idle time (inverse frequency for LFU) */
|
||||||
sds key; /* Key name. */
|
sds key; /* Key name. */
|
||||||
sds cached; /* Cached SDS object for key name. */
|
sds cached; /* Cached SDS object for key name. */
|
||||||
int dbid; /* Key DB number. */
|
int dbid; /* Key DB number. */
|
||||||
@ -55,6 +59,8 @@ struct evictionPoolEntry {
|
|||||||
|
|
||||||
static struct evictionPoolEntry *EvictionPoolLRU;
|
static struct evictionPoolEntry *EvictionPoolLRU;
|
||||||
|
|
||||||
|
unsigned long LFUDecrAndReturn(robj *o);
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
* Implementation of eviction, aging and LRU
|
* Implementation of eviction, aging and LRU
|
||||||
* --------------------------------------------------------------------------*/
|
* --------------------------------------------------------------------------*/
|
||||||
@ -158,7 +164,18 @@ void evictionPoolPopulate(int dbid, dict *sampledict, dict *keydict, struct evic
|
|||||||
* again in the key dictionary to obtain the value object. */
|
* again in the key dictionary to obtain the value object. */
|
||||||
if (sampledict != keydict) de = dictFind(keydict, key);
|
if (sampledict != keydict) de = dictFind(keydict, key);
|
||||||
o = dictGetVal(de);
|
o = dictGetVal(de);
|
||||||
|
if (server.maxmemory_policy & MAXMEMORY_FLAG_LRU) {
|
||||||
idle = estimateObjectIdleTime(o);
|
idle = estimateObjectIdleTime(o);
|
||||||
|
} else {
|
||||||
|
/* When we use an LRU policy, we sort the keys by idle time
|
||||||
|
* so that we expire keys starting from greater idle time.
|
||||||
|
* However when the policy is an LFU one, we have a frequency
|
||||||
|
* estimation, and we want to evict keys with lower frequency
|
||||||
|
* first. So inside the pool we put objects using the inverted
|
||||||
|
* frequency subtracting the actual frequency to the maximum
|
||||||
|
* frequency of 255. */
|
||||||
|
idle = 255-LFUDecrAndReturn(o);
|
||||||
|
}
|
||||||
|
|
||||||
/* Insert the element inside the pool.
|
/* Insert the element inside the pool.
|
||||||
* First, find the first empty bucket or the first populated
|
* First, find the first empty bucket or the first populated
|
||||||
@ -361,7 +378,7 @@ int freeMemoryIfNeeded(void) {
|
|||||||
dict *dict;
|
dict *dict;
|
||||||
dictEntry *de;
|
dictEntry *de;
|
||||||
|
|
||||||
if (server.maxmemory_policy & MAXMEMORY_FLAG_LRU) {
|
if (server.maxmemory_policy & (MAXMEMORY_FLAG_LRU|MAXMEMORY_FLAG_LFU)) {
|
||||||
struct evictionPoolEntry *pool = EvictionPoolLRU;
|
struct evictionPoolEntry *pool = EvictionPoolLRU;
|
||||||
|
|
||||||
while(bestkey == NULL) {
|
while(bestkey == NULL) {
|
||||||
@ -372,7 +389,8 @@ int freeMemoryIfNeeded(void) {
|
|||||||
* every DB. */
|
* every DB. */
|
||||||
for (i = 0; i < server.dbnum; i++) {
|
for (i = 0; i < server.dbnum; i++) {
|
||||||
db = server.db+i;
|
db = server.db+i;
|
||||||
dict = (server.maxmemory_policy == MAXMEMORY_ALLKEYS_LRU) ?
|
dict = (server.maxmemory_policy == MAXMEMORY_ALLKEYS_LRU ||
|
||||||
|
server.maxmemory_policy == MAXMEMORY_ALLKEYS_LFU) ?
|
||||||
db->dict : db->expires;
|
db->dict : db->expires;
|
||||||
if ((keys = dictSize(dict)) != 0) {
|
if ((keys = dictSize(dict)) != 0) {
|
||||||
evictionPoolPopulate(i, dict, db->dict, pool);
|
evictionPoolPopulate(i, dict, db->dict, pool);
|
||||||
@ -386,7 +404,9 @@ int freeMemoryIfNeeded(void) {
|
|||||||
if (pool[k].key == NULL) continue;
|
if (pool[k].key == NULL) continue;
|
||||||
bestdbid = pool[k].dbid;
|
bestdbid = pool[k].dbid;
|
||||||
|
|
||||||
if (server.maxmemory_policy == MAXMEMORY_ALLKEYS_LRU) {
|
if (server.maxmemory_policy == MAXMEMORY_ALLKEYS_LRU ||
|
||||||
|
server.maxmemory_policy == MAXMEMORY_ALLKEYS_LFU)
|
||||||
|
{
|
||||||
de = dictFind(server.db[pool[k].dbid].dict,
|
de = dictFind(server.db[pool[k].dbid].dict,
|
||||||
pool[k].key);
|
pool[k].key);
|
||||||
} else {
|
} else {
|
||||||
@ -469,37 +489,6 @@ int freeMemoryIfNeeded(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* allkeys-lfu and volatile-lfu */
|
|
||||||
else if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
|
|
||||||
long bestfreq = 0; /* Initialized to avoid warning. */
|
|
||||||
|
|
||||||
for (i = 0; i < server.dbnum; i++) {
|
|
||||||
j = (++next_db) % server.dbnum;
|
|
||||||
db = server.db+j;
|
|
||||||
dict = (server.maxmemory_policy == MAXMEMORY_ALLKEYS_LFU) ?
|
|
||||||
db->dict : db->expires;
|
|
||||||
if (dictSize(dict) != 0) {
|
|
||||||
for (k = 0; k < server.maxmemory_samples; k++) {
|
|
||||||
sds thiskey;
|
|
||||||
long thisfreq;
|
|
||||||
|
|
||||||
de = dictGetRandomKey(dict);
|
|
||||||
thiskey = dictGetKey(de);
|
|
||||||
robj *o = dictFetchValue(db->dict,thiskey);
|
|
||||||
thisfreq = LFUDecrAndReturn(o);
|
|
||||||
|
|
||||||
/* Keys with a smaller access frequency are
|
|
||||||
* better candidates for deletion */
|
|
||||||
if (bestkey == NULL || thisfreq < bestfreq) {
|
|
||||||
bestkey = thiskey;
|
|
||||||
bestfreq = thisfreq;
|
|
||||||
bestdbid = j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Finally remove the selected key. */
|
/* Finally remove the selected key. */
|
||||||
if (bestkey) {
|
if (bestkey) {
|
||||||
db = server.db+bestdbid;
|
db = server.db+bestdbid;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user