From ad6b0f70b27712879c6435cb58edc5bff259f7a8 Mon Sep 17 00:00:00 2001 From: antirez Date: Thu, 20 Mar 2014 11:47:12 +0100 Subject: [PATCH] Obtain LRU clock in a resolution dependent way. For testing purposes it is handy to have a very high resolution of the LRU clock, so that it is possible to experiment with scripts running in just a few seconds how the eviction algorithms works. This commit allows Redis to use the cached LRU clock, or a value computed on demand, depending on the resolution. So normally we have the good performance of a precomputed value, and a clock that wraps in many days using the normal resolution, but if needed, changing a define will switch behavior to an high resolution LRU clock. --- src/db.c | 2 +- src/object.c | 7 ++++--- src/redis.c | 26 ++++++++++++-------------- src/redis.h | 9 ++++++++- 4 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/db.c b/src/db.c index 950ebe4f..090d469d 100644 --- a/src/db.c +++ b/src/db.c @@ -50,7 +50,7 @@ robj *lookupKey(redisDb *db, robj *key) { * Don't do it if we have a saving child, as this will trigger * a copy on write madness. */ if (server.rdb_child_pid == -1 && server.aof_child_pid == -1) - val->lru = server.lruclock; + val->lru = LRU_CLOCK(); return val; } else { return NULL; diff --git a/src/object.c b/src/object.c index 775a0dd5..caed4456 100644 --- a/src/object.c +++ b/src/object.c @@ -651,10 +651,11 @@ char *strEncoding(int encoding) { /* Given an object returns the min number of seconds the object was never * requested, using an approximated LRU algorithm. */ unsigned long long estimateObjectIdleTime(robj *o) { - if (server.lruclock >= o->lru) { - return (server.lruclock - o->lru) * REDIS_LRU_CLOCK_RESOLUTION; + unsigned long long lruclock = LRU_CLOCK(); + if (lruclock >= o->lru) { + return (lruclock - o->lru) * REDIS_LRU_CLOCK_RESOLUTION; } else { - return ((REDIS_LRU_CLOCK_MAX - o->lru) + server.lruclock) * + return (lruclock + (REDIS_LRU_CLOCK_MAX - o->lru)) * REDIS_LRU_CLOCK_RESOLUTION; } } diff --git a/src/redis.c b/src/redis.c index b8ed8732..7719745c 100644 --- a/src/redis.c +++ b/src/redis.c @@ -843,9 +843,8 @@ void activeExpireCycle(int type) { } } -void updateLRUClock(void) { - server.lruclock = (mstime()/REDIS_LRU_CLOCK_RESOLUTION) & - REDIS_LRU_CLOCK_MAX; +unsigned int getLRUClock(void) { + return (mstime()/REDIS_LRU_CLOCK_RESOLUTION) & REDIS_LRU_CLOCK_MAX; } /* Add a sample to the operations per second array of samples. */ @@ -1044,19 +1043,18 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) { run_with_period(100) trackOperationsPerSecond(); - /* We have just 22 bits per object for LRU information. - * So we use an (eventually wrapping) LRU clock with 10 seconds resolution. - * 2^22 bits with 10 seconds resolution is more or less 1.5 years. + /* We have just REDIS_LRU_BITS bits per object for LRU information. + * So we use an (eventually wrapping) LRU clock. * - * Note that even if this will wrap after 1.5 years it's not a problem, - * everything will still work but just some object will appear younger - * to Redis. But for this to happen a given object should never be touched - * for 1.5 years. + * Note that even if the counter wraps it's not a big problem, + * everything will still work but some object will appear younger + * to Redis. However for this to happen a given object should never be + * touched for all the time needed to the counter to wrap, which is + * not likely. * * Note that you can change the resolution altering the - * REDIS_LRU_CLOCK_RESOLUTION define. - */ - updateLRUClock(); + * REDIS_LRU_CLOCK_RESOLUTION define. */ + server.lruclock = getLRUClock(); /* Record the max memory used since the server was started. */ if (zmalloc_used_memory() > server.stat_peak_memory) @@ -1421,7 +1419,7 @@ void initServerConfig() { server.migrate_cached_sockets = dictCreate(&migrateCacheDictType,NULL); server.loading_process_events_interval_bytes = (1024*1024*2); - updateLRUClock(); + server.lruclock = getLRUClock(); resetServerSaveParams(); appendServerSaveParams(60*60,1); /* save after 1 hour and 1 change */ diff --git a/src/redis.h b/src/redis.h index c3438a6a..a57a7f43 100644 --- a/src/redis.h +++ b/src/redis.h @@ -394,6 +394,12 @@ typedef struct redisObject { void *ptr; } robj; +/* Macro used to obtain the current LRU clock. + * If the current resolution is lower than the frequency we refresh the + * LRU clock (as it should be in production servers) we return the + * precomputed value, otherwise we need to resort to a function call. */ +#define LRU_CLOCK() ((1000/server.hz <= REDIS_LRU_CLOCK_RESOLUTION) ? server.lruclock : getLRUClock()) + /* Macro used to initialize a Redis object allocated on the stack. * Note that this macro is taken near the structure definition to make sure * we'll update it when the structure is changed, to avoid bugs like @@ -1055,7 +1061,7 @@ char *strEncoding(int encoding); int compareStringObjects(robj *a, robj *b); int collateStringObjects(robj *a, robj *b); int equalStringObjects(robj *a, robj *b); -unsigned long estimateObjectIdleTime(robj *o); +unsigned long long estimateObjectIdleTime(robj *o); #define sdsEncodedObject(objptr) (objptr->encoding == REDIS_ENCODING_RAW || objptr->encoding == REDIS_ENCODING_EMBSTR) /* Synchronous I/O with timeout */ @@ -1156,6 +1162,7 @@ void adjustOpenFilesLimit(void); void closeListeningSockets(int unlink_unix_socket); void updateCachedTime(void); void resetServerStats(void); +unsigned int getLRUClock(void); /* Set data type */ robj *setTypeCreate(robj *value);