Fix naming: FIFO -> LRU
This commit is contained in:
parent
19d878e21c
commit
38f54cdf77
@ -12,7 +12,7 @@ require "./db-cars.cr"
|
||||
# ENV["REPORT_DIR"] rescue "results"
|
||||
# ENV["NBRUN"] rescue 100
|
||||
# ENV["MAXINDEXES"] rescue 5_000
|
||||
# ENV["FIFO_SIZE"] rescue 10_000
|
||||
# ENV["LRU_SIZE"] rescue 10_000
|
||||
|
||||
class Context
|
||||
class_property report_dir = "results"
|
||||
@ -21,7 +21,7 @@ class Context
|
||||
class_property from = 1_000
|
||||
class_property to = 50_000
|
||||
class_property incr = 1_000
|
||||
class_property fifo_size : UInt32 = 10_000
|
||||
class_property lru_size : UInt32 = 10_000
|
||||
end
|
||||
|
||||
# To simplify the creation of graphs, it's better to have fake data for
|
||||
@ -103,13 +103,13 @@ end
|
||||
def bench_searches()
|
||||
cars_ram = SPECDB::RAMOnly(Car).new
|
||||
cars_cached = SPECDB::Cached(Car).new
|
||||
cars_fifo = SPECDB::Common(Car).new "-#{Context.fifo_size}", Context.fifo_size
|
||||
cars_lru = SPECDB::Common(Car).new "-#{Context.lru_size}", Context.lru_size
|
||||
cars_semi = SPECDB::Uncached(Car).new "-semi"
|
||||
cars_uncached = SPECDB::Uncached(Car).new
|
||||
|
||||
ram_Sby_name, ram_Sby_color, ram_Sby_keywords = ram_indexes cars_ram
|
||||
cached_Sby_name, cached_Sby_color, cached_Sby_keywords = cached_indexes cars_cached
|
||||
fifo_Sby_name, fifo_Sby_color, fifo_Sby_keywords = cached_indexes cars_fifo
|
||||
lru_Sby_name, lru_Sby_color, lru_Sby_keywords = cached_indexes cars_lru
|
||||
semi_Sby_name, semi_Sby_color, semi_Sby_keywords = cached_indexes cars_semi
|
||||
uncached_Sby_name, uncached_Sby_color, uncached_Sby_keywords = uncached_indexes cars_uncached
|
||||
|
||||
@ -117,7 +117,7 @@ def bench_searches()
|
||||
|
||||
prepare_env cars_ram, "ram", ram_Sby_name, ram_Sby_color, ram_Sby_keywords, &fn
|
||||
prepare_env cars_cached, "cached", cached_Sby_name, cached_Sby_color, cached_Sby_keywords, &fn
|
||||
prepare_env cars_fifo, "fifo", fifo_Sby_name, fifo_Sby_color, fifo_Sby_keywords, &fn
|
||||
prepare_env cars_lru, "lru", lru_Sby_name, lru_Sby_color, lru_Sby_keywords, &fn
|
||||
prepare_env cars_semi, "semi", semi_Sby_name, semi_Sby_color, semi_Sby_keywords, &fn
|
||||
prepare_env cars_uncached, "uncached", uncached_Sby_name, uncached_Sby_color, uncached_Sby_keywords, &fn
|
||||
end
|
||||
@ -136,13 +136,13 @@ end
|
||||
def bench_add()
|
||||
cars_ram = SPECDB::RAMOnly(Car).new
|
||||
cars_cached = SPECDB::Cached(Car).new
|
||||
cars_fifo = SPECDB::Common(Car).new "-#{Context.fifo_size}", Context.fifo_size
|
||||
cars_lru = SPECDB::Common(Car).new "-#{Context.lru_size}", Context.lru_size
|
||||
cars_semi = SPECDB::Uncached(Car).new "-semi"
|
||||
cars_uncached = SPECDB::Uncached(Car).new
|
||||
|
||||
ram_indexes cars_ram
|
||||
cached_indexes cars_cached
|
||||
cached_indexes cars_fifo
|
||||
cached_indexes cars_lru
|
||||
cached_indexes cars_semi
|
||||
uncached_indexes cars_uncached
|
||||
|
||||
@ -152,8 +152,8 @@ def bench_add()
|
||||
avr = perform_add(cars_cached)
|
||||
puts "(cached db and indexes) add a value (average on #{Context.nb_run} tries): #{avr}"
|
||||
|
||||
avr = perform_add(cars_fifo)
|
||||
puts "(fifo db and cached indexes) add a value (average on #{Context.nb_run} tries): #{avr}"
|
||||
avr = perform_add(cars_lru)
|
||||
puts "(lru db and cached indexes) add a value (average on #{Context.nb_run} tries): #{avr}"
|
||||
|
||||
avr = perform_add(cars_semi)
|
||||
puts "(uncached db but cached indexes) add a value (average on #{Context.nb_run} tries): #{avr}"
|
||||
@ -167,23 +167,23 @@ def bench_add()
|
||||
cars_uncached.rm_storage_dir
|
||||
end
|
||||
|
||||
def bench_50_shades_of_fifo()
|
||||
cars_fifo1 = SPECDB::Common(Car).new "-1k", 1_000
|
||||
cars_fifo5 = SPECDB::Common(Car).new "-5k", 5_000
|
||||
cars_fifo10 = SPECDB::Common(Car).new "-10k", 10_000
|
||||
cars_fifo20 = SPECDB::Common(Car).new "-20k", 20_000
|
||||
def bench_50_shades_of_lru()
|
||||
cars_lru1 = SPECDB::Common(Car).new "-1k", 1_000
|
||||
cars_lru5 = SPECDB::Common(Car).new "-5k", 5_000
|
||||
cars_lru10 = SPECDB::Common(Car).new "-10k", 10_000
|
||||
cars_lru20 = SPECDB::Common(Car).new "-20k", 20_000
|
||||
|
||||
fifo_Sby_name1, fifo_Sby_color1, fifo_Sby_keywords1 = cached_indexes cars_fifo1
|
||||
fifo_Sby_name5, fifo_Sby_color5, fifo_Sby_keywords5 = cached_indexes cars_fifo5
|
||||
fifo_Sby_name10, fifo_Sby_color10, fifo_Sby_keywords10 = cached_indexes cars_fifo10
|
||||
fifo_Sby_name20, fifo_Sby_color20, fifo_Sby_keywords20 = cached_indexes cars_fifo20
|
||||
lru_Sby_name1, lru_Sby_color1, lru_Sby_keywords1 = cached_indexes cars_lru1
|
||||
lru_Sby_name5, lru_Sby_color5, lru_Sby_keywords5 = cached_indexes cars_lru5
|
||||
lru_Sby_name10, lru_Sby_color10, lru_Sby_keywords10 = cached_indexes cars_lru10
|
||||
lru_Sby_name20, lru_Sby_color20, lru_Sby_keywords20 = cached_indexes cars_lru20
|
||||
|
||||
fn = ->search_benchmark(DODB::Storage(Car), Int32, String, DODB::Trigger::Index(Car), DODB::Trigger::Partition(Car), DODB::Trigger::Tags(Car))
|
||||
|
||||
prepare_env cars_fifo1, "fifo1", fifo_Sby_name1, fifo_Sby_color1, fifo_Sby_keywords1, &fn
|
||||
prepare_env cars_fifo5, "fifo5", fifo_Sby_name5, fifo_Sby_color5, fifo_Sby_keywords5, &fn
|
||||
prepare_env cars_fifo10, "fifo10", fifo_Sby_name10, fifo_Sby_color10, fifo_Sby_keywords10, &fn
|
||||
prepare_env cars_fifo20, "fifo20", fifo_Sby_name20, fifo_Sby_color20, fifo_Sby_keywords20, &fn
|
||||
prepare_env cars_lru1, "lru1", lru_Sby_name1, lru_Sby_color1, lru_Sby_keywords1, &fn
|
||||
prepare_env cars_lru5, "lru5", lru_Sby_name5, lru_Sby_color5, lru_Sby_keywords5, &fn
|
||||
prepare_env cars_lru10, "lru10", lru_Sby_name10, lru_Sby_color10, lru_Sby_keywords10, &fn
|
||||
prepare_env cars_lru20, "lru20", lru_Sby_name20, lru_Sby_color20, lru_Sby_keywords20, &fn
|
||||
end
|
||||
|
||||
ENV["REPORT_DIR"]?.try { |report_dir| Context.report_dir = report_dir }
|
||||
@ -194,7 +194,7 @@ ENV["NBRUN"]?.try { |it| Context.nb_run = it.to_i }
|
||||
ENV["DBSIZE"]?.try { |it| Context.to = it.to_i }
|
||||
ENV["DBSIZE_START"]?.try { |it| Context.from = it.to_i }
|
||||
ENV["DBSIZE_INCREMENT"]?.try { |it| Context.incr = it.to_i }
|
||||
ENV["FIFO_SIZE"]?.try { |it| Context.fifo_size = it.to_u32 }
|
||||
ENV["LRU_SIZE"]?.try { |it| Context.lru_size = it.to_u32 }
|
||||
|
||||
puts "REPORT_DIR: #{Context.report_dir}"
|
||||
puts "MAXINDEXES: #{Context.max_indexes}"
|
||||
@ -202,20 +202,20 @@ puts "NBRUN: #{Context.nb_run}"
|
||||
puts "DBSIZE: #{Context.to}"
|
||||
puts "DBSIZE_START: #{Context.from}"
|
||||
puts "DBSIZE_INCREMENT: #{Context.incr}"
|
||||
puts "FIFO_SIZE: #{Context.fifo_size}"
|
||||
puts "LRU_SIZE: #{Context.lru_size}"
|
||||
|
||||
if ARGV.size == 0
|
||||
puts "Usage: benchmark-cars (fifo|search|add)"
|
||||
puts "Usage: benchmark-cars (lru|search|add)"
|
||||
exit 0
|
||||
end
|
||||
|
||||
case ARGV[0]
|
||||
when /fifo/
|
||||
bench_50_shades_of_fifo
|
||||
when /lru/
|
||||
bench_50_shades_of_lru
|
||||
when /search/
|
||||
bench_searches
|
||||
when /add/
|
||||
bench_add
|
||||
else
|
||||
puts "Usage: benchmark-cars (fifo|search|add)"
|
||||
puts "Usage: benchmark-cars (lru|search|add)"
|
||||
end
|
||||
|
@ -1,70 +0,0 @@
|
||||
require "benchmark"
|
||||
require "./utilities.cr"
|
||||
require "../src/fifo.cr"
|
||||
|
||||
def add(fifo : FIFO(Int32) | EfficientFIFO(Int32), nb : UInt32)
|
||||
i = 0
|
||||
while i < nb
|
||||
fifo << i
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
|
||||
def report_add(fifo : FIFO(Int32) | EfficientFIFO(Int32), nb : UInt32, fname : String)
|
||||
File.open("#{Context.report_dir}/#{fname}.raw", "w") do |file|
|
||||
i = 0
|
||||
while i < nb
|
||||
elapsed_time = perform_something { fifo << i }
|
||||
i += 1
|
||||
file.puts "#{i} #{elapsed_time.total_nanoseconds}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Context
|
||||
class_property nb_values : UInt32 = 100_000
|
||||
class_property fifo_size : UInt32 = 10_000
|
||||
class_property report_dir = "results"
|
||||
end
|
||||
|
||||
if nb_values = ENV["NBVAL"]?
|
||||
Context.nb_values = nb_values.to_u32
|
||||
end
|
||||
|
||||
if fifo_size = ENV["FIFOSIZE"]?
|
||||
Context.fifo_size = fifo_size.to_u32
|
||||
end
|
||||
|
||||
if ARGV.size > 0
|
||||
puts "Usage: benchmark-fifo"
|
||||
puts ""
|
||||
puts "envvar: REPORT_DIR=<directory> where to put the results"
|
||||
puts "envvar: REPORT_EACH_ADD=<any> to report the duration of each addition of a value in the structure"
|
||||
puts "envvar: NBVAL=<nb> (default: 100_000) nb of values to add to the structure"
|
||||
puts "envvar: FIFOSIZE=<nb> (default: 10_000) max number of values in the structure"
|
||||
exit 0
|
||||
end
|
||||
|
||||
ENV["REPORT_DIR"]?.try { |report_dir| Context.report_dir = report_dir }
|
||||
Dir.mkdir_p Context.report_dir
|
||||
|
||||
if ENV["REPORT_EACH_ADD"]?
|
||||
FIFO(Int32).new(Context.fifo_size).tap do |fifo|
|
||||
report_add fifo, Context.nb_values, "fifo_#{Context.fifo_size}_#{Context.nb_values}"
|
||||
end
|
||||
EfficientFIFO(Int32).new(Context.fifo_size).tap do |fifo|
|
||||
report_add fifo, Context.nb_values, "efficientfifo_#{Context.fifo_size}_#{Context.nb_values}"
|
||||
end
|
||||
else
|
||||
Benchmark.ips do |x|
|
||||
x.report("adding #{Context.nb_values} values, FIFO limited to #{Context.fifo_size}") do
|
||||
fifo = FIFO(Int32).new Context.fifo_size
|
||||
add fifo, Context.nb_values
|
||||
end
|
||||
|
||||
x.report("adding #{Context.nb_values} values, EfficientFIFO limited to #{Context.fifo_size}") do
|
||||
fifo = EfficientFIFO(Int32).new Context.fifo_size
|
||||
add fifo, Context.nb_values
|
||||
end
|
||||
end
|
||||
end
|
70
spec/benchmark-lru.cr
Normal file
70
spec/benchmark-lru.cr
Normal file
@ -0,0 +1,70 @@
|
||||
require "benchmark"
|
||||
require "./utilities.cr"
|
||||
require "../src/lru.cr"
|
||||
|
||||
def add(lru : LRU(Int32) | EfficientLRU(Int32), nb : UInt32)
|
||||
i = 0
|
||||
while i < nb
|
||||
lru << i
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
|
||||
def report_add(lru : LRU(Int32) | EfficientLRU(Int32), nb : UInt32, fname : String)
|
||||
File.open("#{Context.report_dir}/#{fname}.raw", "w") do |file|
|
||||
i = 0
|
||||
while i < nb
|
||||
elapsed_time = perform_something { lru << i }
|
||||
i += 1
|
||||
file.puts "#{i} #{elapsed_time.total_nanoseconds}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Context
|
||||
class_property nb_values : UInt32 = 100_000
|
||||
class_property lru_size : UInt32 = 10_000
|
||||
class_property report_dir = "results"
|
||||
end
|
||||
|
||||
if nb_values = ENV["NBVAL"]?
|
||||
Context.nb_values = nb_values.to_u32
|
||||
end
|
||||
|
||||
if lru_size = ENV["LRUSIZE"]?
|
||||
Context.lru_size = lru_size.to_u32
|
||||
end
|
||||
|
||||
if ARGV.size > 0
|
||||
puts "Usage: benchmark-lru"
|
||||
puts ""
|
||||
puts "envvar: REPORT_DIR=<directory> where to put the results"
|
||||
puts "envvar: REPORT_EACH_ADD=<any> to report the duration of each addition of a value in the structure"
|
||||
puts "envvar: NBVAL=<nb> (default: 100_000) nb of values to add to the structure"
|
||||
puts "envvar: LRUSIZE=<nb> (default: 10_000) max number of values in the structure"
|
||||
exit 0
|
||||
end
|
||||
|
||||
ENV["REPORT_DIR"]?.try { |report_dir| Context.report_dir = report_dir }
|
||||
Dir.mkdir_p Context.report_dir
|
||||
|
||||
if ENV["REPORT_EACH_ADD"]?
|
||||
LRU(Int32).new(Context.lru_size).tap do |lru|
|
||||
report_add lru, Context.nb_values, "lru_#{Context.lru_size}_#{Context.nb_values}"
|
||||
end
|
||||
EfficientLRU(Int32).new(Context.lru_size).tap do |lru|
|
||||
report_add lru, Context.nb_values, "efficientlru_#{Context.lru_size}_#{Context.nb_values}"
|
||||
end
|
||||
else
|
||||
Benchmark.ips do |x|
|
||||
x.report("adding #{Context.nb_values} values, LRU limited to #{Context.lru_size}") do
|
||||
lru = LRU(Int32).new Context.lru_size
|
||||
add lru, Context.nb_values
|
||||
end
|
||||
|
||||
x.report("adding #{Context.nb_values} values, EfficientLRU limited to #{Context.lru_size}") do
|
||||
lru = EfficientLRU(Int32).new Context.lru_size
|
||||
add lru, Context.nb_values
|
||||
end
|
||||
end
|
||||
end
|
@ -45,17 +45,17 @@ describe "tracking inconsistencies between implementations" do
|
||||
cars_ram0 = SPECDB::RAMOnly(Car).new "-0"
|
||||
cars_ram1 = SPECDB::RAMOnly(Car).new "-1"
|
||||
cars_ram2 = SPECDB::RAMOnly(Car).new "-2"
|
||||
cars_fifo = SPECDB::Common(Car).new "-3", 5
|
||||
cars_lru = SPECDB::Common(Car).new "-3", 5
|
||||
|
||||
uncached_searchby_name, uncached_searchby_color, uncached_searchby_keywords = uncached_indexes cars_ram0
|
||||
cached_searchby_name, cached_searchby_color, cached_searchby_keywords = cached_indexes cars_ram1
|
||||
ram_searchby_name, ram_searchby_color, ram_searchby_keywords = ram_indexes cars_ram2
|
||||
fifo_cached_searchby_name, fifo_cached_searchby_color, fifo_cached_searchby_keywords = cached_indexes cars_fifo
|
||||
lru_cached_searchby_name, lru_cached_searchby_color, lru_cached_searchby_keywords = cached_indexes cars_lru
|
||||
|
||||
add_cars cars_ram0, 1
|
||||
add_cars cars_ram1, 1
|
||||
add_cars cars_ram2, 1
|
||||
add_cars cars_fifo, 1
|
||||
add_cars cars_lru, 1
|
||||
|
||||
# Searches should be consistent between all implementations of basic indexes, partitions and tags.
|
||||
|
||||
@ -63,10 +63,10 @@ describe "tracking inconsistencies between implementations" do
|
||||
uncached_corvet_car = uncached_searchby_name.get? "Corvet-0"
|
||||
cached_corvet_car = cached_searchby_name.get? "Corvet-0"
|
||||
ram_corvet_car = ram_searchby_name.get? "Corvet-0"
|
||||
fifo_cached_corvet_car = fifo_cached_searchby_name.get? "Corvet-0"
|
||||
lru_cached_corvet_car = lru_cached_searchby_name.get? "Corvet-0"
|
||||
uncached_corvet_car.should eq cached_corvet_car
|
||||
uncached_corvet_car.should eq ram_corvet_car
|
||||
uncached_corvet_car.should eq fifo_cached_corvet_car
|
||||
uncached_corvet_car.should eq lru_cached_corvet_car
|
||||
uncached_corvet_car.should eq corvet0
|
||||
|
||||
# Partitions.
|
||||
@ -76,10 +76,10 @@ describe "tracking inconsistencies between implementations" do
|
||||
uncached_red_cars = uncached_searchby_color.get? "red"
|
||||
cached_red_cars = cached_searchby_color.get? "red"
|
||||
ram_red_cars = ram_searchby_color.get? "red"
|
||||
fifo_cached_red_cars = fifo_cached_searchby_color.get? "red"
|
||||
lru_cached_red_cars = lru_cached_searchby_color.get? "red"
|
||||
uncached_red_cars.sort.should eq cached_red_cars.sort
|
||||
uncached_red_cars.sort.should eq ram_red_cars.sort
|
||||
uncached_red_cars.sort.should eq fifo_cached_red_cars.sort
|
||||
uncached_red_cars.sort.should eq lru_cached_red_cars.sort
|
||||
uncached_red_cars.sort.should eq red_cars.sort
|
||||
|
||||
# Tags.
|
||||
@ -89,16 +89,16 @@ describe "tracking inconsistencies between implementations" do
|
||||
uncached_fast_cars = uncached_searchby_keywords.get? "fast"
|
||||
cached_fast_cars = cached_searchby_keywords.get? "fast"
|
||||
ram_fast_cars = ram_searchby_keywords.get? "fast"
|
||||
fifo_cached_fast_cars = fifo_cached_searchby_keywords.get? "fast"
|
||||
lru_cached_fast_cars = lru_cached_searchby_keywords.get? "fast"
|
||||
uncached_fast_cars.sort.should eq cached_fast_cars.sort
|
||||
uncached_fast_cars.sort.should eq ram_fast_cars.sort
|
||||
uncached_fast_cars.sort.should eq fifo_cached_fast_cars.sort
|
||||
uncached_fast_cars.sort.should eq lru_cached_fast_cars.sort
|
||||
uncached_fast_cars.sort.should eq fast_cars.sort
|
||||
|
||||
cars_ram0.rm_storage_dir
|
||||
cars_ram1.rm_storage_dir
|
||||
cars_ram2.rm_storage_dir
|
||||
cars_fifo.rm_storage_dir
|
||||
cars_lru.rm_storage_dir
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -24,10 +24,10 @@ describe "SPECDB::Common" do
|
||||
|
||||
db << car3
|
||||
db.data.keys.sort.should eq([0, 2, 3] of Int32)
|
||||
db.fifo.to_s.should eq "[ 3, 0, 2 ]"
|
||||
db.lru.to_s.should eq "[ 3, 0, 2 ]"
|
||||
|
||||
db.delete 2
|
||||
db.data.keys.sort.should eq([0, 3] of Int32)
|
||||
db.fifo.to_s.should eq "[ 3, 0 ]"
|
||||
db.lru.to_s.should eq "[ 3, 0 ]"
|
||||
end
|
||||
end
|
||||
|
@ -1,54 +0,0 @@
|
||||
require "spec"
|
||||
require "../src/fifo.cr"
|
||||
|
||||
describe "FIFO" do
|
||||
it "add and remove values" do
|
||||
fifo = FIFO(Int32).new 3 # Only 3 allowed entries.
|
||||
(fifo << 1).should be_nil # there is still room in the fifo
|
||||
(fifo << 2).should be_nil # there is still room in the fifo
|
||||
(fifo << 3).should be_nil # last entry without exceeding the allowed size
|
||||
(fifo << 4).should eq 1 # -> 1 (least recently used data)
|
||||
(fifo << 4).should be_nil # -> nil (already in the fifo)
|
||||
(fifo << 2).should be_nil # -> nil (already in the fifo)
|
||||
(fifo << 5).should eq 3 # -> 3 (least recently used data)
|
||||
fifo.data.should eq([5, 2, 4] of Int32)
|
||||
|
||||
fifo.delete 2
|
||||
fifo.data.should eq([5, 4] of Int32)
|
||||
end
|
||||
end
|
||||
|
||||
describe "EfficientFIFO" do
|
||||
it "add and remove values" do
|
||||
fifo = EfficientFIFO(Int32).new 3 # Only 3 allowed entries.
|
||||
(fifo << 1).should be_nil # there is still room in the fifo
|
||||
(fifo << 2).should be_nil # there is still room in the fifo
|
||||
(fifo << 3).should be_nil # last entry without exceeding the allowed size
|
||||
(fifo << 4).should eq 1 # -> 1 (least recently used data)
|
||||
(fifo << 4).should be_nil # -> nil (already in the fifo)
|
||||
(fifo << 2).should be_nil # -> nil (already in the fifo)
|
||||
(fifo << 5).should eq 3 # -> 3 (least recently used data)
|
||||
fifo.list.to_s.should eq "[ 5, 2, 4 ]"
|
||||
|
||||
fifo.delete 2
|
||||
fifo.list.to_s.should eq "[ 5, 4 ]"
|
||||
|
||||
(fifo << 4).should be_nil # -> nil (just a re-order)
|
||||
fifo.list.to_s.should eq "[ 4, 5 ]"
|
||||
|
||||
fifo.delete 5
|
||||
(fifo << 0).should be_nil
|
||||
fifo.list.to_s.should eq "[ 0, 4 ]"
|
||||
|
||||
(fifo << 1).should be_nil
|
||||
fifo.list.to_s.should eq "[ 1, 0, 4 ]"
|
||||
|
||||
fifo.delete 4
|
||||
fifo.list.to_s.should eq "[ 1, 0 ]"
|
||||
|
||||
fifo.delete 4
|
||||
fifo.list.to_s.should eq "[ 1, 0 ]"
|
||||
fifo.list.size.should eq 2
|
||||
fifo.hash.size.should eq 2
|
||||
end
|
||||
end
|
54
spec/test-lru.cr
Normal file
54
spec/test-lru.cr
Normal file
@ -0,0 +1,54 @@
|
||||
require "spec"
|
||||
require "../src/lru.cr"
|
||||
|
||||
describe "LRU" do
|
||||
it "add and remove values" do
|
||||
lru = LRU(Int32).new 3 # Only 3 allowed entries.
|
||||
(lru << 1).should be_nil # there is still room in the lru
|
||||
(lru << 2).should be_nil # there is still room in the lru
|
||||
(lru << 3).should be_nil # last entry without exceeding the allowed size
|
||||
(lru << 4).should eq 1 # -> 1 (least recently used data)
|
||||
(lru << 4).should be_nil # -> nil (already in the lru)
|
||||
(lru << 2).should be_nil # -> nil (already in the lru)
|
||||
(lru << 5).should eq 3 # -> 3 (least recently used data)
|
||||
lru.data.should eq([5, 2, 4] of Int32)
|
||||
|
||||
lru.delete 2
|
||||
lru.data.should eq([5, 4] of Int32)
|
||||
end
|
||||
end
|
||||
|
||||
describe "EfficientLRU" do
|
||||
it "add and remove values" do
|
||||
lru = EfficientLRU(Int32).new 3 # Only 3 allowed entries.
|
||||
(lru << 1).should be_nil # there is still room in the lru
|
||||
(lru << 2).should be_nil # there is still room in the lru
|
||||
(lru << 3).should be_nil # last entry without exceeding the allowed size
|
||||
(lru << 4).should eq 1 # -> 1 (least recently used data)
|
||||
(lru << 4).should be_nil # -> nil (already in the lru)
|
||||
(lru << 2).should be_nil # -> nil (already in the lru)
|
||||
(lru << 5).should eq 3 # -> 3 (least recently used data)
|
||||
lru.list.to_s.should eq "[ 5, 2, 4 ]"
|
||||
|
||||
lru.delete 2
|
||||
lru.list.to_s.should eq "[ 5, 4 ]"
|
||||
|
||||
(lru << 4).should be_nil # -> nil (just a re-order)
|
||||
lru.list.to_s.should eq "[ 4, 5 ]"
|
||||
|
||||
lru.delete 5
|
||||
(lru << 0).should be_nil
|
||||
lru.list.to_s.should eq "[ 0, 4 ]"
|
||||
|
||||
(lru << 1).should be_nil
|
||||
lru.list.to_s.should eq "[ 1, 0, 4 ]"
|
||||
|
||||
lru.delete 4
|
||||
lru.list.to_s.should eq "[ 1, 0 ]"
|
||||
|
||||
lru.delete 4
|
||||
lru.list.to_s.should eq "[ 1, 0 ]"
|
||||
lru.list.size.should eq 2
|
||||
lru.hash.size.should eq 2
|
||||
end
|
||||
end
|
@ -1,6 +1,6 @@
|
||||
require "file_utils"
|
||||
require "json"
|
||||
|
||||
require "./fifo.cr" # FIFO class to implement a cache policy.
|
||||
require "./lru.cr" # LRU class to implement a cache policy.
|
||||
require "./list.cr" # Double Linked List.
|
||||
require "./dodb/*" # Databases and indexes (basic indexes, partitions, tags, etc.).
|
||||
|
@ -33,13 +33,13 @@
|
||||
#
|
||||
# NOTE: fast for frequently requested data and requires a stable (and configurable) amount of memory.
|
||||
class DODB::Storage::Common(V) < DODB::Storage::Cached(V)
|
||||
# The *fifo* is an instance of `EfficientFIFO` where the key of the requested data is pushed.
|
||||
# The *lru* is an instance of `EfficientLRU` where the key of the requested data is pushed.
|
||||
# In case the number of stored entries exceeds what is allowed, the least recently used entry is removed.
|
||||
property fifo : EfficientFIFO(Int32)
|
||||
property lru : EfficientLRU(Int32)
|
||||
|
||||
# Initializes the `DODB::Storage::Common` database with a maximum number of entries in the cache.
|
||||
def initialize(@directory_name : String, max_entries : UInt32)
|
||||
@fifo = EfficientFIFO(Int32).new max_entries
|
||||
@lru = EfficientLRU(Int32).new max_entries
|
||||
Dir.mkdir_p data_path
|
||||
Dir.mkdir_p locks_directory
|
||||
|
||||
@ -47,7 +47,7 @@ class DODB::Storage::Common(V) < DODB::Storage::Cached(V)
|
||||
end
|
||||
|
||||
# Verifies that the value is in cache, or read it on disk.
|
||||
# Pushes the key in the fifo.
|
||||
# Pushes the key in the lru.
|
||||
def [](key : Int32) : V
|
||||
val = @data[key]?
|
||||
if val.nil?
|
||||
@ -55,14 +55,14 @@ class DODB::Storage::Common(V) < DODB::Storage::Cached(V)
|
||||
val = read file_path key
|
||||
@data[key] = val
|
||||
end
|
||||
push_fifo key
|
||||
push_lru key
|
||||
val
|
||||
end
|
||||
|
||||
# Assumes new entries are more requested than old ones.
|
||||
def []=(key : Int32, value : V)
|
||||
super key, value
|
||||
push_fifo key
|
||||
push_lru key
|
||||
end
|
||||
|
||||
# :inherit:
|
||||
@ -70,19 +70,19 @@ class DODB::Storage::Common(V) < DODB::Storage::Cached(V)
|
||||
# Assumes new entries are more requested than old ones.
|
||||
def <<(item : V)
|
||||
key = super item
|
||||
push_fifo key
|
||||
push_lru key
|
||||
end
|
||||
|
||||
def unsafe_delete(key : Int32)
|
||||
@fifo.delete key if super key
|
||||
@lru.delete key if super key
|
||||
end
|
||||
|
||||
def delete(key : Int32)
|
||||
@fifo.delete key if super key
|
||||
@lru.delete key if super key
|
||||
end
|
||||
|
||||
private def push_fifo(key : Int32)
|
||||
if entry_to_remove = @fifo << key
|
||||
private def push_lru(key : Int32)
|
||||
if entry_to_remove = @lru << key
|
||||
@data.delete entry_to_remove
|
||||
end
|
||||
end
|
||||
|
@ -1,31 +1,31 @@
|
||||
require "./list.cr"
|
||||
|
||||
# This class is a simpler implementation of `EfficientFIFO`, used to implement an eviction policy for data cache
|
||||
# This class is a simpler implementation of `EfficientLRU`, used to implement an eviction policy for data cache
|
||||
# for `DODB::Storage::Common`.
|
||||
# It enables to keep track of recently used data.
|
||||
#
|
||||
# **How this works**.
|
||||
# Each time a value is added in the database, its key is put in this "FIFO" structure.
|
||||
# Each time a value is added in the database, its key is put in this "LRU" structure.
|
||||
# In this structure, **values are unique** and adding a value several times is considered as "using the value",
|
||||
# so it is pushed back at the entry of the FIFO structure, as a new value.
|
||||
# so it is pushed back at the entry of the LRU structure, as a new value.
|
||||
# In case the number of entries exceeds what is allowed, the least recently used value is removed.
|
||||
# ```
|
||||
# fifo = FIFO(Int32).new 3 # Only 3 allowed entries.
|
||||
# lru = LRU(Int32).new 3 # Only 3 allowed entries.
|
||||
#
|
||||
# pp! fifo << 1 # -> nil (there is still room in the FIFO structure)
|
||||
# pp! fifo << 2 # -> nil (there is still room in the FIFO structure)
|
||||
# pp! fifo << 3 # -> nil (last entry without exceeding the allowed size)
|
||||
# pp! fifo << 4 # -> 1 (least recently used data)
|
||||
# pp! fifo << 4 # -> nil (already in the structure)
|
||||
# pp! fifo << 2 # -> nil (already in the structure)
|
||||
# pp! fifo << 5 # -> 3 (least recently used data)
|
||||
# pp! lru << 1 # -> nil (there is still room in the LRU structure)
|
||||
# pp! lru << 2 # -> nil (there is still room in the LRU structure)
|
||||
# pp! lru << 3 # -> nil (last entry without exceeding the allowed size)
|
||||
# pp! lru << 4 # -> 1 (least recently used data)
|
||||
# pp! lru << 4 # -> nil (already in the structure)
|
||||
# pp! lru << 2 # -> nil (already in the structure)
|
||||
# pp! lru << 5 # -> 3 (least recently used data)
|
||||
# ```
|
||||
#
|
||||
# The number of entries in the FIFO structure is configurable.
|
||||
# The number of entries in the LRU structure is configurable.
|
||||
# WARNING: this implementation becomes slow very fast (0(n) complexity), but doesn't cost much memory.
|
||||
# WARNING: this *FIFO* class doesn't allow the same value multiple times.
|
||||
class FIFO(V)
|
||||
# This array is used as the *fifo structure*.
|
||||
# WARNING: this *LRU* class doesn't allow the same value multiple times.
|
||||
class LRU(V)
|
||||
# This array is used as the *lru structure*.
|
||||
property data : Array(V)
|
||||
|
||||
# Maximum allowed entries in the structure.
|
||||
@ -35,7 +35,7 @@ class FIFO(V)
|
||||
@data = Array(V).new
|
||||
end
|
||||
|
||||
# Pushes a value in the FIFO and gets the oldest value whether it exceeds the allowed number of entries.
|
||||
# Pushes a value in the LRU and gets the oldest value whether it exceeds the allowed number of entries.
|
||||
# NOTE: `#<<(v : V)` is (almost) the only function since it's enough for the intended use, feel free to improve this.
|
||||
# WARNING: implementation is extremely simple (3 lines) and not designed to be highly efficient.
|
||||
def <<(v : V) : V?
|
||||
@ -58,32 +58,32 @@ end
|
||||
# It enables to keep track of recently used data.
|
||||
#
|
||||
# **How this works**.
|
||||
# Each time a value is added in the database, its key is put in this "FIFO" structure.
|
||||
# Each time a value is added in the database, its key is put in this "LRU" structure.
|
||||
# In this structure, **values are unique** and adding a value several times is considered as "using the value",
|
||||
# so it is pushed back at the entry of the FIFO structure, as a new value.
|
||||
# so it is pushed back at the entry of the LRU structure, as a new value.
|
||||
# In case the number of entries exceeds what is allowed, the least recently used value is removed.
|
||||
# ```
|
||||
# fifo = EfficientFIFO(Int32).new 3 # Only 3 allowed entries.
|
||||
# lru = EfficientLRU(Int32).new 3 # Only 3 allowed entries.
|
||||
#
|
||||
# pp! fifo << 1 # -> nil (there is still room in the FIFO structure)
|
||||
# pp! fifo << 2 # -> nil (there is still room in the FIFO structure)
|
||||
# pp! fifo << 3 # -> nil (last entry without exceeding the allowed size)
|
||||
# pp! fifo << 4 # -> 1 (least recently used data)
|
||||
# pp! fifo << 4 # -> nil (already in the structure)
|
||||
# pp! fifo << 2 # -> nil (already in the structure)
|
||||
# pp! fifo << 5 # -> 3 (least recently used data)
|
||||
# pp! lru << 1 # -> nil (there is still room in the LRU structure)
|
||||
# pp! lru << 2 # -> nil (there is still room in the LRU structure)
|
||||
# pp! lru << 3 # -> nil (last entry without exceeding the allowed size)
|
||||
# pp! lru << 4 # -> 1 (least recently used data)
|
||||
# pp! lru << 4 # -> nil (already in the structure)
|
||||
# pp! lru << 2 # -> nil (already in the structure)
|
||||
# pp! lru << 5 # -> 3 (least recently used data)
|
||||
# ```
|
||||
#
|
||||
# **Implementation details.**
|
||||
# Contrary to the `FIFO` class, this implementation is time-efficient.
|
||||
# Contrary to the `LRU` class, this implementation is time-efficient.
|
||||
# However, this efficiency is a memory tradeoff: all the entries are added to a double-linked list to keep
|
||||
# track of the order **and** to a hash to perform efficient searches of the values in the double-linked list.
|
||||
# Thus, all the nodes are added twice, once in the list, once in the hash.
|
||||
#
|
||||
# The number of entries in the FIFO structure is configurable.
|
||||
# The number of entries in the LRU structure is configurable.
|
||||
# NOTE: this implementation is time-efficient, but costs some memory.
|
||||
class EfficientFIFO(V)
|
||||
# Both this list and the hash are used as the *fifo structures*.
|
||||
class EfficientLRU(V)
|
||||
# Both this list and the hash are used as the *lru structures*.
|
||||
# The list preserves the *order* of the entries while the *hash* enables fast retrieval of entries in the list.
|
||||
property list : DoubleLinkedList(V)
|
||||
property hash : Hash(V, DoubleLinkedList::Node(V))
|
||||
@ -93,7 +93,7 @@ class EfficientFIFO(V)
|
||||
@hash = Hash(V, DoubleLinkedList::Node(V)).new
|
||||
end
|
||||
|
||||
# Pushes a value in the FIFO and gets the oldest value whether it exceeds the allowed number of entries.
|
||||
# Pushes a value in the LRU and gets the oldest value whether it exceeds the allowed number of entries.
|
||||
# NOTE: `#<<(v : V)` is (almost) the only function since it's enough for the intended use, feel free to improve this.
|
||||
def <<(v : V) : V?
|
||||
if node = hash[v]?
|
Loading…
Reference in New Issue
Block a user