From a8e2d0849e2a40f8bec14cd1b7ff170c17f772c3 Mon Sep 17 00:00:00 2001 From: antirez Date: Mon, 18 Jul 2016 13:49:31 +0200 Subject: [PATCH] LFU: Initial naive eviction cycle. It is possible to get better results by using the pool like in the LRU case. Also from tests during the morning I believe the current implementation has issues in the frequency decay function that should decrease the counter at periodic intervals. --- src/db.c | 9 ++++++++- src/evict.c | 36 +++++++++++++++++++++++++++++++++--- src/object.c | 8 ++++++++ 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/db.c b/src/db.c index b00227d81..d33c810b3 100644 --- a/src/db.c +++ b/src/db.c @@ -175,7 +175,14 @@ void dbOverwrite(redisDb *db, robj *key, robj *val) { dictEntry *de = dictFind(db->dict,key->ptr); serverAssertWithInfo(NULL,key,de != NULL); - dictReplace(db->dict, key->ptr, val); + if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) { + robj *old = dictGetVal(de); + int saved_lru = old->lru; + dictReplace(db->dict, key->ptr, val); + val->lru = saved_lru; + } else { + dictReplace(db->dict, key->ptr, val); + } } /* High level Set operation. This function can be used in order to set diff --git a/src/evict.c b/src/evict.c index 753a2ac81..b48892b41 100644 --- a/src/evict.c +++ b/src/evict.c @@ -294,6 +294,7 @@ unsigned long LFUDecrAndReturn(robj *o) { if (LFUTimeElapsed(ldt) > LFU_DECR_INTERVAL && counter) { if (counter > LFU_INIT_VAL*2) { counter /= 2; + if (counter < LFU_INIT_VAL*2) counter = LFU_INIT_VAL*2; } else { counter--; } @@ -360,9 +361,7 @@ int freeMemoryIfNeeded(void) { dict *dict; dictEntry *de; - if (server.maxmemory_policy == MAXMEMORY_ALLKEYS_LRU || - server.maxmemory_policy == MAXMEMORY_VOLATILE_LRU) - { + if (server.maxmemory_policy & MAXMEMORY_FLAG_LRU) { struct evictionPoolEntry *pool = EvictionPoolLRU; while(bestkey == NULL) { @@ -470,6 +469,37 @@ 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. */ if (bestkey) { db = server.db+bestdbid; diff --git a/src/object.c b/src/object.c index 8f19f60f2..c64d810b0 100644 --- a/src/object.c +++ b/src/object.c @@ -722,10 +722,18 @@ void objectCommand(client *c) { } else if (!strcasecmp(c->argv[1]->ptr,"idletime") && c->argc == 3) { if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk)) == NULL) return; + if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) { + addReplyError(c,"An LFU maxmemory policy is selected, idle time not tracked. Please note that when switching between policies at runtime LRU and LFU data will take some time to adjust."); + return; + } addReplyLongLong(c,estimateObjectIdleTime(o)/1000); } else if (!strcasecmp(c->argv[1]->ptr,"freq") && c->argc == 3) { if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk)) == NULL) return; + if (server.maxmemory_policy & MAXMEMORY_FLAG_LRU) { + addReplyError(c,"An LRU maxmemory policy is selected, access frequency not tracked. Please note that when switching between policies at runtime LRU and LFU data will take some time to adjust."); + return; + } addReplyLongLong(c,o->lru&255); } else { addReplyError(c,"Syntax error. Try OBJECT (refcount|encoding|idletime|freq)");