hyper_iterator 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +5 -0
- data/Dockerfile +13 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +21 -0
- data/README.md +184 -0
- data/Rakefile +14 -0
- data/UPDATE.md +21 -0
- data/benchmark/each_bang_bm.rb +85 -0
- data/benchmark/each_slice_bang_bm.rb +99 -0
- data/benchmark/memory_bm/each.rb +23 -0
- data/benchmark/memory_bm/each_bang.rb +31 -0
- data/benchmark/memory_bm/each_slice.rb +27 -0
- data/benchmark/memory_bm/each_slice_bang.rb +27 -0
- data/bin/bm +14 -0
- data/bin/console +14 -0
- data/bin/setup +9 -0
- data/hyper_iterator.gemspec +32 -0
- data/lib/hyper_iterator/version.rb +3 -0
- data/lib/hyper_iterator.rb +14 -0
- data/lib/iterators/each_bang.rb +8 -0
- data/lib/iterators/each_slice_bang.rb +16 -0
- metadata +122 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f776d5a162fa0d997d516aee5ced0de1d1834297
|
4
|
+
data.tar.gz: 4cfe8ac82bf8931932f886a466fac97bbbeec80c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5660248cb7aab3bdf506ed1336e3670e0a22ed9d8e460e7748ea347ed2720b46ba32383eb327f16d7e4f6b23dd734829599c5cc7a37725c3ed3845c7558756aa
|
7
|
+
data.tar.gz: bb43e91db9b085103bc59db9191c6b896d1e8b79c55fc51875a5678c0742e0cc96e5d9e3ec2ad0788e548fdbf45257b43865c65bd619c3859bc102b015b1e938
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Dockerfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Pick a version of your choice
|
2
|
+
# If you don't see one, please check this page
|
3
|
+
# https://hub.docker.com/_/ruby/
|
4
|
+
# and add it here
|
5
|
+
|
6
|
+
# FROM ruby:2.1.10
|
7
|
+
# FROM ruby:2.2.2
|
8
|
+
# FROM ruby:2.2.6
|
9
|
+
# FROM ruby:2.3.0
|
10
|
+
# FROM ruby:2.3.3
|
11
|
+
FROM ruby:2.4.0
|
12
|
+
RUN ruby -v
|
13
|
+
RUN gem install require_all
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Edmund Li
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
[![Build Status](https://travis-ci.org/EdmundLeex/hyper_iterator.svg?branch=master)](https://travis-ci.org/EdmundLeex/hyper_iterator)
|
2
|
+
|
3
|
+
# HyperIterator
|
4
|
+
|
5
|
+
**Caution: this gem monkey patches Ruby's `Array` class.**
|
6
|
+
|
7
|
+
Inspired by [Ruby Performance Optimization](https://media.pragprog.com/titles/adrpo/iterators.pdf),
|
8
|
+
HyperIterator is reimplementation of Ruby iterators in Ruby, designed to address performance
|
9
|
+
drawbacks from native implementations, mainly in memory usage.
|
10
|
+
|
11
|
+
The main idea is to remove objects from array during iteration. In iteration of large array,
|
12
|
+
this would allow garbage collection to happen before the iteration finishes, therefore reduce
|
13
|
+
memory usage.
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
*This is in experimental stage. More testing will be conducted.*
|
18
|
+
*You can see my [updates in here](UPDATE.md)*
|
19
|
+
|
20
|
+
Add this line to your application's Gemfile:
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
gem 'hyper_iterator'
|
24
|
+
```
|
25
|
+
|
26
|
+
And then execute:
|
27
|
+
|
28
|
+
$ bundle
|
29
|
+
|
30
|
+
Or install it yourself as:
|
31
|
+
|
32
|
+
$ gem install hyper_iterator
|
33
|
+
|
34
|
+
## Available Methods (adding more)
|
35
|
+
|
36
|
+
- `each_slice!`
|
37
|
+
- `each!`
|
38
|
+
|
39
|
+
## Gotcha
|
40
|
+
|
41
|
+
These methods work just as the non bang version, except that, it **WILL MUTATE** the original array
|
42
|
+
by **REMOVING ALL** the elements from it.
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
arr = (1..10).to_a
|
46
|
+
arr.each! { |el| ... }
|
47
|
+
# After the iteration, the arr is empty!
|
48
|
+
# arr => []
|
49
|
+
```
|
50
|
+
|
51
|
+
## Benchmark
|
52
|
+
|
53
|
+
### For basic benchmarks
|
54
|
+
```
|
55
|
+
rake benchmark
|
56
|
+
```
|
57
|
+
|
58
|
+
#### Example report
|
59
|
+
|
60
|
+
```
|
61
|
+
---------------------------------------------------------
|
62
|
+
---------------------- each_slice! ----------------------
|
63
|
+
---------------------------------------------------------
|
64
|
+
------------------ Garbage Collection -------------------
|
65
|
+
Array#each_slice
|
66
|
+
----------------------
|
67
|
+
100 100 100 100 100 100 100 100 100 100 100 100 100
|
68
|
+
|
69
|
+
Array#each_slice!
|
70
|
+
----------------------
|
71
|
+
100 92 84 76 68 60 52 44 36 28 20 12 4
|
72
|
+
---------------------------------------------------------
|
73
|
+
-------------------- Objects Created --------------------
|
74
|
+
Array#each_slice
|
75
|
+
----------------------
|
76
|
+
# of arrays: 126
|
77
|
+
# of nodes: 2
|
78
|
+
|
79
|
+
Array#each_slice!
|
80
|
+
----------------------
|
81
|
+
# of arrays: 125
|
82
|
+
# of nodes: 0
|
83
|
+
---------------------------------------------------------
|
84
|
+
--------------- Execution Time Comparison ---------------
|
85
|
+
Rehearsal -----------------------------------------------
|
86
|
+
each_slice 0.040000 0.000000 0.040000 ( 0.049524)
|
87
|
+
each_slice! 0.040000 0.010000 0.050000 ( 0.047753)
|
88
|
+
-------------------------------------- total: 0.090000sec
|
89
|
+
|
90
|
+
user system total real
|
91
|
+
each_slice 0.040000 0.010000 0.050000 ( 0.048487)
|
92
|
+
each_slice! 0.040000 0.010000 0.050000 ( 0.052009)
|
93
|
+
---------------------------------------------------------
|
94
|
+
```
|
95
|
+
|
96
|
+
### Memory Benchmark
|
97
|
+
|
98
|
+
To eliminate the variable of different machines, you will need to install [Docker](https://docs.docker.com/engine/installation/)
|
99
|
+
for this.
|
100
|
+
|
101
|
+
1. Go to [Docker](https://docs.docker.com/engine/installation/) to download and install Docker
|
102
|
+
2. Go to the `Dockerfile`, and pick a Ruby version of your choice
|
103
|
+
3. Run `bin/setup` in your command line
|
104
|
+
4. Run `bin/bm` in your command line
|
105
|
+
|
106
|
+
#### Methodology
|
107
|
+
|
108
|
+
The native implementation (non bang version) retains the entire array. So if we keep on adding objects to
|
109
|
+
memory, it will blow up pretty quickly (given the memory is limited).
|
110
|
+
|
111
|
+
While using the bang version in this gem, the refernce to the objects store in the array will be removed.
|
112
|
+
So the Garbage Collector (GC) can see them as removable objects and clean them up.
|
113
|
+
|
114
|
+
The test code simply creates duplicate object as it iterates. Since the bang version iterator keeps removing
|
115
|
+
the references, the memory gets to recycle itself. In this repot, it will show as more iterations compare
|
116
|
+
to the non bang version.
|
117
|
+
|
118
|
+
This is because as the memory gets freed up, we just have more rooms to store new objects, until the GC is
|
119
|
+
not freeing up memory fast enough.
|
120
|
+
|
121
|
+
FYI: In this test, the empty array (created from every iteration to store the new objects) is also otaking
|
122
|
+
up more space. If you pop the empty array off (see [./benchmark/memory_bm/each_bang.rb](./benchmark/memory_bm/each_bang.rb)),
|
123
|
+
you can get a lot more iterations!
|
124
|
+
|
125
|
+
#### Example report
|
126
|
+
|
127
|
+
- #i is the nth iteration
|
128
|
+
- The rest is time spent in execution
|
129
|
+
- The last line is empty because memory blew up
|
130
|
+
|
131
|
+
```
|
132
|
+
----------------- Array#each ------------------
|
133
|
+
#i user system total real
|
134
|
+
0 0.010000 0.000000 0.010000 ( 0.043329)
|
135
|
+
1 0.000000 0.010000 0.010000 ( 0.010112)
|
136
|
+
2 0.000000 0.000000 0.000000 ( 0.025059)
|
137
|
+
3 0.000000 0.000000 0.000000 ( 0.010436)
|
138
|
+
4 0.000000 0.000000 0.000000 ( 0.008007)
|
139
|
+
5 0.000000 0.010000 0.010000 ( 0.009692)
|
140
|
+
6 0.000000 0.010000 0.010000 ( 0.050283)
|
141
|
+
7
|
142
|
+
----------------- Array#each! -----------------
|
143
|
+
#i user system total real
|
144
|
+
0 0.000000 0.000000 0.000000 ( 0.004570)
|
145
|
+
1 0.010000 0.000000 0.010000 ( 0.006715)
|
146
|
+
2 0.000000 0.010000 0.010000 ( 0.040050)
|
147
|
+
3 0.010000 0.000000 0.010000 ( 0.009338)
|
148
|
+
4 0.000000 0.000000 0.000000 ( 0.006622)
|
149
|
+
5 0.000000 0.010000 0.010000 ( 0.019370)
|
150
|
+
6 0.000000 0.000000 0.000000 ( 0.012097)
|
151
|
+
7 0.000000 0.010000 0.010000 ( 0.025825)
|
152
|
+
8 0.000000 0.010000 0.010000 ( 0.014587)
|
153
|
+
9 0.000000 0.010000 0.010000 ( 0.014893)
|
154
|
+
10 0.000000 0.000000 0.000000 ( 0.026595)
|
155
|
+
11 0.000000 0.000000 0.000000 ( 0.012371)
|
156
|
+
12 0.010000 0.000000 0.010000 ( 0.023964)
|
157
|
+
13 0.000000 0.000000 0.000000 ( 0.007783)
|
158
|
+
14 0.000000 0.000000 0.000000 ( 0.004344)
|
159
|
+
15 0.010000 0.020000 0.030000 ( 0.119632)
|
160
|
+
16 0.000000 0.010000 0.010000 ( 0.010222)
|
161
|
+
17 0.000000 0.000000 0.000000 ( 0.009091)
|
162
|
+
18 %
|
163
|
+
```
|
164
|
+
|
165
|
+
## Development
|
166
|
+
|
167
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
168
|
+
|
169
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
170
|
+
|
171
|
+
## Contributing
|
172
|
+
|
173
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/edmundleex/hyper_iterator. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
174
|
+
|
175
|
+
## TODO
|
176
|
+
|
177
|
+
- [ ] `each_slice!` should go to `Enumerable` module
|
178
|
+
- [ ] `each!` for `Hash`
|
179
|
+
- [ ] More comon iterators...
|
180
|
+
|
181
|
+
## License
|
182
|
+
|
183
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
184
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rake/testtask"
|
3
|
+
|
4
|
+
Rake::TestTask.new(:test) do |t|
|
5
|
+
t.libs << "test"
|
6
|
+
t.libs << "lib"
|
7
|
+
t.test_files = FileList['test/**/*_test.rb']
|
8
|
+
end
|
9
|
+
|
10
|
+
task :benchmark do
|
11
|
+
Dir['./benchmark/*.rb'].each { |f| ruby f }
|
12
|
+
end
|
13
|
+
|
14
|
+
task :default => :test
|
data/UPDATE.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
### 3/16/17
|
2
|
+
|
3
|
+
- Tweak the two benchmark file and made it work
|
4
|
+
- This time around, I am dynamically creating new arrays and counting the number of
|
5
|
+
iterations before it blows up. And it's clear that the `each!` can complete more iteration
|
6
|
+
before it uses all the memory
|
7
|
+
- Changed `each_slice!` implementation to avoid using `slice!`. The execution time comp
|
8
|
+
looks good (seems even faster than the native `each_slice`). And the memory usage comp
|
9
|
+
looks very good too!
|
10
|
+
|
11
|
+
### 3/14/17
|
12
|
+
|
13
|
+
- Testing benchmark in containerized environment to simulate limited allocated memory.
|
14
|
+
- `each!` seems to be working fine. I can see gradual memory release in docker stat even
|
15
|
+
not by a large margin.
|
16
|
+
- Next thing to test would be creating more objects within the iteration to use up more
|
17
|
+
memory. Given that with 4MB allocation, the current [./benchmark/each.rb](benchmark/each.rb)
|
18
|
+
and [./benchmark/each_bang.rb](benchmark/each_bang.rb) is running at the edge of using up
|
19
|
+
all memory, creating more objects supposedly would kill the process using `each` while
|
20
|
+
`each!` should survive.
|
21
|
+
- `each_slice!` implementation is very slow. Possibly because `slice!` is O(n)?
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require_relative '../lib/hyper_iterator'
|
3
|
+
|
4
|
+
class Thing; end
|
5
|
+
|
6
|
+
puts '---------------------------------------------------------'
|
7
|
+
puts '------------------------- each! -------------------------'
|
8
|
+
|
9
|
+
puts '---------------------------------------------------------'
|
10
|
+
puts '------------------ Garbage Collection -------------------'
|
11
|
+
|
12
|
+
puts 'Array#each'
|
13
|
+
puts '----------------------'
|
14
|
+
|
15
|
+
arr = Array.new(100) { Thing.new }
|
16
|
+
line = ''
|
17
|
+
|
18
|
+
arr.each do |el|
|
19
|
+
GC.start
|
20
|
+
print ObjectSpace.each_object(Thing).count
|
21
|
+
print ' '
|
22
|
+
end
|
23
|
+
|
24
|
+
puts
|
25
|
+
puts
|
26
|
+
|
27
|
+
puts 'Array#each!'
|
28
|
+
puts '----------------------'
|
29
|
+
|
30
|
+
arr = Array.new(100) { Thing.new }
|
31
|
+
|
32
|
+
arr.each! do |el|
|
33
|
+
GC.start
|
34
|
+
print ObjectSpace.each_object(Thing).count
|
35
|
+
print ' '
|
36
|
+
end
|
37
|
+
|
38
|
+
puts
|
39
|
+
|
40
|
+
puts '---------------------------------------------------------'
|
41
|
+
puts '-------------------- Objects Created --------------------'
|
42
|
+
|
43
|
+
|
44
|
+
GC.disable
|
45
|
+
arr = Array.new(1000) { Thing.new }
|
46
|
+
|
47
|
+
before = ObjectSpace.count_objects
|
48
|
+
arr.each do |el|
|
49
|
+
end
|
50
|
+
after = ObjectSpace.count_objects
|
51
|
+
|
52
|
+
puts 'Array#each'
|
53
|
+
puts '----------------------'
|
54
|
+
puts "# of arrays: %d" % (after[:T_ARRAY] - before[:T_ARRAY])
|
55
|
+
puts "# of nodes: %d" % (after[:T_NODE] - before[:T_NODE])
|
56
|
+
|
57
|
+
puts
|
58
|
+
|
59
|
+
arr = Array.new(1000) { Thing.new }
|
60
|
+
|
61
|
+
before = ObjectSpace.count_objects
|
62
|
+
arr.each! do |el|
|
63
|
+
end
|
64
|
+
after = ObjectSpace.count_objects
|
65
|
+
|
66
|
+
puts 'Array#each!'
|
67
|
+
puts '----------------------'
|
68
|
+
puts "# of arrays: %d" % (after[:T_ARRAY] - before[:T_ARRAY])
|
69
|
+
puts "# of nodes: %d" % (after[:T_NODE] - before[:T_NODE])
|
70
|
+
|
71
|
+
# puts '---------------------------------------------------------'
|
72
|
+
# puts '--------------- Execution Time Comparison ---------------'
|
73
|
+
|
74
|
+
# GC.enable
|
75
|
+
# n = 10
|
76
|
+
# arr = Array.new(1_000_000) { Thing.new }
|
77
|
+
|
78
|
+
# Benchmark.bmbm(7) do |x|
|
79
|
+
# x.report('each!') { n.times { arr.each! { |el| nil } } }
|
80
|
+
# x.report('each') { n.times { arr.each { |el| nil } } }
|
81
|
+
# end
|
82
|
+
|
83
|
+
puts '---------------------------------------------------------'
|
84
|
+
|
85
|
+
puts
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require_relative '../lib/hyper_iterator'
|
3
|
+
|
4
|
+
class Thing; end
|
5
|
+
|
6
|
+
puts '---------------------------------------------------------'
|
7
|
+
puts '---------------------- each_slice! ----------------------'
|
8
|
+
|
9
|
+
puts '---------------------------------------------------------'
|
10
|
+
puts '------------------ Garbage Collection -------------------'
|
11
|
+
|
12
|
+
slize_size = rand(1..10)
|
13
|
+
|
14
|
+
puts 'Array#each_slice'
|
15
|
+
puts '----------------------'
|
16
|
+
|
17
|
+
arr = Array.new(100) { Thing.new }
|
18
|
+
line = ''
|
19
|
+
|
20
|
+
arr.each_slice(slize_size) do |slice|
|
21
|
+
GC.start
|
22
|
+
print ObjectSpace.each_object(Thing).count
|
23
|
+
print ' '
|
24
|
+
end
|
25
|
+
|
26
|
+
puts
|
27
|
+
puts
|
28
|
+
|
29
|
+
puts 'Array#each_slice!'
|
30
|
+
puts '----------------------'
|
31
|
+
|
32
|
+
arr = Array.new(100) { Thing.new }
|
33
|
+
|
34
|
+
arr.each_slice!(slize_size) do |slice|
|
35
|
+
GC.start
|
36
|
+
print ObjectSpace.each_object(Thing).count
|
37
|
+
print ' '
|
38
|
+
end
|
39
|
+
|
40
|
+
puts
|
41
|
+
|
42
|
+
puts '---------------------------------------------------------'
|
43
|
+
puts '-------------------- Objects Created --------------------'
|
44
|
+
|
45
|
+
|
46
|
+
GC.disable
|
47
|
+
arr = Array.new(1000) { Thing.new }
|
48
|
+
|
49
|
+
before = ObjectSpace.count_objects
|
50
|
+
arr.each_slice(slize_size) do |slice|
|
51
|
+
end
|
52
|
+
after = ObjectSpace.count_objects
|
53
|
+
|
54
|
+
puts 'Array#each_slice'
|
55
|
+
puts '----------------------'
|
56
|
+
puts "# of arrays: %d" % (after[:T_ARRAY] - before[:T_ARRAY])
|
57
|
+
puts "# of nodes: %d" % (after[:T_NODE] - before[:T_NODE])
|
58
|
+
|
59
|
+
puts
|
60
|
+
|
61
|
+
arr = Array.new(1000) { Thing.new }
|
62
|
+
|
63
|
+
before = ObjectSpace.count_objects
|
64
|
+
arr.each_slice!(slize_size) do |slice|
|
65
|
+
end
|
66
|
+
after = ObjectSpace.count_objects
|
67
|
+
|
68
|
+
puts 'Array#each_slice!'
|
69
|
+
puts '----------------------'
|
70
|
+
puts "# of arrays: %d" % (after[:T_ARRAY] - before[:T_ARRAY])
|
71
|
+
puts "# of nodes: %d" % (after[:T_NODE] - before[:T_NODE])
|
72
|
+
|
73
|
+
puts '---------------------------------------------------------'
|
74
|
+
puts '--------------- Execution Time Comparison ---------------'
|
75
|
+
|
76
|
+
GC.enable
|
77
|
+
n = 10
|
78
|
+
arr = Array.new(1_000_000) { Thing.new }
|
79
|
+
|
80
|
+
Benchmark.bmbm(7) do |x|
|
81
|
+
x.report('each_slice') do
|
82
|
+
n.times do
|
83
|
+
arr.each_slice(slize_size) do |slice|
|
84
|
+
slice.each { |el| nil }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
x.report('each_slice!') do
|
89
|
+
n.times do
|
90
|
+
arr.each_slice!(slize_size) do |slice|
|
91
|
+
slice.each { |el| nil }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
puts '---------------------------------------------------------'
|
98
|
+
|
99
|
+
puts
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require_relative '../../lib/hyper_iterator'
|
3
|
+
$stdout.sync = true
|
4
|
+
|
5
|
+
puts
|
6
|
+
puts '----------------- Array#each ------------------'
|
7
|
+
puts '#i user system total real'
|
8
|
+
|
9
|
+
base_arr = Array.new(10000) { '-' * 10 }
|
10
|
+
arrs = [base_arr]
|
11
|
+
|
12
|
+
i = 0
|
13
|
+
while true
|
14
|
+
print "#{i} "
|
15
|
+
arr = []
|
16
|
+
report = Benchmark.measure do
|
17
|
+
arrs[i].each { |el| arr << el.dup }
|
18
|
+
end
|
19
|
+
|
20
|
+
puts report
|
21
|
+
arrs << arr
|
22
|
+
i += 1
|
23
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require_relative '../../lib/hyper_iterator'
|
3
|
+
$stdout.sync = true
|
4
|
+
|
5
|
+
puts
|
6
|
+
puts '----------------- Array#each! -----------------'
|
7
|
+
puts '#i user system total real'
|
8
|
+
|
9
|
+
base_arr = Array.new(10000) { '-' * 10 }
|
10
|
+
arrs = [base_arr]
|
11
|
+
|
12
|
+
i = 0
|
13
|
+
while true
|
14
|
+
print "#{i} "
|
15
|
+
arr = []
|
16
|
+
report = Benchmark.measure do
|
17
|
+
arrs[i].each! { |el| arr << el.dup }
|
18
|
+
|
19
|
+
# Try to use this line and see how many more iterations you can get
|
20
|
+
# Remember to uncomment line#24 as well.
|
21
|
+
# You need to pop the array so the reference goes away.
|
22
|
+
# arrs[0].each! { |el| arr << el.dup }
|
23
|
+
end
|
24
|
+
|
25
|
+
# Uncomment this to use with the above line#22
|
26
|
+
# arrs.pop
|
27
|
+
|
28
|
+
puts report
|
29
|
+
arrs << arr
|
30
|
+
i += 1
|
31
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require_relative '../../lib/hyper_iterator'
|
3
|
+
$stdout.sync = true
|
4
|
+
|
5
|
+
puts 'Array#each_slice'
|
6
|
+
puts 'creating array'
|
7
|
+
base_arr = Array.new(10000) { '-' * 10 }
|
8
|
+
arrs = [base_arr]
|
9
|
+
puts 'finished creating base array'
|
10
|
+
puts 'each iteration begins'
|
11
|
+
|
12
|
+
i = 0
|
13
|
+
while true
|
14
|
+
print "#{i} "
|
15
|
+
arr = []
|
16
|
+
report = Benchmark.measure do
|
17
|
+
arrs[i].each_slice(100) do |sl|
|
18
|
+
sl.each { |el| arr << el.dup }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
puts report
|
23
|
+
arrs << arr
|
24
|
+
i += 1
|
25
|
+
end
|
26
|
+
|
27
|
+
puts
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require_relative '../../lib/hyper_iterator'
|
3
|
+
$stdout.sync = true
|
4
|
+
|
5
|
+
puts 'Array#each_slice'
|
6
|
+
puts 'creating array'
|
7
|
+
base_arr = Array.new(10000) { '-' * 10 }
|
8
|
+
arrs = [base_arr]
|
9
|
+
puts 'finished creating base array'
|
10
|
+
puts 'each iteration begins'
|
11
|
+
|
12
|
+
i = 0
|
13
|
+
while true
|
14
|
+
print "#{i} "
|
15
|
+
arr = []
|
16
|
+
report = Benchmark.measure do
|
17
|
+
arrs[i].each_slice!(100) do |sl|
|
18
|
+
sl.each { |el| arr << el.dup }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
puts report
|
23
|
+
arrs << arr
|
24
|
+
i += 1
|
25
|
+
end
|
26
|
+
|
27
|
+
puts
|
data/bin/bm
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
docker_command="docker run -m=4m -v `pwd`:`pwd` -w `pwd` hyper_iterator ruby ./benchmark/memory_bm"
|
4
|
+
if [ "$1" = "each" ] || [ "$1" = 1 ]; then
|
5
|
+
eval "$docker_command/each.rb"
|
6
|
+
eval "$docker_command/each_bang.rb"
|
7
|
+
elif [ "$1" = "each" ] || [ "$1" = 2 ]; then
|
8
|
+
eval "$docker_command/each_slice.rb"
|
9
|
+
eval "$docker_command/each_slice_bang.rb"
|
10
|
+
else
|
11
|
+
echo 'Please specify method for benchmarking. You can choose anyone from below:'
|
12
|
+
echo '1. each!'
|
13
|
+
echo '2. each_slice!'
|
14
|
+
fi
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "hyper_iterator"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'hyper_iterator/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "hyper_iterator"
|
8
|
+
spec.version = HyperIterator::VERSION
|
9
|
+
spec.summary = "Implementation of Ruby Iterators with High Performance"
|
10
|
+
spec.description = ""
|
11
|
+
|
12
|
+
spec.required_ruby_version = ">= 2.1.2"
|
13
|
+
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.authors = ["Edmund Li"]
|
17
|
+
spec.email = ["edmund.xz.lee@gmail.com"]
|
18
|
+
spec.homepage = "https://github.com/EdmundLeex/hyper_iterator"
|
19
|
+
|
20
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
21
|
+
f.match(%r{^(test|spec|features)/})
|
22
|
+
end
|
23
|
+
spec.bindir = "exe"
|
24
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
25
|
+
spec.require_paths = ["lib"]
|
26
|
+
|
27
|
+
spec.add_dependency 'require_all', '~> 1.4.0'
|
28
|
+
|
29
|
+
spec.add_development_dependency "bundler", "~> 1.14"
|
30
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
31
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hyper_iterator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Edmund Li
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-03-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: require_all
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.4.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.4.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.14'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.14'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '5.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '5.0'
|
69
|
+
description: ''
|
70
|
+
email:
|
71
|
+
- edmund.xz.lee@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".gitignore"
|
77
|
+
- ".travis.yml"
|
78
|
+
- Dockerfile
|
79
|
+
- Gemfile
|
80
|
+
- LICENSE.txt
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- UPDATE.md
|
84
|
+
- benchmark/each_bang_bm.rb
|
85
|
+
- benchmark/each_slice_bang_bm.rb
|
86
|
+
- benchmark/memory_bm/each.rb
|
87
|
+
- benchmark/memory_bm/each_bang.rb
|
88
|
+
- benchmark/memory_bm/each_slice.rb
|
89
|
+
- benchmark/memory_bm/each_slice_bang.rb
|
90
|
+
- bin/bm
|
91
|
+
- bin/console
|
92
|
+
- bin/setup
|
93
|
+
- hyper_iterator.gemspec
|
94
|
+
- lib/hyper_iterator.rb
|
95
|
+
- lib/hyper_iterator/version.rb
|
96
|
+
- lib/iterators/each_bang.rb
|
97
|
+
- lib/iterators/each_slice_bang.rb
|
98
|
+
homepage: https://github.com/EdmundLeex/hyper_iterator
|
99
|
+
licenses:
|
100
|
+
- MIT
|
101
|
+
metadata: {}
|
102
|
+
post_install_message:
|
103
|
+
rdoc_options: []
|
104
|
+
require_paths:
|
105
|
+
- lib
|
106
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 2.1.2
|
111
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
requirements: []
|
117
|
+
rubyforge_project:
|
118
|
+
rubygems_version: 2.2.2
|
119
|
+
signing_key:
|
120
|
+
specification_version: 4
|
121
|
+
summary: Implementation of Ruby Iterators with High Performance
|
122
|
+
test_files: []
|