celluloid-pmap 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
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
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ script: rake
2
+ rvm:
3
+ - 1.9.3
4
+ - ruby-head
5
+ - jruby-19mode
6
+ - jruby-head
7
+ - rbx-19mode
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in celluloid-pmap.gemspec
4
+ gemspec
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
+ [![Build Status](https://travis-ci.org/jwo/celluloid-pmap.png?branch=master)](https://travis-ci.org/jwo/celluloid-pmap) [![Code Climate](https://codeclimate.com/badge.png)](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
+ ![celluloid](https://f.cloud.github.com/assets/123075/109654/7584c1fa-6a8c-11e2-9ad6-114818b7fbe4.png)
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,6 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new('spec')
5
+
6
+ task :default => 'spec'
@@ -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,12 @@
1
+ require 'celluloid'
2
+ module Celluloid::Pmap
3
+
4
+ class ParallelMapWorker
5
+ include Celluloid
6
+
7
+ def yielder(element=nil, &block)
8
+ yield element
9
+ end
10
+ end
11
+
12
+ end
@@ -0,0 +1,5 @@
1
+ module Celluloid
2
+ module Pmap
3
+ VERSION = "0.1.0"
4
+ end
5
+ 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
@@ -0,0 +1,3 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'celluloid/pmap'
3
+ require 'support/benchmark_spec'
@@ -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