summaryrefslogtreecommitdiff
path: root/utils/lru/test-lru.rb
diff options
context:
space:
mode:
Diffstat (limited to 'utils/lru/test-lru.rb')
-rw-r--r--utils/lru/test-lru.rb268
1 files changed, 172 insertions, 96 deletions
diff --git a/utils/lru/test-lru.rb b/utils/lru/test-lru.rb
index ee0527ef4..dadc6d505 100644
--- a/utils/lru/test-lru.rb
+++ b/utils/lru/test-lru.rb
@@ -1,112 +1,188 @@
require 'rubygems'
require 'redis'
-r = Redis.new
-r.config("SET","maxmemory","2000000")
-r.config("SET","maxmemory-policy","allkeys-lru")
-r.config("SET","maxmemory-samples",5)
-r.config("RESETSTAT")
-r.flushall
-
-puts <<EOF
-<html>
-<body>
-<style>
-.box {
- width:5px;
- height:5px;
- float:left;
- margin: 1px;
-}
+$runs = []; # Remember the error rate of each run for average purposes.
+
+def testit(filename)
+ r = Redis.new
+ r.config("SET","maxmemory","2000000")
+ r.config("SET","maxmemory-policy","allkeys-lru")
+ r.config("SET","maxmemory-samples",10)
+ 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
-.old {
- border: 1px black solid;
-}
+ # Fill the DB up to the first eviction.
+ oldsize = r.dbsize
+ id = 0
+ while true
+ id += 1
+ r.set(id,"foo")
+ newsize = r.dbsize
+ break if newsize == oldsize # A key was evicted? Stop.
+ oldsize = newsize
+ end
-.new {
- border: 1px green solid;
-}
+ inserted = r.dbsize
+ first_set_max_id = id
+ html << "#{r.dbsize} keys inserted"
+
+ # Access keys sequentially, so that in theory the first part will be expired
+ # and the latter part will not, according to perfect LRU.
+
+ STDERR.puts "Access keys sequentially"
+ (1..first_set_max_id).each{|id|
+ r.get(id)
+ sleep 0.001
+ STDERR.print(".") if (id % 150) == 0
+ }
+ 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"
+ 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
-.ex {
- background-color: #666;
-}
-</style>
-<pre>
+ html << "#{add} additional keys added."
+ html << "#{r.dbsize} keys in DB"
+
+ # 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!"
+ 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
-# Fill
-oldsize = r.dbsize
-id = 0
-while true
- id += 1
- r.set(id,"foo")
- newsize = r.dbsize
- break if newsize == oldsize
- oldsize = newsize
+ f = File.open(filename,"w")
+ f.write(html)
+ f.close
end
-inserted = r.dbsize
-first_set_max_id = id
-puts "#{r.dbsize} keys inserted"
-
-# Access keys sequentially
-
-puts "Access keys sequentially"
-(1..first_set_max_id).each{|id|
- r.get(id)
-# sleep 0.001
-}
+def print_avg
+ avg = ($runs.reduce {|a,b| a+b}) / $runs.length
+ puts "#{$runs.length} runs, AVG is #{avg}"
+end
-# Insert more 50% keys. We expect that the new keys
-half = inserted/2
-puts "Insert enough keys to evict half the keys we inserted"
-add = 0
-while true
- add += 1
- id += 1
- r.set(id,"foo")
- break if r.info['evicted_keys'].to_i >= half
+if ARGV.length < 1
+ STDERR.puts "Usage: ruby test-lru.rb <html-output-filename> [num-runs]"
+ exit 1
end
-puts "#{add} additional keys added."
-puts "#{r.dbsize} keys in DB"
-
-# 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
-edecr = 1.0/(first_set_max_id/2)
-(1..(first_set_max_id/2)).each{|id|
- e -= edecr if e > 0
- e = 0 if e < 0
- if r.exists(id)
- errors += e
- end
-}
+filename = ARGV[0]
+numruns = 1
-puts "#{errors} errors!"
-puts "</pre>"
-
-# Generate the graphical representation
-(1..id).each{|id|
- # Mark first set and added items in a different way.
- c = "box"
- if id <= first_set_max_id
- c << " old"
- else
- c << " new"
- end
+numruns = ARGV[1].to_i if ARGV.length == 2
- # Add class if exists
- c << " ex" if r.exists(id)
- puts "<div class=\"#{c}\"></div>"
+numruns.times {
+ testit(filename)
+ print_avg if numruns != 1
}
-
-# Close HTML page
-
-puts <<EOF
-</body>
-</html>
-EOF