parallel 0.3.7 → 0.4.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/VERSION +1 -1
- data/lib/parallel.rb +68 -32
- data/parallel.gemspec +4 -2
- data/spec/cases/no_dump_with_each.rb +16 -0
- data/spec/parallel_spec.rb +4 -0
- metadata +4 -2
    
        data/VERSION
    CHANGED
    
    | @@ -1 +1 @@ | |
| 1 | 
            -
            0. | 
| 1 | 
            +
            0.4.0
         | 
    
        data/lib/parallel.rb
    CHANGED
    
    | @@ -3,7 +3,9 @@ require 'thread' # to get Thread.exclusive | |
| 3 3 | 
             
            class Parallel
         | 
| 4 4 | 
             
              VERSION = File.read( File.join(File.dirname(__FILE__),'..','VERSION') ).strip
         | 
| 5 5 |  | 
| 6 | 
            -
              def self.in_threads(count  | 
| 6 | 
            +
              def self.in_threads(options={:count => 2})
         | 
| 7 | 
            +
                count, options = extract_count_from_options(options)
         | 
| 8 | 
            +
             | 
| 7 9 | 
             
                out = []
         | 
| 8 10 | 
             
                threads = []
         | 
| 9 11 |  | 
| @@ -17,40 +19,18 @@ class Parallel | |
| 17 19 | 
             
                out
         | 
| 18 20 | 
             
              end
         | 
| 19 21 |  | 
| 20 | 
            -
              def self.in_processes( | 
| 22 | 
            +
              def self.in_processes(options = {}, &block)
         | 
| 23 | 
            +
                count, options = extract_count_from_options(options)
         | 
| 21 24 | 
             
                count ||= processor_count
         | 
| 25 | 
            +
                preserve_results = (options[:preserve_results] != false)
         | 
| 22 26 |  | 
| 23 | 
            -
                 | 
| 24 | 
            -
                 | 
| 25 | 
            -
                 | 
| 26 | 
            -
                pids = []
         | 
| 27 | 
            -
                count.times do |i|
         | 
| 28 | 
            -
                  reads[i], writes[i] = IO.pipe
         | 
| 29 | 
            -
                  # activate copy on write friendly GC of REE
         | 
| 30 | 
            -
                  GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly=)
         | 
| 31 | 
            -
                  pids << Process.fork do
         | 
| 32 | 
            -
                    Marshal.dump(yield(i), writes[i]) # Serialize result
         | 
| 33 | 
            -
                  end
         | 
| 34 | 
            -
                end
         | 
| 35 | 
            -
             | 
| 36 | 
            -
                kill_on_ctrl_c(pids)
         | 
| 37 | 
            -
             | 
| 38 | 
            -
                # Collect results from pipes simultanously
         | 
| 39 | 
            -
                # otherwise pipes get stuck when to much is written (buffer full)
         | 
| 40 | 
            -
                out = []
         | 
| 41 | 
            -
                in_threads(count) do |i|
         | 
| 42 | 
            -
                  writes[i].close
         | 
| 43 | 
            -
                  while text = reads[i].gets
         | 
| 44 | 
            -
                    out[i] = out[i].to_s + text
         | 
| 45 | 
            -
                  end
         | 
| 46 | 
            -
                  reads[i].close
         | 
| 47 | 
            -
                end
         | 
| 48 | 
            -
             | 
| 49 | 
            -
                out.map{|x| Marshal.load(x) } # Deserialize results
         | 
| 27 | 
            +
                pipes = fork_and_start_writing(count, :preserve_results => preserve_results, &block)
         | 
| 28 | 
            +
                out = read_from_pipes(pipes)
         | 
| 29 | 
            +
                out.map{|x| deserialize(x) } if preserve_results
         | 
| 50 30 | 
             
              end
         | 
| 51 31 |  | 
| 52 32 | 
             
              def self.each(array, options={}, &block)
         | 
| 53 | 
            -
                map(array, options, &block)
         | 
| 33 | 
            +
                map(array, options.merge(:preserve_results => false), &block)
         | 
| 54 34 | 
             
                array
         | 
| 55 35 | 
             
              end
         | 
| 56 36 |  | 
| @@ -74,7 +54,7 @@ class Parallel | |
| 74 54 | 
             
                  loop do
         | 
| 75 55 | 
             
                    index = Thread.exclusive{ current+=1 }
         | 
| 76 56 | 
             
                    break if index >= array.size
         | 
| 77 | 
            -
                    results[index] = *send(method, 1){ yield array[index] }
         | 
| 57 | 
            +
                    results[index] = *send(method, options.merge(:count => 1)){ yield array[index] }
         | 
| 78 58 | 
             
                  end
         | 
| 79 59 | 
             
                end
         | 
| 80 60 |  | 
| @@ -92,7 +72,63 @@ class Parallel | |
| 92 72 |  | 
| 93 73 | 
             
              private
         | 
| 94 74 |  | 
| 75 | 
            +
              # Collect results from pipes simultanously
         | 
| 76 | 
            +
              # otherwise pipes get stuck when to much is written (buffer full)
         | 
| 77 | 
            +
              def self.read_from_pipes(reads)
         | 
| 78 | 
            +
                out = []
         | 
| 79 | 
            +
                in_threads(reads.size) do |i|
         | 
| 80 | 
            +
                  out[i] = ''
         | 
| 81 | 
            +
                  while text = reads[i].gets
         | 
| 82 | 
            +
                    out[i] += text
         | 
| 83 | 
            +
                  end
         | 
| 84 | 
            +
                  reads[i].close
         | 
| 85 | 
            +
                end
         | 
| 86 | 
            +
                out
         | 
| 87 | 
            +
              end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
              # fork and start writing results into n pipes
         | 
| 90 | 
            +
              def self.fork_and_start_writing(count, options, &block)
         | 
| 91 | 
            +
                reads = []
         | 
| 92 | 
            +
                pids = []
         | 
| 93 | 
            +
                count.times do |i|
         | 
| 94 | 
            +
                  reads[i], write = IO.pipe
         | 
| 95 | 
            +
                  pids << do_in_new_process(i, options.merge(:write_to => (options[:preserve_results] ? write : nil)), &block)
         | 
| 96 | 
            +
                  write.close
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
                kill_on_ctrl_c(pids)
         | 
| 99 | 
            +
                reads
         | 
| 100 | 
            +
              end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
              def self.do_in_new_process(work_item, options)
         | 
| 103 | 
            +
                # activate copy on write friendly GC of REE
         | 
| 104 | 
            +
                GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly=)
         | 
