celluloid-pmap 0.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/.gitignore +17 -0
- data/.rspec +2 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +103 -0
- data/Rakefile +6 -0
- data/celluloid-pmap.gemspec +25 -0
- data/lib/celluloid/pmap.rb +24 -0
- data/lib/celluloid/pmap/parallel_map_worker.rb +12 -0
- data/lib/celluloid/pmap/version.rb +5 -0
- data/spec/celluloid/pmap/parallel_map_worker_spec.rb +14 -0
- data/spec/celluloid/pmap_spec.rb +47 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/support/benchmark_spec.rb +26 -0
- metadata +120 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Jesse Wolgamott
|
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,103 @@
|
|
1
|
+
# Celluloid::Pmap
|
2
|
+
|
3
|
+
[](https://travis-ci.org/jwo/celluloid-pmap) [](https://codeclimate.com/github/jwo/celluloid-pmap)
|
4
|
+
|
5
|
+
Parallel Mapping using Celluloid
|
6
|
+
|
7
|
+
Celluloid Futures are wicked sweet, and when combined with a `pmap`
|
8
|
+
implementation AND a supervisor to keep the max threads down, you can be wicked
|
9
|
+
sweet too!
|
10
|
+
|
11
|
+
### How does this happen?
|
12
|
+

|
13
|
+
|
14
|
+
We use Celluloid Futures and Celluloid pool to execute blocks in parallel.
|
15
|
+
|
16
|
+
The pmap will return an array of values when all of the Futures have completed and return values (or return nil).
|
17
|
+
|
18
|
+
The pool can help to make sure you don't exceed your connection resources. A common use case for this is in Rails, you can easily exceed the default ActiveRecord connection size.
|
19
|
+
|
20
|
+
### Inspiration for this code
|
21
|
+
|
22
|
+
Tony Arcieri created [celluloid](http://celluloid.io/), and the [simple_pmap example](https://github.com/celluloid/celluloid/blob/master/examples/simple_pmap.rb) from which this codebase started
|
23
|
+
|
24
|
+
### Is this production ready?
|
25
|
+
|
26
|
+
I've used this implementation in several production systems over the last year. All complexity is with Celluloid (not 1.0 yet, but in my experience has been highly stable.)
|
27
|
+
|
28
|
+
### Why is this a gem?
|
29
|
+
|
30
|
+
Because I've been implementing the same initializer code in every project I've worked on for the last 6 months. It was time to take a stand, man.
|
31
|
+
|
32
|
+
### What rubies will this run on?
|
33
|
+
|
34
|
+
* 1.9.3
|
35
|
+
* ruby-head (2.0)
|
36
|
+
* jruby-19mode
|
37
|
+
* jruby-head
|
38
|
+
* rbx-19mode
|
39
|
+
|
40
|
+
|
41
|
+
## Installation
|
42
|
+
|
43
|
+
Add this line to your application's Gemfile:
|
44
|
+
|
45
|
+
gem 'celluloid-pmap'
|
46
|
+
|
47
|
+
## Usage
|
48
|
+
|
49
|
+
Default usage will execute in parallel. Simply pass a block to an Enumerable
|
50
|
+
(like an Array)
|
51
|
+
|
52
|
+
```
|
53
|
+
puts "You'll see the puts happen instantly, and the sleep in parallel"
|
54
|
+
|
55
|
+
[55,65,75,85].pmap{|speed_limit| puts "I can't drive _#{speed_limit}_";
|
56
|
+
sleep(rand)}
|
57
|
+
|
58
|
+
=> You'll see the puts happen instantly, and the sleep in parallel
|
59
|
+
"I can't drive _55_ ""I can't drive _65_ "
|
60
|
+
"I can't drive _75_ "
|
61
|
+
"I can't drive _85_ "
|
62
|
+
```
|
63
|
+
|
64
|
+
Problem: When using with ActiveRecord, you can quickly run out of connections.
|
65
|
+
Answer: Specify the max number of threads (actors) to create at once!
|
66
|
+
|
67
|
+
```
|
68
|
+
puts "You should see two distinct groups of timestamps, 3 seconds apart"
|
69
|
+
puts [1,2,3].pmap(2){|speed_limit| puts Time.now.tap { sleep(3) }}
|
70
|
+
|
71
|
+
=> You should see two distinct groups of timestamps, 3 seconds apart
|
72
|
+
2013-01-29 21:15:01 -0600
|
73
|
+
2013-01-29 21:15:01 -0600
|
74
|
+
2013-01-29 21:15:04 -0600
|
75
|
+
```
|
76
|
+
|
77
|
+
We default pmap's threads to the number of Celluloid cores in the system.
|
78
|
+
|
79
|
+
## When will this help?
|
80
|
+
|
81
|
+
* When the blocks are IO bound (like database or web queries)
|
82
|
+
* When you're running JRuby or Rubinius
|
83
|
+
* When you're running C Extensions
|
84
|
+
|
85
|
+
## So what will this not help with?
|
86
|
+
|
87
|
+
* Pure math or ruby computations
|
88
|
+
|
89
|
+
## Image Credit
|
90
|
+
|
91
|
+
Ben Scheirman (@subdigital) originally used the awesome Celluloid He-Man image
|
92
|
+
in a presentation on background workers. "He-Man and the Masters of the
|
93
|
+
Universe," AND "She-Ra: Princess of Power" are copyright Mattel.
|
94
|
+
|
95
|
+
More information on He-Man can be found at the unspeakably wow site: http://castlegrayskull.org
|
96
|
+
|
97
|
+
## Contributing
|
98
|
+
|
99
|
+
1. Fork it
|
100
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
101
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
102
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
103
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'celluloid/pmap/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "celluloid-pmap"
|
8
|
+
gem.version = Celluloid::Pmap::VERSION
|
9
|
+
gem.authors = ["Jesse Wolgamott"]
|
10
|
+
gem.email = ["jesse@comalproductions.com"]
|
11
|
+
gem.description = %q{Easy Parallel Executing using Celluloid}
|
12
|
+
gem.summary = %q{ Celluloid Futures are wicked sweet, and when combined with a #pmap implementation AND a supervisor to keep the max threads down, you can be wicked sweet too!}
|
13
|
+
gem.homepage = "https://github.com/jwo/celluloid-pmap"
|
14
|
+
gem.license = "MIT"
|
15
|
+
|
16
|
+
gem.files = `git ls-files`.split($/)
|
17
|
+
gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
|
+
gem.require_paths = ["lib"]
|
20
|
+
|
21
|
+
gem.add_dependency('celluloid', '~> 0.12')
|
22
|
+
|
23
|
+
gem.add_development_dependency "rake"
|
24
|
+
gem.add_development_dependency "rspec"
|
25
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'celluloid'
|
2
|
+
require 'celluloid/pmap/parallel_map_worker'
|
3
|
+
require "celluloid/pmap/version"
|
4
|
+
|
5
|
+
module Celluloid
|
6
|
+
module Pmap
|
7
|
+
|
8
|
+
def self.included(base)
|
9
|
+
base.class_eval do
|
10
|
+
|
11
|
+
def pmap(size=Celluloid.cores, &block)
|
12
|
+
pool = Pmap::ParallelMapWorker.pool(size: size)
|
13
|
+
futures = map { |elem| pool.future :yielder, elem, &block }
|
14
|
+
futures.map { |future| future.value }
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module Enumerable
|
23
|
+
include Celluloid::Pmap
|
24
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'celluloid/pmap/parallel_map_worker'
|
2
|
+
|
3
|
+
describe Celluloid::Pmap::ParallelMapWorker do
|
4
|
+
|
5
|
+
it "should execute the block we send" do
|
6
|
+
result = subject.yielder { 6 + 3 }
|
7
|
+
result.should eq(9)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should send in the argument to the block" do
|
11
|
+
result = subject.yielder(6) {|e| e + 3 }
|
12
|
+
result.should eq(9)
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Celluloid::Pmap do
|
4
|
+
|
5
|
+
it 'should have a version number' do
|
6
|
+
Celluloid::Pmap::VERSION.should_not be_nil
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should still map correctly' do
|
10
|
+
[4,5,6,7].map{|x| x + 1}.should eq([5,6,7,8])
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should have same results when pmap' do
|
14
|
+
[4,5,6,7].pmap{|x| x + 1}.should eq([5,6,7,8])
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should sleep in sequence for map' do
|
18
|
+
expect {
|
19
|
+
[1,2,3].map{|x| x; sleep(1) }
|
20
|
+
}.to take_approximately(3).seconds
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should sleep in parallel for pmap' do
|
24
|
+
Celluloid.stub(:cores) { 4 }
|
25
|
+
expect {
|
26
|
+
[1,2,3].pmap{|x| x; sleep(1) }
|
27
|
+
}.to take_approximately(1).seconds
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should default to the number of cores on the machine' do
|
31
|
+
Celluloid.stub(:cores) { 4 }
|
32
|
+
expect {
|
33
|
+
[1,2,3,4,5,6].pmap{|x| x; sleep(1) }
|
34
|
+
}.to take_approximately(2).seconds
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'can be set to many threads at once' do
|
38
|
+
expect {
|
39
|
+
[1,2,3,4,5,6].pmap(10) {|x| x; sleep(1) }
|
40
|
+
}.to take_approximately(1).seconds
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should be included in enumerable' do
|
44
|
+
Enumerable.ancestors.should include(Celluloid::Pmap)
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
|
3
|
+
RSpec::Matchers.define :take_approximately do |n|
|
4
|
+
chain :seconds do; end
|
5
|
+
|
6
|
+
match do |block|
|
7
|
+
@elapsed = Benchmark.realtime do
|
8
|
+
block.call
|
9
|
+
end
|
10
|
+
@elapsed.should be_within(0.2).of(n)
|
11
|
+
end
|
12
|
+
|
13
|
+
failure_message_for_should do |actual|
|
14
|
+
"expected block to take about #{expected} seconds, but took #{@elapsed}"
|
15
|
+
end
|
16
|
+
|
17
|
+
failure_message_for_should_not do |actual|
|
18
|
+
"expected block to not take about #{expected} seconds, but took #{@elapsed}"
|
19
|
+
end
|
20
|
+
|
21
|
+
description do
|
22
|
+
"take approximately #{expectely} seconds to execute"
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: celluloid-pmap
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jesse Wolgamott
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-01-30 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: celluloid
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0.12'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0.12'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: Easy Parallel Executing using Celluloid
|
63
|
+
email:
|
64
|
+
- jesse@comalproductions.com
|
65
|
+
executables: []
|
66
|
+
extensions: []
|
67
|
+
extra_rdoc_files: []
|
68
|
+
files:
|
69
|
+
- .gitignore
|
70
|
+
- .rspec
|
71
|
+
- .travis.yml
|
72
|
+
- Gemfile
|
73
|
+
- LICENSE.txt
|
74
|
+
- README.md
|
75
|
+
- Rakefile
|
76
|
+
- celluloid-pmap.gemspec
|
77
|
+
- lib/celluloid/pmap.rb
|
78
|
+
- lib/celluloid/pmap/parallel_map_worker.rb
|
79
|
+
- lib/celluloid/pmap/version.rb
|
80
|
+
- spec/celluloid/pmap/parallel_map_worker_spec.rb
|
81
|
+
- spec/celluloid/pmap_spec.rb
|
82
|
+
- spec/spec_helper.rb
|
83
|
+
- spec/support/benchmark_spec.rb
|
84
|
+
homepage: https://github.com/jwo/celluloid-pmap
|
85
|
+
licenses:
|
86
|
+
- MIT
|
87
|
+
post_install_message:
|
88
|
+
rdoc_options: []
|
89
|
+
require_paths:
|
90
|
+
- lib
|
91
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
92
|
+
none: false
|
93
|
+
requirements:
|
94
|
+
- - ! '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
segments:
|
98
|
+
- 0
|
99
|
+
hash: 1634678292580827716
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ! '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
segments:
|
107
|
+
- 0
|
108
|
+
hash: 1634678292580827716
|
109
|
+
requirements: []
|
110
|
+
rubyforge_project:
|
111
|
+
rubygems_version: 1.8.24
|
112
|
+
signing_key:
|
113
|
+
specification_version: 3
|
114
|
+
summary: ! 'Celluloid Futures are wicked sweet, and when combined with a #pmap implementation
|
115
|
+
AND a supervisor to keep the max threads down, you can be wicked sweet too!'
|
116
|
+
test_files:
|
117
|
+
- spec/celluloid/pmap/parallel_map_worker_spec.rb
|
118
|
+
- spec/celluloid/pmap_spec.rb
|
119
|
+
- spec/spec_helper.rb
|
120
|
+
- spec/support/benchmark_spec.rb
|