parallel 1.17.0 → 1.21.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.
- checksums.yaml +4 -4
- data/lib/parallel/processor_count.rb +27 -26
- data/lib/parallel/version.rb +2 -1
- data/lib/parallel.rb +76 -64
- metadata +9 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4dd167566b23262ceacbd640fda95c5e8d0d8149cc430f51ada53765a927cb18
|
4
|
+
data.tar.gz: 6a21c00b1109d5c665a471aec4a7af7d213ded2fc9123132b6576ee65ceb1758
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f8a810c3b7fa77e8dd2b2539238b856b9b4ea40b5df49cf2cc4af2afbd2a73d9b6f34baab614c41e7b3b8bfe1917413bfbd014f647c5b4b96e7243d5433523d
|
7
|
+
data.tar.gz: 0bef1c7dc19b4738e228985512ee6cce1915f67ee29161b743821f69366d561ed8c3c5cbd4d9e6829342108fe9f10329563770d1978a660a04c58c0d9e2c8461
|
@@ -1,41 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'etc'
|
2
3
|
|
3
4
|
module Parallel
|
5
|
+
# TODO: inline this method into parallel.rb and kill physical_processor_count in next major release
|
4
6
|
module ProcessorCount
|
5
|
-
# Number of processors seen by the OS
|
7
|
+
# Number of processors seen by the OS, used for process scheduling
|
6
8
|
def processor_count
|
7
|
-
@processor_count ||=
|
8
|
-
Etc.nprocessors
|
9
|
-
end
|
9
|
+
@processor_count ||= Integer(ENV['PARALLEL_PROCESSOR_COUNT'] || Etc.nprocessors)
|
10
10
|
end
|
11
11
|
|
12
12
|
# Number of physical processor cores on the current system.
|
13
|
-
#
|
14
13
|
def physical_processor_count
|
15
14
|
@physical_processor_count ||= begin
|
16
|
-
ppc =
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
15
|
+
ppc =
|
16
|
+
case RbConfig::CONFIG["target_os"]
|
17
|
+
when /darwin[12]/
|
18
|
+
IO.popen("/usr/sbin/sysctl -n hw.physicalcpu").read.to_i
|
19
|
+
when /linux/
|
20
|
+
cores = {} # unique physical ID / core ID combinations
|
21
|
+
phy = 0
|
22
|
+
IO.read("/proc/cpuinfo").scan(/^physical id.*|^core id.*/) do |ln|
|
23
|
+
if ln.start_with?("physical")
|
24
|
+
phy = ln[/\d+/]
|
25
|
+
elsif ln.start_with?("core")
|
26
|
+
cid = "#{phy}:#{ln[/\d+/]}"
|
27
|
+
cores[cid] = true unless cores[cid]
|
28
|
+
end
|
28
29
|
end
|
30
|
+
cores.count
|
31
|
+
when /mswin|mingw/
|
32
|
+
require 'win32ole'
|
33
|
+
result_set = WIN32OLE.connect("winmgmts://").ExecQuery(
|
34
|
+
"select NumberOfCores from Win32_Processor"
|
35
|
+
)
|
36
|
+
result_set.to_enum.collect(&:NumberOfCores).reduce(:+)
|
37
|
+
else
|
38
|
+
processor_count
|
29
39
|
end
|
30
|
-
cores.count
|
31
|
-
when /mswin|mingw/
|
32
|
-
require 'win32ole'
|
33
|
-
result_set = WIN32OLE.connect("winmgmts://").ExecQuery(
|
34
|
-
"select NumberOfCores from Win32_Processor")
|
35
|
-
result_set.to_enum.collect(&:NumberOfCores).reduce(:+)
|
36
|
-
else
|
37
|
-
processor_count
|
38
|
-
end
|
39
40
|
# fall back to logical count if physical info is invalid
|
40
41
|
ppc > 0 ? ppc : processor_count
|
41
42
|
end
|
data/lib/parallel/version.rb
CHANGED
data/lib/parallel.rb
CHANGED
@@ -1,31 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'rbconfig'
|
2
3
|
require 'parallel/version'
|
3
4
|
require 'parallel/processor_count'
|
4
5
|
|
5
6
|
module Parallel
|
6
|
-
extend
|
7
|
+
extend ProcessorCount
|
8
|
+
|
9
|
+
Stop = Object.new.freeze
|
7
10
|
|
8
11
|
class DeadWorker < StandardError
|
9
12
|
end
|
10
13
|
|
11
14
|
class Break < StandardError
|
15
|
+
attr_reader :value
|
16
|
+
|
17
|
+
def initialize(value = nil)
|
18
|
+
super()
|
19
|
+
@value = value
|
20
|
+
end
|
12
21
|
end
|
13
22
|
|
14
|
-
class Kill <
|
23
|
+
class Kill < Break
|
15
24
|
end
|
16
25
|
|
17
26
|
class UndumpableException < StandardError
|
18
27
|
attr_reader :backtrace
|
28
|
+
|
19
29
|
def initialize(original)
|
20
30
|
super "#{original.class}: #{original.message}"
|
21
31
|
@backtrace = original.backtrace
|
22
32
|
end
|
23
33
|
end
|
24
34
|
|
25
|
-
Stop = Object.new.freeze
|
26
|
-
|
27
35
|
class ExceptionWrapper
|
28
36
|
attr_reader :exception
|
37
|
+
|
29
38
|
def initialize(exception)
|
30
39
|
# Remove the bindings stack added by the better_errors gem,
|
31
40
|
# because it cannot be marshalled
|
@@ -36,7 +45,7 @@ module Parallel
|
|
36
45
|
@exception =
|
37
46
|
begin
|
38
47
|
Marshal.dump(exception) && exception
|
39
|
-
rescue
|
48
|
+
rescue StandardError
|
40
49
|
UndumpableException.new(exception)
|
41
50
|
end
|
42
51
|
end
|
@@ -45,8 +54,11 @@ module Parallel
|
|
45
54
|
class Worker
|
46
55
|
attr_reader :pid, :read, :write
|
47
56
|
attr_accessor :thread
|
57
|
+
|
48
58
|
def initialize(read, write, pid)
|
49
|
-
@read
|
59
|
+
@read = read
|
60
|
+
@write = write
|
61
|
+
@pid = pid
|
50
62
|
end
|
51
63
|
|
52
64
|
def stop
|
@@ -73,7 +85,7 @@ module Parallel
|
|
73
85
|
rescue EOFError
|
74
86
|
raise DeadWorker
|
75
87
|
end
|
76
|
-
raise result.exception if ExceptionWrapper
|
88
|
+
raise result.exception if result.is_a?(ExceptionWrapper)
|
77
89
|
result
|
78
90
|
end
|
79
91
|
|
@@ -102,7 +114,7 @@ module Parallel
|
|
102
114
|
item, index = @mutex.synchronize do
|
103
115
|
return if @stopped
|
104
116
|
item = @lambda.call
|
105
|
-
@stopped = (item ==
|
117
|
+
@stopped = (item == Stop)
|
106
118
|
return if @stopped
|
107
119
|
[item, @index += 1]
|
108
120
|
end
|
@@ -140,7 +152,7 @@ module Parallel
|
|
140
152
|
end
|
141
153
|
|
142
154
|
def queue_wrapper(array)
|
143
|
-
array.respond_to?(:num_waiting) && array.respond_to?(:pop) &&
|
155
|
+
array.respond_to?(:num_waiting) && array.respond_to?(:pop) && -> { array.pop(false) }
|
144
156
|
end
|
145
157
|
end
|
146
158
|
|
@@ -156,7 +168,7 @@ module Parallel
|
|
156
168
|
|
157
169
|
if @to_be_killed.empty?
|
158
170
|
old_interrupt = trap_interrupt(signal) do
|
159
|
-
|
171
|
+
warn 'Parallel execution interrupted, exiting ...'
|
160
172
|
@to_be_killed.flatten.each { |pid| kill(pid) }
|
161
173
|
end
|
162
174
|
end
|
@@ -200,45 +212,44 @@ module Parallel
|
|
200
212
|
end
|
201
213
|
|
202
214
|
class << self
|
203
|
-
def in_threads(options={:
|
215
|
+
def in_threads(options = { count: 2 })
|
216
|
+
threads = []
|
217
|
+
count, = extract_count_from_options(options)
|
218
|
+
|
204
219
|
Thread.handle_interrupt(Exception => :never) do
|
205
|
-
|
206
|
-
threads = []
|
207
|
-
count, _ = extract_count_from_options(options)
|
220
|
+
Thread.handle_interrupt(Exception => :immediate) do
|
208
221
|
count.times do |i|
|
209
222
|
threads << Thread.new { yield(i) }
|
210
223
|
end
|
211
|
-
|
212
|
-
threads.map(&:value)
|
213
|
-
end
|
214
|
-
ensure
|
215
|
-
threads.each(&:kill)
|
224
|
+
threads.map(&:value)
|
216
225
|
end
|
226
|
+
ensure
|
227
|
+
threads.each(&:kill)
|
217
228
|
end
|
218
229
|
end
|
219
230
|
|
220
231
|
def in_processes(options = {}, &block)
|
221
232
|
count, options = extract_count_from_options(options)
|
222
233
|
count ||= processor_count
|
223
|
-
map(0...count, options.merge(:
|
234
|
+
map(0...count, options.merge(in_processes: count), &block)
|
224
235
|
end
|
225
236
|
|
226
|
-
def each(array, options={}, &block)
|
227
|
-
map(array, options.merge(:
|
237
|
+
def each(array, options = {}, &block)
|
238
|
+
map(array, options.merge(preserve_results: false), &block)
|
228
239
|
end
|
229
240
|
|
230
241
|
def any?(*args, &block)
|
231
242
|
raise "You must provide a block when calling #any?" if block.nil?
|
232
|
-
!each(*args) { |*a| raise
|
243
|
+
!each(*args) { |*a| raise Kill if block.call(*a) }
|
233
244
|
end
|
234
245
|
|
235
246
|
def all?(*args, &block)
|
236
247
|
raise "You must provide a block when calling #all?" if block.nil?
|
237
|
-
!!each(*args) { |*a| raise
|
248
|
+
!!each(*args) { |*a| raise Kill unless block.call(*a) }
|
238
249
|
end
|
239
250
|
|
240
|
-
def each_with_index(array, options={}, &block)
|
241
|
-
each(array, options.merge(:
|
251
|
+
def each_with_index(array, options = {}, &block)
|
252
|
+
each(array, options.merge(with_index: true), &block)
|
242
253
|
end
|
243
254
|
|
244
255
|
def map(source, options = {}, &block)
|
@@ -246,8 +257,8 @@ module Parallel
|
|
246
257
|
options[:mutex] = Mutex.new
|
247
258
|
|
248
259
|
if options[:in_processes] && options[:in_threads]
|
249
|
-
raise ArgumentError
|
250
|
-
elsif RUBY_PLATFORM =~ /java/
|
260
|
+
raise ArgumentError, "Please specify only one of `in_processes` or `in_threads`."
|
261
|
+
elsif RUBY_PLATFORM =~ (/java/) && !(options[:in_processes])
|
251
262
|
method = :in_threads
|
252
263
|
size = options[method] || processor_count
|
253
264
|
elsif options[:in_threads]
|
@@ -269,20 +280,22 @@ module Parallel
|
|
269
280
|
options[:return_results] = (options[:preserve_results] != false || !!options[:finish])
|
270
281
|
add_progress_bar!(job_factory, options)
|
271
282
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
283
|
+
result =
|
284
|
+
if size == 0
|
285
|
+
work_direct(job_factory, options, &block)
|
286
|
+
elsif method == :in_threads
|
287
|
+
work_in_threads(job_factory, options.merge(count: size), &block)
|
288
|
+
else
|
289
|
+
work_in_processes(job_factory, options.merge(count: size), &block)
|
290
|
+
end
|
291
|
+
|
292
|
+
return result.value if result.is_a?(Break)
|
293
|
+
raise result if result.is_a?(Exception)
|
294
|
+
options[:return_results] ? result : source
|
282
295
|
end
|
283
296
|
|
284
|
-
def map_with_index(array, options={}, &block)
|
285
|
-
map(array, options.merge(:
|
297
|
+
def map_with_index(array, options = {}, &block)
|
298
|
+
map(array, options.merge(with_index: true), &block)
|
286
299
|
end
|
287
300
|
|
288
301
|
def flat_map(*args, &block)
|
@@ -336,10 +349,10 @@ module Parallel
|
|
336
349
|
call_with_index(item, index, options, &block)
|
337
350
|
end
|
338
351
|
end
|
339
|
-
rescue
|
352
|
+
rescue StandardError
|
340
353
|
exception = $!
|
341
354
|
end
|
342
|
-
|
355
|
+
exception || results
|
343
356
|
ensure
|
344
357
|
self.worker_number = nil
|
345
358
|
end
|
@@ -360,13 +373,13 @@ module Parallel
|
|
360
373
|
call_with_index(item, index, options, &block)
|
361
374
|
end
|
362
375
|
results_mutex.synchronize { results[index] = result }
|
363
|
-
rescue
|
376
|
+
rescue StandardError
|
364
377
|
exception = $!
|
365
378
|
end
|
366
379
|
end
|
367
380
|
end
|
368
381
|
|
369
|
-
|
382
|
+
exception || results
|
370
383
|
end
|
371
384
|
|
372
385
|
def work_in_processes(job_factory, options, &blk)
|
@@ -400,9 +413,9 @@ module Parallel
|
|
400
413
|
results_mutex.synchronize { results[index] = result } # arrays are not threads safe on jRuby
|
401
414
|
rescue StandardError => e
|
402
415
|
exception = e
|
403
|
-
if
|
416
|
+
if exception.is_a?(Kill)
|
404
417
|
(workers - [worker]).each do |w|
|
405
|
-
w.thread
|
418
|
+
w.thread&.kill
|
406
419
|
UserInterruptHandler.kill(w.pid)
|
407
420
|
end
|
408
421
|
end
|
@@ -413,19 +426,18 @@ module Parallel
|
|
413
426
|
end
|
414
427
|
end
|
415
428
|
end
|
416
|
-
|
417
|
-
handle_exception(exception, results)
|
429
|
+
exception || results
|
418
430
|
end
|
419
431
|
|
420
|
-
def replace_worker(job_factory, workers,
|
432
|
+
def replace_worker(job_factory, workers, index, options, blk)
|
421
433
|
options[:mutex].synchronize do
|
422
434
|
# old worker is no longer used ... stop it
|
423
|
-
worker = workers[
|
435
|
+
worker = workers[index]
|
424
436
|
worker.stop
|
425
437
|
|
426
438
|
# create a new replacement worker
|
427
439
|
running = workers - [worker]
|
428
|
-
workers[
|
440
|
+
workers[index] = worker(job_factory, options.merge(started_workers: running, worker_number: index), &blk)
|
429
441
|
end
|
430
442
|
end
|
431
443
|
|
@@ -467,11 +479,17 @@ module Parallel
|
|
467
479
|
until read.eof?
|
468
480
|
data = Marshal.load(read)
|
469
481
|
item, index = job_factory.unpack(data)
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
482
|
+
|
483
|
+
result =
|
484
|
+
begin
|
485
|
+
call_with_index(item, index, options, &block)
|
486
|
+
# https://github.com/rspec/rspec-support/blob/673133cdd13b17077b3d88ece8d7380821f8d7dc/lib/rspec/support.rb#L132-L140
|
487
|
+
rescue NoMemoryError, SignalException, Interrupt, SystemExit # rubocop:disable Lint/ShadowedException
|
488
|
+
raise $!
|
489
|
+
rescue Exception # # rubocop:disable Lint/RescueException
|
490
|
+
ExceptionWrapper.new($!)
|
491
|
+
end
|
492
|
+
|
475
493
|
begin
|
476
494
|
Marshal.dump(result, write)
|
477
495
|
rescue Errno::EPIPE
|
@@ -480,12 +498,6 @@ module Parallel
|
|
480
498
|
end
|
481
499
|
end
|
482
500
|
|
483
|
-
def handle_exception(exception, results)
|
484
|
-
return nil if [Parallel::Break, Parallel::Kill].include? exception.class
|
485
|
-
raise exception if exception
|
486
|
-
results
|
487
|
-
end
|
488
|
-
|
489
501
|
# options is either a Integer or a Hash with :count
|
490
502
|
def extract_count_from_options(options)
|
491
503
|
if options.is_a?(Hash)
|
@@ -500,10 +512,10 @@ module Parallel
|
|
500
512
|
def call_with_index(item, index, options, &block)
|
501
513
|
args = [item]
|
502
514
|
args << index if options[:with_index]
|
515
|
+
results = block.call(*args)
|
503
516
|
if options[:return_results]
|
504
|
-
|
517
|
+
results
|
505
518
|
else
|
506
|
-
block.call(*args)
|
507
519
|
nil # avoid GC overhead of passing large results around
|
508
520
|
end
|
509
521
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: parallel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.21.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Grosser
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-09-13 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email: michael@grosser.it
|
@@ -23,7 +23,11 @@ files:
|
|
23
23
|
homepage: https://github.com/grosser/parallel
|
24
24
|
licenses:
|
25
25
|
- MIT
|
26
|
-
metadata:
|
26
|
+
metadata:
|
27
|
+
bug_tracker_uri: https://github.com/grosser/parallel/issues
|
28
|
+
documentation_uri: https://github.com/grosser/parallel/blob/v1.21.0/Readme.md
|
29
|
+
source_code_uri: https://github.com/grosser/parallel/tree/v1.21.0
|
30
|
+
wiki_uri: https://github.com/grosser/parallel/wiki
|
27
31
|
post_install_message:
|
28
32
|
rdoc_options: []
|
29
33
|
require_paths:
|
@@ -32,15 +36,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
32
36
|
requirements:
|
33
37
|
- - ">="
|
34
38
|
- !ruby/object:Gem::Version
|
35
|
-
version: '2.
|
39
|
+
version: '2.5'
|
36
40
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
37
41
|
requirements:
|
38
42
|
- - ">="
|
39
43
|
- !ruby/object:Gem::Version
|
40
44
|
version: '0'
|
41
45
|
requirements: []
|
42
|
-
|
43
|
-
rubygems_version: 2.7.6
|
46
|
+
rubygems_version: 3.2.16
|
44
47
|
signing_key:
|
45
48
|
specification_version: 4
|
46
49
|
summary: Run any kind of code in parallel processes
|