| 105 | 
            +
                Process.fork do
         | 
| 106 | 
            +
                  result = yield(work_item)
         | 
| 107 | 
            +
                  serialize(result, options) if options[:write_to]
         | 
| 108 | 
            +
                end
         | 
| 109 | 
            +
              end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
              def self.serialize(something, options)
         | 
| 112 | 
            +
                Marshal.dump(something, options[:write_to])
         | 
| 113 | 
            +
              end
         | 
| 114 | 
            +
             | 
| 115 | 
            +
              def self.deserialize(something)
         | 
| 116 | 
            +
                Marshal.load(something)
         | 
| 117 | 
            +
              end
         | 
| 118 | 
            +
             | 
| 119 | 
            +
              # options is either a Interger or a Hash with :count
         | 
| 120 | 
            +
              def self.extract_count_from_options(options)
         | 
| 121 | 
            +
                if options.is_a?(Hash)
         | 
| 122 | 
            +
                  count = options[:count]
         | 
| 123 | 
            +
                else
         | 
| 124 | 
            +
                  count = options
         | 
| 125 | 
            +
                  options = {}
         | 
| 126 | 
            +
                end
         | 
| 127 | 
            +
                [count, options]
         | 
| 128 | 
            +
              end
         | 
| 129 | 
            +
             | 
| 95 130 | 
             
              # split an array into groups of size items
         | 
| 131 | 
            +
              # (copied from ActiveSupport, to not require it)
         | 
| 96 132 | 
             
              def self.in_groups_of(array, size)
         | 
| 97 133 | 
             
                results = []
         | 
| 98 134 | 
             
                loop do
         | 
| @@ -106,7 +142,7 @@ class Parallel | |
| 106 142 | 
             
                results
         | 
| 107 143 | 
             
              end
         | 
| 108 144 |  | 
| 109 | 
            -
              # | 
| 145 | 
            +
              # kill all these processes (children) if user presses Ctrl+c
         | 
| 110 146 | 
             
              def self.kill_on_ctrl_c(pids)
         | 
| 111 147 | 
             
                Signal.trap :SIGINT do
         | 
| 112 148 | 
             
                  $stderr.puts 'Parallel execution interrupted, exiting ...'
         | 
    
        data/parallel.gemspec
    CHANGED
    
    | @@ -5,11 +5,11 @@ | |
| 5 5 |  | 
| 6 6 | 
             
            Gem::Specification.new do |s|
         | 
| 7 7 | 
             
              s.name = %q{parallel}
         | 
| 8 | 
            -
              s.version = "0. | 
