parallel 1.19.1 → 1.22.1
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 +25 -23
- data/lib/parallel/version.rb +2 -1
- data/lib/parallel.rb +159 -70
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7efbfe49c3df93ae88464b4bd4ce85d1aaccc9c173e1a81e7cf9100fe182982c
|
4
|
+
data.tar.gz: 7e82ff83bd44c96da760d83ab665baed41bc62d53f010987951c805cf41723e4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a81cae96dd4dcfa1d287ce3e5893c830de3cb8e305236db4170db81f94de021f187da0d21462b7df5e99310002c14984fbb00aef818936c13d0ae834c1c1b2e7
|
7
|
+
data.tar.gz: 79c099a5f1f8bdad3d9f187829213d242087779605f14fb421f0172c1de1a8f853b4f13045935410a989d845d08e736e9552c8e3b9a6c3763d2dfdd27ede7daf
|
@@ -1,39 +1,41 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
3
2
|
module Parallel
|
4
3
|
# TODO: inline this method into parallel.rb and kill physical_processor_count in next major release
|
5
4
|
module ProcessorCount
|
6
5
|
# Number of processors seen by the OS, used for process scheduling
|
7
6
|
def processor_count
|
7
|
+
require 'etc'
|
8
8
|
@processor_count ||= Integer(ENV['PARALLEL_PROCESSOR_COUNT'] || Etc.nprocessors)
|
9
9
|
end
|
10
10
|
|
11
11
|
# Number of physical processor cores on the current system.
|
12
12
|
def physical_processor_count
|
13
13
|
@physical_processor_count ||= begin
|
14
|
-
ppc =
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
14
|
+
ppc =
|
15
|
+
case RbConfig::CONFIG["target_os"]
|
16
|
+
when /darwin[12]/
|
17
|
+
IO.popen("/usr/sbin/sysctl -n hw.physicalcpu").read.to_i
|
18
|
+
when /linux/
|
19
|
+
cores = {} # unique physical ID / core ID combinations
|
20
|
+
phy = 0
|
21
|
+
File.read("/proc/cpuinfo").scan(/^physical id.*|^core id.*/) do |ln|
|
22
|
+
if ln.start_with?("physical")
|
23
|
+
phy = ln[/\d+/]
|
24
|
+
elsif ln.start_with?("core")
|
25
|
+
cid = "#{phy}:#{ln[/\d+/]}"
|
26
|
+
cores[cid] = true unless cores[cid]
|
27
|
+
end
|
26
28
|
end
|
29
|
+
cores.count
|
30
|
+
when /mswin|mingw/
|
31
|
+
require 'win32ole'
|
32
|
+
result_set = WIN32OLE.connect("winmgmts://").ExecQuery(
|
33
|
+
"select NumberOfCores from Win32_Processor"
|
34
|
+
)
|
35
|
+
result_set.to_enum.collect(&:NumberOfCores).reduce(:+)
|
36
|
+
else
|
37
|
+
processor_count
|
27
38
|
end
|
28
|
-
cores.count
|
29
|
-
when /mswin|mingw/
|
30
|
-
require 'win32ole'
|
31
|
-
result_set = WIN32OLE.connect("winmgmts://").ExecQuery(
|
32
|
-
"select NumberOfCores from Win32_Processor")
|
33
|
-
result_set.to_enum.collect(&:NumberOfCores).reduce(:+)
|
34
|
-
else
|
35
|
-
processor_count
|
36
|
-
end
|
37
39
|
# fall back to logical count if physical info is invalid
|
38
40
|
ppc > 0 ? ppc : processor_count
|
39
41
|
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,13 +257,16 @@ 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]
|
254
265
|
method = :in_threads
|
255
266
|
size = options[method]
|
267
|
+
elsif options[:in_ractors]
|
268
|
+
method = :in_ractors
|
269
|
+
size = options[method]
|
256
270
|
else
|
257
271
|
method = :in_processes
|
258
272
|
if Process.respond_to?(:fork)
|
@@ -269,20 +283,24 @@ module Parallel
|
|
269
283
|
options[:return_results] = (options[:preserve_results] != false || !!options[:finish])
|
270
284
|
add_progress_bar!(job_factory, options)
|
271
285
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
286
|
+
result =
|
287
|
+
if size == 0
|
288
|
+
work_direct(job_factory, options, &block)
|
289
|
+
elsif method == :in_threads
|
290
|
+
work_in_threads(job_factory, options.merge(count: size), &block)
|
291
|
+
elsif method == :in_ractors
|
292
|
+
work_in_ractors(job_factory, options.merge(count: size), &block)
|
293
|
+
else
|
294
|
+
work_in_processes(job_factory, options.merge(count: size), &block)
|
295
|
+
end
|
296
|
+
|
297
|
+
return result.value if result.is_a?(Break)
|
298
|
+
raise result if result.is_a?(Exception)
|
299
|
+
options[:return_results] ? result : source
|
282
300
|
end
|
283
301
|
|
284
|
-
def map_with_index(array, options={}, &block)
|
285
|
-
map(array, options.merge(:
|
302
|
+
def map_with_index(array, options = {}, &block)
|
303
|
+
map(array, options.merge(with_index: true), &block)
|
286
304
|
end
|
287
305
|
|
288
306
|
def flat_map(*args, &block)
|
@@ -336,10 +354,10 @@ module Parallel
|
|
336
354
|
call_with_index(item, index, options, &block)
|
337
355
|
end
|
338
356
|
end
|
339
|
-
rescue
|
357
|
+
rescue StandardError
|
340
358
|
exception = $!
|
341
359
|
end
|
342
|
-
|
360
|
+
exception || results
|
343
361
|
ensure
|
344
362
|
self.worker_number = nil
|
345
363
|
end
|
@@ -360,13 +378,79 @@ module Parallel
|
|
360
378
|
call_with_index(item, index, options, &block)
|
361
379
|
end
|
362
380
|
results_mutex.synchronize { results[index] = result }
|
363
|
-
rescue
|
381
|
+
rescue StandardError
|
364
382
|
exception = $!
|
365
383
|
end
|
366
384
|
end
|
367
385
|
end
|
368
386
|
|
369
|
-
|
387
|
+
exception || results
|
388
|
+
end
|
389
|
+
|
390
|
+
def work_in_ractors(job_factory, options)
|
391
|
+
exception = nil
|
392
|
+
results = []
|
393
|
+
results_mutex = Mutex.new # arrays are not thread-safe on jRuby
|
394
|
+
|
395
|
+
callback = options[:ractor]
|
396
|
+
if block_given? || !callback
|
397
|
+
raise ArgumentError, "pass the code you want to execute as `ractor: [ClassName, :method_name]`"
|
398
|
+
end
|
399
|
+
|
400
|
+
# build
|
401
|
+
ractors = Array.new(options.fetch(:count)) do
|
402
|
+
Ractor.new do
|
403
|
+
loop do
|
404
|
+
got = receive
|
405
|
+
(klass, method_name), item, index = got
|
406
|
+
break if index == :break
|
407
|
+
begin
|
408
|
+
Ractor.yield [nil, klass.send(method_name, item), item, index]
|
409
|
+
rescue StandardError => e
|
410
|
+
Ractor.yield [e, nil, item, index]
|
411
|
+
end
|
412
|
+
end
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
# start
|
417
|
+
ractors.dup.each do |ractor|
|
418
|
+
if set = job_factory.next
|
419
|
+
item, index = set
|
420
|
+
instrument_start item, index, options
|
421
|
+
ractor.send [callback, item, index]
|
422
|
+
else
|
423
|
+
ractor.send([[nil, nil], nil, :break]) # stop the ractor
|
424
|
+
ractors.delete ractor
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
# replace with new items
|
429
|
+
while set = job_factory.next
|
430
|
+
item_next, index_next = set
|
431
|
+
done, (exception, result, item, index) = Ractor.select(*ractors)
|
432
|
+
if exception
|
433
|
+
ractors.delete done
|
434
|
+
break
|
435
|
+
end
|
436
|
+
instrument_finish item, index, result, options
|
437
|
+
results_mutex.synchronize { results[index] = (options[:preserve_results] == false ? nil : result) }
|
438
|
+
|
439
|
+
instrument_start item_next, index_next, options
|
440
|
+
done.send([callback, item_next, index_next])
|
441
|
+
end
|
442
|
+
|
443
|
+
# finish
|
444
|
+
ractors.each do |ractor|
|
445
|
+
(new_exception, result, item, index) = ractor.take
|
446
|
+
exception ||= new_exception
|
447
|
+
next if new_exception
|
448
|
+
instrument_finish item, index, result, options
|
449
|
+
results_mutex.synchronize { results[index] = (options[:preserve_results] == false ? nil : result) }
|
450
|
+
ractor.send([[nil, nil], nil, :break]) # stop the ractor
|
451
|
+
end
|
452
|
+
|
453
|
+
exception || results
|
370
454
|
end
|
371
455
|
|
372
456
|
def work_in_processes(job_factory, options, &blk)
|
@@ -400,9 +484,9 @@ module Parallel
|
|
400
484
|
results_mutex.synchronize { results[index] = result } # arrays are not threads safe on jRuby
|
401
485
|
rescue StandardError => e
|
402
486
|
exception = e
|
403
|
-
if
|
487
|
+
if exception.is_a?(Kill)
|
404
488
|
(workers - [worker]).each do |w|
|
405
|
-
w.thread
|
489
|
+
w.thread&.kill
|
406
490
|
UserInterruptHandler.kill(w.pid)
|
407
491
|
end
|
408
492
|
end
|
@@ -414,18 +498,18 @@ module Parallel
|
|
414
498
|
end
|
415
499
|
end
|
416
500
|
|
417
|
-
|
501
|
+
exception || results
|
418
502
|
end
|
419
503
|
|
420
|
-
def replace_worker(job_factory, workers,
|
504
|
+
def replace_worker(job_factory, workers, index, options, blk)
|
421
505
|
options[:mutex].synchronize do
|
422
506
|
# old worker is no longer used ... stop it
|
423
|
-
worker = workers[
|
507
|
+
worker = workers[index]
|
424
508
|
worker.stop
|
425
509
|
|
426
510
|
# create a new replacement worker
|
427
511
|
running = workers - [worker]
|
428
|
-
workers[
|
512
|
+
workers[index] = worker(job_factory, options.merge(started_workers: running, worker_number: index), &blk)
|
429
513
|
end
|
430
514
|
end
|
431
515
|
|
@@ -467,14 +551,17 @@ module Parallel
|
|
467
551
|
until read.eof?
|
468
552
|
data = Marshal.load(read)
|
469
553
|
item, index = job_factory.unpack(data)
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
554
|
+
|
555
|
+
result =
|
556
|
+
begin
|
557
|
+
call_with_index(item, index, options, &block)
|
558
|
+
# https://github.com/rspec/rspec-support/blob/673133cdd13b17077b3d88ece8d7380821f8d7dc/lib/rspec/support.rb#L132-L140
|
559
|
+
rescue NoMemoryError, SignalException, Interrupt, SystemExit # rubocop:disable Lint/ShadowedException
|
560
|
+
raise $!
|
561
|
+
rescue Exception # # rubocop:disable Lint/RescueException
|
562
|
+
ExceptionWrapper.new($!)
|
563
|
+
end
|
564
|
+
|
478
565
|
begin
|
479
566
|
Marshal.dump(result, write)
|
480
567
|
rescue Errno::EPIPE
|
@@ -483,12 +570,6 @@ module Parallel
|
|
483
570
|
end
|
484
571
|
end
|
485
572
|
|
486
|
-
def handle_exception(exception, results)
|
487
|
-
return nil if [Parallel::Break, Parallel::Kill].include? exception.class
|
488
|
-
raise exception if exception
|
489
|
-
results
|
490
|
-
end
|
491
|
-
|
492
573
|
# options is either a Integer or a Hash with :count
|
493
574
|
def extract_count_from_options(options)
|
494
575
|
if options.is_a?(Hash)
|
@@ -503,21 +584,29 @@ module Parallel
|
|
503
584
|
def call_with_index(item, index, options, &block)
|
504
585
|
args = [item]
|
505
586
|
args << index if options[:with_index]
|
587
|
+
results = block.call(*args)
|
506
588
|
if options[:return_results]
|
507
|
-
|
589
|
+
results
|
508
590
|
else
|
509
|
-
block.call(*args)
|
510
591
|
nil # avoid GC overhead of passing large results around
|
511
592
|
end
|
512
593
|
end
|
513
594
|
|
514
595
|
def with_instrumentation(item, index, options)
|
515
|
-
|
516
|
-
on_finish = options[:finish]
|
517
|
-
options[:mutex].synchronize { on_start.call(item, index) } if on_start
|
596
|
+
instrument_start(item, index, options)
|
518
597
|
result = yield
|
519
|
-
|
598
|
+
instrument_finish(item, index, result, options)
|
520
599
|
result unless options[:preserve_results] == false
|
521
600
|
end
|
601
|
+
|
602
|
+
def instrument_finish(item, index, result, options)
|
603
|
+
return unless on_finish = options[:finish]
|
604
|
+
options[:mutex].synchronize { on_finish.call(item, index, result) }
|
605
|
+
end
|
606
|
+
|
607
|
+
def instrument_start(item, index, options)
|
608
|
+
return unless on_start = options[:start]
|
609
|
+
options[:mutex].synchronize { on_start.call(item, index) }
|
610
|
+
end
|
522
611
|
end
|
523
612
|
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.22.1
|
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: 2022-03-25 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email: michael@grosser.it
|
@@ -25,8 +25,8 @@ licenses:
|
|
25
25
|
- MIT
|
26
26
|
metadata:
|
27
27
|
bug_tracker_uri: https://github.com/grosser/parallel/issues
|
28
|
-
documentation_uri: https://github.com/grosser/parallel/blob/v1.
|
29
|
-
source_code_uri: https://github.com/grosser/parallel/tree/v1.
|
28
|
+
documentation_uri: https://github.com/grosser/parallel/blob/v1.22.1/Readme.md
|
29
|
+
source_code_uri: https://github.com/grosser/parallel/tree/v1.22.1
|
30
30
|
wiki_uri: https://github.com/grosser/parallel/wiki
|
31
31
|
post_install_message:
|
32
32
|
rdoc_options: []
|
@@ -36,14 +36,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
37
37
|
- - ">="
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: '2.
|
39
|
+
version: '2.5'
|
40
40
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
41
|
requirements:
|
42
42
|
- - ">="
|
43
43
|
- !ruby/object:Gem::Version
|
44
44
|
version: '0'
|
45
45
|
requirements: []
|
46
|
-
rubygems_version: 3.
|
46
|
+
rubygems_version: 3.1.6
|
47
47
|
signing_key:
|
48
48
|
specification_version: 4
|
49
49
|
summary: Run any kind of code in parallel processes
|