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 +9 -0
- data/bin/lrjew +20 -0
- data/lib/lrjew/bounded_stack.rb +36 -0
- data/lib/lrjew/indexed_bounded_stack.rb +39 -0
- data/lib/lrjew/linked_list.rb +82 -0
- data/lib/lrjew/lru.rb +32 -0
- data/lib/lrjew.rb +6 -0
- data/spec/bounded_stack_spec.rb +29 -0
- data/spec/indexed_bounded_stack_spec.rb +43 -0
- data/spec/linked_list_spec.rb +92 -0
- data/spec/lru_spec.rb +28 -0
- data/spec/spec_helper.rb +9 -0
- metadata +84 -0
data/Rakefile
ADDED
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,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
|
data/spec/spec_helper.rb
ADDED
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
|