diff options
| author | Mitja Felicijan <mitja.felicijan@gmail.com> | 2026-01-21 22:52:54 +0100 |
|---|---|---|
| committer | Mitja Felicijan <mitja.felicijan@gmail.com> | 2026-01-21 22:52:54 +0100 |
| commit | dcacc00e3750300617ba6e16eb346713f91a783a (patch) | |
| tree | 38e2d4fb5ed9d119711d4295c6eda4b014af73fd /examples/redis-unstable/utils/lru | |
| parent | 58dac10aeb8f5a041c46bddbeaf4c7966a99b998 (diff) | |
| download | crep-dcacc00e3750300617ba6e16eb346713f91a783a.tar.gz | |
Remove testing data
Diffstat (limited to 'examples/redis-unstable/utils/lru')
| -rw-r--r-- | examples/redis-unstable/utils/lru/README | 19 | ||||
| -rw-r--r-- | examples/redis-unstable/utils/lru/lfu-simulation.c | 158 | ||||
| -rw-r--r-- | examples/redis-unstable/utils/lru/test-lru.rb | 223 |
3 files changed, 0 insertions, 400 deletions
diff --git a/examples/redis-unstable/utils/lru/README b/examples/redis-unstable/utils/lru/README deleted file mode 100644 index f043b29..0000000 --- a/examples/redis-unstable/utils/lru/README +++ /dev/null @@ -1,19 +0,0 @@ -The test-lru.rb program can be used in order to check the behavior of the -Redis approximated LRU algorithm against the theoretical output of true -LRU algorithm. - -In order to use the program you need to recompile Redis setting the define -REDIS_LRU_CLOCK_RESOLUTION to 1, by editing the file server.h. -This allows to execute the program in a fast way since the 1 ms resolution -is enough for all the objects to have a different enough time stamp during -the test. - -The program is executed like this: - - ruby test-lru.rb /tmp/lru.html - -You can optionally specify a number of times to run, so that the program -will output averages of different runs, by adding an additional argument. -For instance in order to run the test 10 times use: - - ruby test-lru.rb /tmp/lru.html 10 diff --git a/examples/redis-unstable/utils/lru/lfu-simulation.c b/examples/redis-unstable/utils/lru/lfu-simulation.c deleted file mode 100644 index 60105e5..0000000 --- a/examples/redis-unstable/utils/lru/lfu-simulation.c +++ /dev/null @@ -1,158 +0,0 @@ -#include <stdio.h> -#include <time.h> -#include <stdint.h> -#include <stdlib.h> - -int decr_every = 1; -int keyspace_size = 1000000; -time_t switch_after = 30; /* Switch access pattern after N seconds. */ - -struct entry { - /* Field that the LFU Redis implementation will have (we have - * 24 bits of total space in the object->lru field). */ - uint8_t counter; /* Logarithmic counter. */ - uint16_t decrtime; /* (Reduced precision) time of last decrement. */ - - /* Fields only useful for visualization. */ - uint64_t hits; /* Number of real accesses. */ - time_t ctime; /* Key creation time. */ -}; - -#define to_16bit_minutes(x) ((x/60) & 65535) -#define LFU_INIT_VAL 5 - -/* Compute the difference in minutes between two 16 bit minutes times - * obtained with to_16bit_minutes(). Since they can wrap around if - * we detect the overflow we account for it as if the counter wrapped - * a single time. */ -uint16_t minutes_diff(uint16_t now, uint16_t prev) { - if (now >= prev) return now-prev; - return 65535-prev+now; -} - -/* Increment a counter logarithmically: the greatest is its value, the - * less likely is that the counter is really incremented. - * The maximum value of the counter is saturated at 255. */ -uint8_t log_incr(uint8_t counter) { - if (counter == 255) return counter; - double r = (double)rand()/RAND_MAX; - double baseval = counter-LFU_INIT_VAL; - if (baseval < 0) baseval = 0; - double limit = 1.0/(baseval*10+1); - if (r < limit) counter++; - return counter; -} - -/* Simulate an access to an entry. */ -void access_entry(struct entry *e) { - e->counter = log_incr(e->counter); - e->hits++; -} - -/* Return the entry LFU value and as a side effect decrement the - * entry value if the decrement time was reached. */ -uint8_t scan_entry(struct entry *e) { - if (minutes_diff(to_16bit_minutes(time(NULL)),e->decrtime) - >= decr_every) - { - if (e->counter) { - if (e->counter > LFU_INIT_VAL*2) { - e->counter /= 2; - } else { - e->counter--; - } - } - e->decrtime = to_16bit_minutes(time(NULL)); - } - return e->counter; -} - -/* Print the entry info. */ -void show_entry(long pos, struct entry *e) { - char *tag = "normal "; - - if (pos >= 10 && pos <= 14) tag = "new no access"; - if (pos >= 15 && pos <= 19) tag = "new accessed "; - if (pos >= keyspace_size -5) tag= "old no access"; - - printf("%ld] <%s> frequency:%d decrtime:%d [%lu hits | age:%ld sec]\n", - pos, tag, e->counter, e->decrtime, (unsigned long)e->hits, - time(NULL) - e->ctime); -} - -int main(void) { - time_t start = time(NULL); - time_t new_entry_time = start; - time_t display_time = start; - struct entry *entries = malloc(sizeof(*entries)*keyspace_size); - long j; - - /* Initialize. */ - for (j = 0; j < keyspace_size; j++) { - entries[j].counter = LFU_INIT_VAL; - entries[j].decrtime = to_16bit_minutes(start); - entries[j].hits = 0; - entries[j].ctime = time(NULL); - } - - while(1) { - time_t now = time(NULL); - long idx; - - /* Scan N random entries (simulates the eviction under maxmemory). */ - for (j = 0; j < 3; j++) { - scan_entry(entries+(rand()%keyspace_size)); - } - - /* Access a random entry: use a power-law access pattern up to - * 'switch_after' seconds. Then revert to flat access pattern. */ - if (now-start < switch_after) { - /* Power law. */ - idx = 1; - while((rand() % 21) != 0 && idx < keyspace_size) idx *= 2; - if (idx > keyspace_size) idx = keyspace_size; - idx = rand() % idx; - } else { - /* Flat. */ - idx = rand() % keyspace_size; - } - - /* Never access entries between position 10 and 14, so that - * we simulate what happens to new entries that are never - * accessed VS new entries which are accessed in positions - * 15-19. - * - * Also never access last 5 entry, so that we have keys which - * are never recreated (old), and never accessed. */ - if ((idx < 10 || idx > 14) && (idx < keyspace_size-5)) - access_entry(entries+idx); - - /* Simulate the addition of new entries at positions between - * 10 and 19, a random one every 10 seconds. */ - if (new_entry_time <= now) { - idx = 10+(rand()%10); - entries[idx].counter = LFU_INIT_VAL; - entries[idx].decrtime = to_16bit_minutes(time(NULL)); - entries[idx].hits = 0; - entries[idx].ctime = time(NULL); - new_entry_time = now+10; - } - - /* Show the first 20 entries and the last 20 entries. */ - if (display_time != now) { - printf("=============================\n"); - printf("Current minutes time: %d\n", (int)to_16bit_minutes(now)); - printf("Access method: %s\n", - (now-start < switch_after) ? "power-law" : "flat"); - - for (j = 0; j < 20; j++) - show_entry(j,entries+j); - - for (j = keyspace_size-20; j < keyspace_size; j++) - show_entry(j,entries+j); - display_time = now; - } - } - return 0; -} - diff --git a/examples/redis-unstable/utils/lru/test-lru.rb b/examples/redis-unstable/utils/lru/test-lru.rb deleted file mode 100644 index d511e20..0000000 --- a/examples/redis-unstable/utils/lru/test-lru.rb +++ /dev/null @@ -1,223 +0,0 @@ -require 'rubygems' -require 'redis' - -$runs = []; # Remember the error rate of each run for average purposes. -$o = {}; # Options set parsing arguments - -def testit(filename) - r = Redis.new - r.config("SET","maxmemory","2000000") - if $o[:ttl] - r.config("SET","maxmemory-policy","volatile-ttl") - else - r.config("SET","maxmemory-policy","allkeys-lru") - end - r.config("SET","maxmemory-samples",5) - r.config("RESETSTAT") - r.flushall - - html = "" - html << <<EOF - <html> - <body> - <style> - .box { - width:5px; - height:5px; - float:left; - margin: 1px; - } - - .old { - border: 1px black solid; - } - - .new { - border: 1px green solid; - } - - .otherdb { - border: 1px red solid; - } - - .ex { - background-color: #666; - } - </style> - <pre> -EOF - - # Fill the DB up to the first eviction. - oldsize = r.dbsize - id = 0 - while true - id += 1 - begin - r.set(id,"foo") - rescue - break - end - newsize = r.dbsize - break if newsize == oldsize # A key was evicted? Stop. - oldsize = newsize - end - - inserted = r.dbsize - first_set_max_id = id - html << "#{r.dbsize} keys inserted.\n" - - # Access keys sequentially, so that in theory the first part will be expired - # and the latter part will not, according to perfect LRU. - - if $o[:ttl] - STDERR.puts "Set increasing expire value" - (1..first_set_max_id).each{|id| - r.expire(id,1000+id) - STDERR.print(".") if (id % 150) == 0 - } - else - STDERR.puts "Access keys sequentially" - (1..first_set_max_id).each{|id| - r.get(id) - sleep 0.001 - STDERR.print(".") if (id % 150) == 0 - } - end - STDERR.puts - - # Insert more 50% keys. We expect that the new keys will rarely be expired - # since their last access time is recent compared to the others. - # - # Note that we insert the first 100 keys of the new set into DB1 instead - # of DB0, so that we can try how cross-DB eviction works. - half = inserted/2 - html << "Insert enough keys to evict half the keys we inserted.\n" - add = 0 - - otherdb_start_idx = id+1 - otherdb_end_idx = id+100 - while true - add += 1 - id += 1 - if id >= otherdb_start_idx && id <= otherdb_end_idx - r.select(1) - r.set(id,"foo") - r.select(0) - else - r.set(id,"foo") - end - break if r.info['evicted_keys'].to_i >= half - end - - html << "#{add} additional keys added.\n" - html << "#{r.dbsize} keys in DB.\n" - - # Check if evicted keys respect LRU - # We consider errors from 1 to N progressively more serious as they violate - # more the access pattern. - - errors = 0 - e = 1 - error_per_key = 100000.0/first_set_max_id - half_set_size = first_set_max_id/2 - maxerr = 0 - (1..(first_set_max_id/2)).each{|id| - if id >= otherdb_start_idx && id <= otherdb_end_idx - r.select(1) - exists = r.exists(id) - r.select(0) - else - exists = r.exists(id) - end - if id < first_set_max_id/2 - thiserr = error_per_key * ((half_set_size-id).to_f/half_set_size) - maxerr += thiserr - errors += thiserr if exists - elsif id >= first_set_max_id/2 - thiserr = error_per_key * ((id-half_set_size).to_f/half_set_size) - maxerr += thiserr - errors += thiserr if !exists - end - } - errors = errors*100/maxerr - - STDERR.puts "Test finished with #{errors}% error! Generating HTML on stdout." - - html << "#{errors}% error!\n" - html << "</pre>" - $runs << errors - - # Generate the graphical representation - (1..id).each{|id| - # Mark first set and added items in a different way. - c = "box" - if id >= otherdb_start_idx && id <= otherdb_end_idx - c << " otherdb" - elsif id <= first_set_max_id - c << " old" - else - c << " new" - end - - # Add class if exists - if id >= otherdb_start_idx && id <= otherdb_end_idx - r.select(1) - exists = r.exists(id) - r.select(0) - else - exists = r.exists(id) - end - - c << " ex" if exists - html << "<div title=\"#{id}\" class=\"#{c}\"></div>" - } - - # Close HTML page - - html << <<EOF - </body> - </html> -EOF - - f = File.open(filename,"w") - f.write(html) - f.close -end - -def print_avg - avg = ($runs.reduce {|a,b| a+b}) / $runs.length - puts "#{$runs.length} runs, AVG is #{avg}" -end - -if ARGV.length < 1 - STDERR.puts "Usage: ruby test-lru.rb <html-output-filename> [--runs <count>] [--ttl]" - STDERR.puts "Options:" - STDERR.puts " --runs <count> Execute the test <count> times." - STDERR.puts " --ttl Set keys with increasing TTL values" - STDERR.puts " (starting from 1000 seconds) in order to" - STDERR.puts " test the volatile-lru policy." - exit 1 -end - -filename = ARGV[0] -$o[:numruns] = 1 - -# Options parsing -i = 1 -while i < ARGV.length - if ARGV[i] == '--runs' - $o[:numruns] = ARGV[i+1].to_i - i+= 1 - elsif ARGV[i] == '--ttl' - $o[:ttl] = true - else - STDERR.puts "Unknown option #{ARGV[i]}" - exit 1 - end - i+= 1 -end - -$o[:numruns].times { - testit(filename) - print_avg if $o[:numruns] != 1 -} |
