lru_redux 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4e9a008051600ae0ef7295fcbd1826f9577e9651
4
+ data.tar.gz: 92cfb4d20f44bf999b1bfd113dc517ed902af2d3
5
+ SHA512:
6
+ metadata.gz: 7687af703aec4575050e5bff2f2e4d0f7d50137e427f3b1ef734de963b39ef291950afe023b448a7d9a27f175b38efacbccdd4d149d153b24e745b3af2e66004
7
+ data.tar.gz: ef7357e9b3c1940ccc6552c261e906094bb1065bfc38eccae83db8386d564f0bb425ae0a91b834c77b7fec2e5dca58ef6ca1edfa05ce02139e58579433e2843e
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.swp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in lru_redux.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,6 @@
1
+ guard 'minitest', :focus_on_failed => true do
2
+ watch(%r{^test/.+_test\.rb$})
3
+ watch(%r{^lib/lru_redux/(.+)\.rb$}) { |m| "test/#{m[1]}_test.rb" }
4
+ end
5
+
6
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Sam Saffron
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # LruRedux
2
+
3
+ An efficient thread safe lru cache.
4
+
5
+ Lru Redux uses a Hash/Double link list backed storage to keep track of nodes in a cache based on last usage.
6
+
7
+ This provides a correct and well specified LRU cache, that is very efficient. Additionally you can optionally use a thread safe wrapper.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'lru_redux'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install lru_redux
22
+
23
+ ## Usage
24
+
25
+ ```ruby
26
+ require 'lru_redux'
27
+ # non thread safe
28
+ cache = LruRedux::Cache(100)
29
+ cache[:a] = "1"
30
+ cache.to_a
31
+ # [[:a,"1"]]
32
+ cache.delete(:a)
33
+ cache.each {|k,v| p "#{k} #{v}"}
34
+ # nothing
35
+
36
+ # for thread safe
37
+ cache = LruRedux::ThreadSafeCache(100)
38
+
39
+ ```
40
+
41
+ ## Benchmarks
42
+
43
+ see: benchmark directory
44
+
45
+ ```
46
+ sam@ubuntu:~/Source/lru_redux/bench$ ruby ./bench.rb
47
+ Rehearsal ---------------------------------------------------------
48
+ thread safe lru 27.940000 0.020000 27.960000 ( 28.000938)
49
+ lru gem 2.300000 0.000000 2.300000 ( 2.305732)
50
+ lru_cache gem 1.960000 0.010000 1.970000 ( 1.975683)
51
+ lru_redux gem 1.710000 0.000000 1.710000 ( 1.704134)
52
+ lru_redux thread safe 2.830000 0.000000 2.830000 ( 2.837608)
53
+ ----------------------------------------------- total: 36.770000sec
54
+
55
+ user system total real
56
+ thread safe lru 27.740000 0.000000 27.740000 ( 27.756163)
57
+ lru gem 2.250000 0.000000 2.250000 ( 2.252772)
58
+ lru_cache gem 1.960000 0.000000 1.960000 ( 1.963679)
59
+ lru_redux gem 1.710000 0.000000 1.710000 ( 1.712147)
60
+ lru_redux thread safe 2.750000 0.000000 2.750000 ( 2.752526)
61
+
62
+ ```
63
+
64
+
65
+ ## Contributing
66
+
67
+ 1. Fork it
68
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
69
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
70
+ 4. Push to the branch (`git push origin my-new-feature`)
71
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.pattern = "test/*_test.rb"
7
+ end
data/bench/bench.rb ADDED
@@ -0,0 +1,36 @@
1
+ require "rubygems"
2
+ require "lru"
3
+ require "benchmark"
4
+ require "lru_cache"
5
+ require "threadsafe-lru"
6
+ $LOAD_PATH.unshift File.expand_path '../lib'
7
+ require File.expand_path('../../lib/lru_redux', __FILE__)
8
+
9
+ lru = Cache::LRU.new(max_elements: 1_000)
10
+ lru_cache = LRUCache.new(1_000)
11
+
12
+ lru_redux = LruRedux::Cache.new(1_000)
13
+ lru_redux_thread_safe = LruRedux::ThreadSafeCache.new(1_000)
14
+ thread_safe_lru = ThreadSafeLru::LruCache.new(1_000)
15
+
16
+ bm = Benchmark.bmbm do |bm|
17
+
18
+ bm.report "thread safe lru" do
19
+ 1_000_000.times do
20
+ thread_safe_lru.get(rand(2_000)){ :value }
21
+ end
22
+ end
23
+
24
+ [
25
+ [lru, "lru gem"],
26
+ [lru_cache, "lru_cache gem"],
27
+ [lru_redux, "lru_redux gem"],
28
+ [lru_redux_thread_safe, "lru_redux thread safe"]
29
+ ].each do |cache, name|
30
+ bm.report name do
31
+ 1_000_000.times do
32
+ cache[rand(2_000)] ||= :value
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,128 @@
1
+ class LruRedux::Cache
2
+
3
+ # for high efficiency nodes in double linked list are stored in arrays
4
+ # [prev,key,val,next]
5
+
6
+ def initialize(size)
7
+ @size = size
8
+ @data = {}
9
+ @head = nil
10
+ @tail = nil
11
+ end
12
+
13
+ def [](key)
14
+ node = @data[key]
15
+ if node
16
+ move_to_head(node)
17
+ node.value
18
+ end
19
+ end
20
+
21
+ def []=(key,val)
22
+ node = @data[key]
23
+ if node
24
+ move_to_head(node)
25
+ node.value = val
26
+ else
27
+ pop_tail
28
+ @data[key] = add_to_head(key,val)
29
+ end
30
+ val
31
+ end
32
+
33
+ def each
34
+ if n = @head
35
+ while n
36
+ yield [n.key, n.value]
37
+ n = n.prev
38
+ end
39
+ end
40
+ end
41
+
42
+ # used further up the chain, non thread safe each
43
+ alias_method :each_unsafe, :each
44
+
45
+ def to_a
46
+ a = []
47
+ self.each_unsafe do |k,v|
48
+ a << [k,v]
49
+ end
50
+ a
51
+ end
52
+
53
+ def delete(k)
54
+ node = @data[k]
55
+ if node
56
+ @data.delete(k)
57
+ prev = node.prev
58
+ nex = node.next
59
+
60
+ prev.next = nex if prev
61
+ nex.prev = prev if nex
62
+ end
63
+ end
64
+
65
+ def count
66
+ @data.count
67
+ end
68
+
69
+ # for cache validation only, ensures all is sound
70
+ def valid?
71
+ expected = {}
72
+ self.each_unsafe do |k,v|
73
+ expected[k] = v
74
+ end
75
+ expected == @data
76
+ end
77
+
78
+ protected
79
+
80
+ def add_to_head(key,val)
81
+ if @head.nil?
82
+ @tail = @head = Node.new(key,val,nil,nil)
83
+ else
84
+ node = Node.new(key,val,@head,nil)
85
+ @head = @head.next = node
86
+ end
87
+ end
88
+
89
+ def move_to_head(node)
90
+ return unless @head && node != @head
91
+
92
+ prev = node.prev
93
+ nex = node.next
94
+
95
+ if prev
96
+ prev.next = nex
97
+ else
98
+ @tail = nex
99
+ end
100
+
101
+ if nex
102
+ nex.prev = prev
103
+ end
104
+
105
+ @head.next = node
106
+ node.prev = @head
107
+ @head = node
108
+ end
109
+
110
+ def pop_tail
111
+ if @data.length == @size
112
+ @data.delete(@tail.key)
113
+ @tail = @tail.next
114
+ @tail.prev = nil
115
+ end
116
+ end
117
+
118
+
119
+ class Node
120
+ attr_accessor :key, :value, :next, :prev
121
+ def initialize(key,value,prev,nex)
122
+ self.key = key
123
+ self.value = value
124
+ self.prev = prev
125
+ self.next = nex
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,20 @@
1
+ require 'thread'
2
+ class LruRedux::ThreadSafeCache < LruRedux::Cache
3
+ def initialize(size)
4
+ @lock = Mutex.new
5
+ super(size)
6
+ end
7
+
8
+ def self.synchronize(*methods)
9
+ methods.each do |method|
10
+ define_method method do |*args, &blk|
11
+ @lock.synchronize do
12
+ super(*args,&blk)
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ synchronize :[], :[]=, :each, :to_a, :delete, :count, :valid?
19
+
20
+ end
@@ -0,0 +1,3 @@
1
+ module LruRedux
2
+ VERSION = "0.0.1"
3
+ end
data/lib/lru_redux.rb ADDED
@@ -0,0 +1,4 @@
1
+ module LruRedux; end
2
+ require "lru_redux/version"
3
+ require "lru_redux/cache"
4
+ require "lru_redux/thread_safe_cache"
data/lru_redux.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'lru_redux/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "lru_redux"
8
+ spec.version = LruRedux::VERSION
9
+ spec.authors = ["Sam Saffron"]
10
+ spec.email = ["sam.saffron@gmail.com"]
11
+ spec.description = %q{An efficient implementation of an lru cache}
12
+ spec.summary = %q{An efficient implementation of an lru cache}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "minitest"
24
+ spec.add_development_dependency "guard-minitest"
25
+ spec.add_development_dependency "guard"
26
+ spec.add_development_dependency "rb-inotify"
27
+ end
@@ -0,0 +1,50 @@
1
+ require 'lru_redux'
2
+ require 'minitest/autorun'
3
+ require 'minitest/pride'
4
+
5
+ class CacheTest < MiniTest::Unit::TestCase
6
+ def setup
7
+ @c = LruRedux::Cache.new(3)
8
+ end
9
+
10
+ def test_drops_old
11
+ @c[:a] = 1
12
+ @c[:b] = 2
13
+ @c[:c] = 3
14
+ @c[:d] = 4
15
+
16
+ assert_equal [[:d,4],[:c,3],[:b,2]], @c.to_a
17
+ assert_nil @c[:a]
18
+ end
19
+
20
+ def test_pushes_lru_to_back
21
+ @c[:a] = 1
22
+ @c[:b] = 2
23
+ @c[:c] = 3
24
+
25
+ @c[:a]
26
+ @c[:d] = 4
27
+
28
+ assert_equal [[:d,4],[:a,1],[:c,3]], @c.to_a
29
+ assert_nil @c[:b]
30
+ end
31
+
32
+
33
+ def test_delete
34
+ @c[:a] = 1
35
+ @c[:b] = 2
36
+ @c[:c] = 3
37
+
38
+ @c.delete(:b)
39
+ assert_equal [[:c,3],[:a,1]], @c.to_a
40
+ assert_nil @c[:b]
41
+ end
42
+
43
+ def test_update
44
+ @c[:a] = 1
45
+ @c[:b] = 2
46
+ @c[:c] = 3
47
+ @c[:a] = 99
48
+ assert_equal [[:a,99],[:c,3],[:b,2]], @c.to_a
49
+ end
50
+ end
@@ -0,0 +1,22 @@
1
+ require 'lru_redux'
2
+ require 'minitest/autorun'
3
+ require 'minitest/pride'
4
+
5
+ class ThreadSafeCacheTest < MiniTest::Unit::TestCase
6
+ def test_additions
7
+ cache = LruRedux::ThreadSafeCache.new(1000)
8
+ threads = []
9
+ 4.times do |t|
10
+ threads << Thread.new do
11
+ 250.times do |i|
12
+ cache[i] = "#{t} #{i}"
13
+ end
14
+ end
15
+ end
16
+
17
+ threads.each{|t| t.join}
18
+ assert_equal cache.count,250
19
+ assert true, cache.valid?
20
+
21
+ end
22
+ end
metadata ADDED
@@ -0,0 +1,144 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lru_redux
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Sam Saffron
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-04-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: guard-minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rb-inotify
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: An efficient implementation of an lru cache
98
+ email:
99
+ - sam.saffron@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - .gitignore
105
+ - Gemfile
106
+ - Guardfile
107
+ - LICENSE.txt
108
+ - README.md
109
+ - Rakefile
110
+ - bench/bench.rb
111
+ - lib/lru_redux.rb
112
+ - lib/lru_redux/cache.rb
113
+ - lib/lru_redux/thread_safe_cache.rb
114
+ - lib/lru_redux/version.rb
115
+ - lru_redux.gemspec
116
+ - test/cache_test.rb
117
+ - test/thread_safe_cache_test.rb
118
+ homepage: ''
119
+ licenses:
120
+ - MIT
121
+ metadata: {}
122
+ post_install_message:
123
+ rdoc_options: []
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - '>='
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - '>='
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ requirements: []
137
+ rubyforge_project:
138
+ rubygems_version: 2.0.0.rc.2
139
+ signing_key:
140
+ specification_version: 4
141
+ summary: An efficient implementation of an lru cache
142
+ test_files:
143
+ - test/cache_test.rb
144
+ - test/thread_safe_cache_test.rb