| 8 | 
            +
              s.version = "0.4.0"
         | 
| 9 9 |  | 
| 10 10 | 
             
              s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
         | 
| 11 11 | 
             
              s.authors = ["Michael Grosser"]
         | 
| 12 | 
            -
              s.date = %q{ | 
| 12 | 
            +
              s.date = %q{2010-01-17}
         | 
| 13 13 | 
             
              s.email = %q{grosser.michael@gmail.com}
         | 
| 14 14 | 
             
              s.extra_rdoc_files = [
         | 
| 15 15 | 
             
                "README.markdown"
         | 
| @@ -20,6 +20,7 @@ Gem::Specification.new do |s| | |
| 20 20 | 
             
                 "VERSION",
         | 
| 21 21 | 
             
                 "lib/parallel.rb",
         | 
| 22 22 | 
             
                 "parallel.gemspec",
         | 
| 23 | 
            +
                 "spec/cases/no_dump_with_each.rb",
         | 
| 23 24 | 
             
                 "spec/cases/parallel_each.rb",
         | 
| 24 25 | 
             
                 "spec/cases/parallel_high_fork_rate.rb",
         | 
| 25 26 | 
             
                 "spec/cases/parallel_influence_outside_data.rb",
         | 
| @@ -47,6 +48,7 @@ Gem::Specification.new do |s| | |
| 47 48 | 
             
                 "spec/cases/parallel_map_uneven.rb",
         | 
| 48 49 | 
             
                 "spec/cases/parallel_map_range.rb",
         | 
| 49 50 | 
             
                 "spec/cases/parallel_with_set_processes.rb",
         | 
| 51 | 
            +
                 "spec/cases/no_dump_with_each.rb",
         | 
| 50 52 | 
             
                 "spec/cases/parallel_start_and_kill.rb",
         | 
| 51 53 | 
             
                 "spec/cases/parallel_raise.rb",
         | 
| 52 54 | 
             
                 "spec/cases/parallel_sleeping_2.rb",
         | 
    
        data/spec/parallel_spec.rb
    CHANGED
    
    | @@ -106,6 +106,10 @@ describe Parallel do | |
| 106 106 | 
             
                it "returns original array, works like map" do
         | 
| 107 107 | 
             
                  `ruby spec/cases/parallel_each.rb`.should == '-b--c--d--a-a b c d'
         | 
| 108 108 | 
             
                end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                it "does not use marshal_dump" do
         | 
| 111 | 
            +
                  `ruby spec/cases/no_dump_with_each.rb 2>&1`.should == 'not dumpable'
         | 
| 112 | 
            +
                end
         | 
| 109 113 | 
             
              end
         | 
| 110 114 |  | 
| 111 115 | 
             
              describe :in_groups_of do
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification 
         | 
| 2 2 | 
             
            name: parallel
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version 
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.4.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors: 
         | 
| 7 7 | 
             
            - Michael Grosser
         | 
| @@ -9,7 +9,7 @@ autorequire: | |
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 11 |  | 
| 12 | 
            -
            date:  | 
| 12 | 
            +
            date: 2010-01-17 00:00:00 +01:00
         | 
| 13 13 | 
             
            default_executable: 
         | 
| 14 14 | 
             
            dependencies: []
         | 
| 15 15 |  | 
| @@ -27,6 +27,7 @@ files: | |
| 27 27 | 
             
            - VERSION
         | 
| 28 28 | 
             
            - lib/parallel.rb
         | 
| 29 29 | 
             
            - parallel.gemspec
         | 
| 30 | 
            +
            - spec/cases/no_dump_with_each.rb
         | 
| 30 31 | 
             
            - spec/cases/parallel_each.rb
         | 
| 31 32 | 
             
            - spec/cases/parallel_high_fork_rate.rb
         | 
| 32 33 | 
             
            - spec/cases/parallel_influence_outside_data.rb
         | 
| @@ -76,6 +77,7 @@ test_files: | |
| 76 77 | 
             
            - spec/cases/parallel_map_uneven.rb
         | 
| 77 78 | 
             
            - spec/cases/parallel_map_range.rb
         | 
| 78 79 | 
             
            - spec/cases/parallel_with_set_processes.rb
         | 
| 80 | 
            +
            - spec/cases/no_dump_with_each.rb
         | 
| 79 81 | 
             
            - spec/cases/parallel_start_and_kill.rb
         | 
| 80 82 | 
             
            - spec/cases/parallel_raise.rb
         | 
| 81 83 | 
             
            - spec/cases/parallel_sleeping_2.rb
         |