lrjew 1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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