lrjew 1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'spec/rake/spectask'
3
+
4
+ Spec::Rake::SpecTask.new do |t|
5
+ t.spec_files = FileList['spec/**/*_spec.rb']
6
+ end
7
+
8
+ desc "Default task is to run specs"
9
+ task :default => :spec
data/bin/lrjew ADDED
@@ -0,0 +1,20 @@
1
+ #!/bin/env ruby
2
+
3
+ require 'lru'
4
+
5
+ lru_size = ARGV[0] || 100_000
6
+
7
+ lru = Lru.new(lru_size)
8
+
9
+ hits = 0
10
+
11
+ GC.disable
12
+
13
+ ARGF.each_with_index do |line, idx|
14
+ lru.include?(line)
15
+ if (idx+1) % 50_000 == 0
16
+ puts "#{Time.now}: #{lru.inspect}"
17
+ GC.start
18
+ GC.disable
19
+ end
20
+ end
@@ -0,0 +1,36 @@
1
+ class BoundedStack
2
+ include Enumerable
3
+
4
+ def initialize(list, maximum_size)
5
+ @list = list
6
+ @maximum_size = maximum_size
7
+ @current_size = list.length
8
+ raise ArgumentError unless @current_size <= @maximum_size
9
+ end
10
+
11
+ def push(data)
12
+ shifted = nil
13
+ if @current_size == @maximum_size
14
+ shifted = @list.shift
15
+ else
16
+ @current_size += 1
17
+ end
18
+
19
+ node = @list.push(data)
20
+ [node, shifted]
21
+ end
22
+
23
+ def length
24
+ @current_size
25
+ end
26
+
27
+ alias_method :size, :length
28
+
29
+ def method_missing(method, *args, &block)
30
+ @list.send(method, *args, &block)
31
+ end
32
+
33
+ def each(&block)
34
+ @list.each(&block)
35
+ end
36
+ end
@@ -0,0 +1,39 @@
1
+ class IndexedBoundedStack
2
+ include Enumerable
3
+
4
+ def initialize(bounded_stack)
5
+ @bounded_stack = bounded_stack
6
+ @index = {}
7
+ end
8
+
9
+ def include?(data)
10
+ @index.has_key?(data)
11
+ end
12
+
13
+ def push(data)
14
+ node, shifted = @bounded_stack.push(data)
15
+ @index[data] = node
16
+ if shifted
17
+ @index.delete(shifted)
18
+ end
19
+ [node, shifted]
20
+ end
21
+
22
+ def delete(data)
23
+ if node = @index[data]
24
+ @bounded_stack.delete(node)
25
+ @index.delete(data)
26
+ end
27
+ end
28
+
29
+ def length
30
+ @indices.length
31
+ end
32
+
33
+ alias_method :size, :length
34
+
35
+ def each(&block)
36
+ @bounded_stack.each(&block)
37
+ end
38
+ end
39
+
@@ -0,0 +1,82 @@
1
+ class LinkedList
2
+ include Enumerable
3
+
4
+ def initialize
5
+ @last = Node.new(nil, nil)
6
+ @first = Node.new(nil, @last)
7
+ @last.prev = @first
8
+ end
9
+
10
+ def push(data)
11
+ case data
12
+ when Node
13
+ insert_before(@first.next, data)
14
+ else
15
+ node = @first.next.push(data)
16
+ @first.next = node
17
+ node.prev = @first
18
+ node
19
+ end
20
+ end
21
+
22
+ def each
23
+ node = @last
24
+ while (node = node.prev) && node.data
25
+ yield node.data
26
+ end
27
+ end
28
+
29
+ def delete(node)
30
+ node.prev.next = node.next
31
+ node.next.prev = node.prev
32
+ node.data
33
+ end
34
+
35
+ def insert_before(before, after)
36
+ after.prev.next = before
37
+ before.next = after
38
+ before.prev = after.prev
39
+ after.prev = before
40
+ end
41
+
42
+ def shift
43
+ delete(@last.prev) unless @last.prev == @first
44
+ end
45
+
46
+ def length
47
+ @first.next.length
48
+ end
49
+
50
+ alias_method :size, :length
51
+ end
52
+
53
+ class Node
54
+ attr_accessor :next, :prev
55
+ attr_reader :data
56
+
57
+ def initialize(data, _next)
58
+ @data = data
59
+ @next = _next
60
+ end
61
+
62
+ def length
63
+ if @data.nil?
64
+ 0
65
+ else
66
+ 1 + @next.length
67
+ end
68
+ end
69
+
70
+ def push(data)
71
+ node = Node.new(data, self)
72
+ self.prev = node
73
+ node
74
+ end
75
+
76
+ def inspect
77
+ prev_data = @prev && @prev.data || nil
78
+ next_data = @next && @next.data || nil
79
+ "#{prev_data.inspect} <- #{@data.inspect} -> #{next_data.inspect}"
80
+ end
81
+ alias_method :to_s, :inspect
82
+ end
data/lib/lrjew/lru.rb ADDED
@@ -0,0 +1,32 @@
1
+ class Lru
2
+ attr_reader :evictions, :hits, :misses, :gets
3
+
4
+ def initialize(indexed_bounded_stack)
5
+ @stack = indexed_bounded_stack
6
+ @gets = @hits = @misses = @evictions = 0
7
+ end
8
+
9
+ def include?(item)
10
+ @gets += 1
11
+ if included = @stack.include?(item)
12
+ @hits += 1
13
+ @stack.delete(item)
14
+ @stack.push(item)
15
+ else
16
+ @misses += 1
17
+ node, shifted = @stack.push(item)
18
+ @evictions += 1 if shifted
19
+ end
20
+ included
21
+ end
22
+
23
+ def inspect
24
+ [
25
+ "gets", @gets,
26
+ "hits", @hits,
27
+ "misses", @misses,
28
+ "evictions", @evictions,
29
+ "rate", @hits.to_f / @gets
30
+ ].join("\t")
31
+ end
32
+ end
data/lib/lrjew.rb ADDED
@@ -0,0 +1,6 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+
3
+ require 'lru/linked_list'
4
+ require 'lru/bounded_stack'
5
+ require 'lru/indexed_bounded_stack'
6
+ require 'lru/lru'
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe BoundedStack do
4
+ before do
5
+ @bounded_stack = BoundedStack.new([], 5)
6
+ end
7
+
8
+ describe "#push" do
9
+ describe "when the stack is not full" do
10
+ it "increments the size" do
11
+ @bounded_stack.push(1)
12
+ @bounded_stack.push(2)
13
+ @bounded_stack.size.should == 2
14
+ @bounded_stack.to_a.should == [1, 2]
15
+ end
16
+ end
17
+
18
+ describe "when the stack is full" do
19
+ it "truncates the list" do
20
+ 5.times do |i|
21
+ @bounded_stack.push(i + 1)
22
+ end
23
+ @bounded_stack.push(6)[1].should == 1
24
+ @bounded_stack.size.should == 5
25
+ @bounded_stack.to_a.should == [2, 3, 4, 5, 6]
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe IndexedBoundedStack do
4
+ before do
5
+ @indexed_stack = IndexedBoundedStack.new(BoundedStack.new(LinkedList.new, 5))
6
+ end
7
+
8
+ describe "#push" do
9
+ it "adds items" do
10
+ @indexed_stack.push(1)
11
+ @indexed_stack.push(2)
12
+ @indexed_stack.to_a.should == [1, 2]
13
+ end
14
+
15
+ it "returns items that were shifted off" do
16
+ 5.times do |i|
17
+ @indexed_stack.push(i + 1)
18
+ end
19
+ node, shifted = @indexed_stack.push(6)
20
+ shifted.should == 1
21
+ end
22
+ end
23
+
24
+ describe "#delete" do
25
+ it "moves an item if it exists" do
26
+ @indexed_stack.push(1)
27
+ @indexed_stack.push(2)
28
+ @indexed_stack.delete(2)
29
+ @indexed_stack.to_a.should == [1]
30
+ @indexed_stack.push(2)
31
+ @indexed_stack.to_a.should == [1, 2]
32
+ end
33
+ end
34
+
35
+ describe "#include?" do
36
+ it "moves an item if it exists" do
37
+ @indexed_stack.push(1)
38
+ @indexed_stack.push(2)
39
+ @indexed_stack.include?(2).should be_true
40
+ @indexed_stack.include?(3).should be_false
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,92 @@
1
+ require 'spec_helper'
2
+
3
+ describe LinkedList do
4
+ before do
5
+ @linked_list = LinkedList.new
6
+ end
7
+
8
+ describe "#push" do
9
+ it "lets you push on multiple values" do
10
+ @linked_list.push(1)
11
+ @linked_list.push(2)
12
+ @linked_list.push(3)
13
+ @linked_list.to_a.should == [1, 2, 3]
14
+ end
15
+ end
16
+
17
+ describe "#delete" do
18
+ it "lets you remove an element in the middle" do
19
+ one = @linked_list.push(1)
20
+ two = @linked_list.push(2)
21
+ three = @linked_list.push(3)
22
+ @linked_list.delete(two)
23
+ @linked_list.to_a.should == [1, 3]
24
+ end
25
+
26
+ it "lets you remove the head" do
27
+ one = @linked_list.push(1)
28
+ two = @linked_list.push(2)
29
+ three = @linked_list.push(3)
30
+
31
+ @linked_list.delete(one)
32
+ @linked_list.to_a.should == [2, 3]
33
+ end
34
+
35
+ it "lets you remove the last" do
36
+ one = @linked_list.push(1)
37
+ two = @linked_list.push(2)
38
+ three = @linked_list.push(3)
39
+
40
+ @linked_list.delete(three)
41
+ @linked_list.to_a.should == [1, 2]
42
+ end
43
+ end
44
+
45
+ describe "#shift" do
46
+ it "removes the last item" do
47
+ @linked_list.push(1)
48
+ @linked_list.push(2)
49
+ @linked_list.push(3)
50
+
51
+ @linked_list.shift.should == 1
52
+ @linked_list.to_a.should == [2, 3]
53
+ end
54
+
55
+ it "removes from a list with one item" do
56
+ @linked_list.push(1)
57
+ @linked_list.shift.should == 1
58
+ @linked_list.to_a.should == []
59
+ end
60
+
61
+ it "shifting an empty list does nothing" do
62
+ @linked_list.shift.should == nil
63
+ @linked_list.to_a.should == []
64
+ end
65
+ end
66
+
67
+ describe "#length" do
68
+ it "is zero for the empty list" do
69
+ @linked_list.length.should == 0
70
+ end
71
+
72
+ it "returns the number of items in the list" do
73
+ @linked_list.push(1)
74
+ @linked_list.push(2)
75
+ @linked_list.length.should == 2
76
+ end
77
+
78
+ it "returns the number of items in the list even after a delete" do
79
+ @linked_list.push(1)
80
+ two = @linked_list.push(2)
81
+ @linked_list.push(3)
82
+ @linked_list.delete(two)
83
+ @linked_list.length.should == 2
84
+ end
85
+
86
+ it "returns zero when you remove all items" do
87
+ one = @linked_list.push(1)
88
+ @linked_list.delete(one)
89
+ @linked_list.length.should == 0
90
+ end
91
+ end
92
+ end
data/spec/lru_spec.rb ADDED
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe Lru do
4
+ before do
5
+ @lru = Lru.new(IndexedBoundedStack.new(BoundedStack.new(LinkedList.new, 5)))
6
+ end
7
+
8
+ describe "#include?" do
9
+ it "hits go up as we check inclusion of items that exist" do
10
+ @lru.include?(1)
11
+ @lru.misses.should == 1
12
+ @lru.hits.should == 0
13
+
14
+ @lru.include?(1)
15
+ @lru.misses.should == 1
16
+ @lru.hits.should == 1
17
+ end
18
+
19
+ it "increments evictions as the stack fills up" do
20
+ 5.times do |i|
21
+ @lru.include?(i + 1)
22
+ end
23
+ @lru.evictions.should == 0
24
+ @lru.include?(6)
25
+ @lru.evictions.should == 1
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,9 @@
1
+ dir = File.dirname(__FILE__)
2
+ $LOAD_PATH.unshift "#{dir}/../lib"
3
+
4
+ require 'rubygems'
5
+ require 'spec'
6
+ require 'lrjew'
7
+
8
+ Spec::Runner.configure do |config|
9
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lrjew
3
+ version: !ruby/object:Gem::Version
4
+ hash: 15
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ version: "1.0"
10
+ platform: ruby
11
+ authors:
12
+ - Steve Jenson
13
+ - Nick Kallen
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-07-15 00:00:00 -07:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: An LRU simulator
23
+ email:
24
+ - stevej@twitter.com
25
+ - nick@twitter.com
26
+ executables:
27
+ - lrjew
28
+ extensions: []
29
+
30
+ extra_rdoc_files: []
31
+
32
+ files:
33
+ - Rakefile
34
+ - lib/lrjew/bounded_stack.rb
35
+ - lib/lrjew/indexed_bounded_stack.rb
36
+ - lib/lrjew/linked_list.rb
37
+ - lib/lrjew/lru.rb
38
+ - lib/lrjew.rb
39
+ - bin/lrjew
40
+ - spec/bounded_stack_spec.rb
41
+ - spec/indexed_bounded_stack_spec.rb
42
+ - spec/linked_list_spec.rb
43
+ - spec/lru_spec.rb
44
+ - spec/spec_helper.rb
45
+ has_rdoc: true
46
+ homepage:
47
+ licenses: []
48
+
49
+ post_install_message:
50
+ rdoc_options: []
51
+
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ hash: 3
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ requirements: []
73
+
74
+ rubyforge_project:
75
+ rubygems_version: 1.3.7
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: An LRU simulator
79
+ test_files:
80
+ - spec/bounded_stack_spec.rb
81
+ - spec/indexed_bounded_stack_spec.rb
82
+ - spec/linked_list_spec.rb
83
+ - spec/lru_spec.rb
84
+ - spec/spec_helper.rb