parallel 1.22.1 → 1.27.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7efbfe49c3df93ae88464b4bd4ce85d1aaccc9c173e1a81e7cf9100fe182982c
4
- data.tar.gz: 7e82ff83bd44c96da760d83ab665baed41bc62d53f010987951c805cf41723e4
3
+ metadata.gz: a657608c1d396b6c563f8905778d64d24c4ae6bb010e716e60a45ccc4a71295e
4
+ data.tar.gz: eb26d89f92521fed38f1bdf99f6374a2cca2e3f4c1557a7f3f92ed81d396ffb7
5
5
  SHA512:
6
- metadata.gz: a81cae96dd4dcfa1d287ce3e5893c830de3cb8e305236db4170db81f94de021f187da0d21462b7df5e99310002c14984fbb00aef818936c13d0ae834c1c1b2e7
7
- data.tar.gz: 79c099a5f1f8bdad3d9f187829213d242087779605f14fb421f0172c1de1a8f853b4f13045935410a989d845d08e736e9552c8e3b9a6c3763d2dfdd27ede7daf
6
+ metadata.gz: d7accf7f9b3d74e1e76f7b89db8f9040d23876e8373a6d6fa9b31e927b9ca4ce8b13330ffd888871795c79d6e3bbe7b17aa787789146ffed67f46c21fa9af3ad
7
+ data.tar.gz: ead77e379ad1c18641ca8d0457214af393beeee60fe4e5558c69d601f6f269628ce2830b6a35a7fe66e461b88fbebad33dd9bb8b767324030294e2a8f7164d8d
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Parallel
3
- VERSION = Version = '1.22.1' # rubocop:disable Naming/ConstantName
3
+ VERSION = Version = '1.27.0' # rubocop:disable Naming/ConstantName
4
4
  end
data/lib/parallel.rb CHANGED
@@ -1,11 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
  require 'rbconfig'
3
3
  require 'parallel/version'
4
- require 'parallel/processor_count'
5
4
 
6
5
  module Parallel
7
- extend ProcessorCount
8
-
9
6
  Stop = Object.new.freeze
10
7
 
11
8
  class DeadWorker < StandardError
@@ -27,7 +24,7 @@ module Parallel
27
24
  attr_reader :backtrace
28
25
 
29
26
  def initialize(original)
30
- super "#{original.class}: #{original.message}"
27
+ super("#{original.class}: #{original.message}")
31
28
  @backtrace = original.backtrace
32
29
  end
33
30
  end
@@ -258,7 +255,7 @@ module Parallel
258
255
 
259
256
  if options[:in_processes] && options[:in_threads]
260
257
  raise ArgumentError, "Please specify only one of `in_processes` or `in_threads`."
261
- elsif RUBY_PLATFORM =~ (/java/) && !(options[:in_processes])
258
+ elsif RUBY_PLATFORM =~ (/java/) && !options[:in_processes]
262
259
  method = :in_threads
263
260
  size = options[method] || processor_count
264
261
  elsif options[:in_threads]
@@ -303,8 +300,47 @@ module Parallel
303
300
  map(array, options.merge(with_index: true), &block)
304
301
  end
305
302
 
306
- def flat_map(*args, &block)
307
- map(*args, &block).flatten(1)
303
+ def flat_map(...)
304
+ map(...).flatten(1)
305
+ end
306
+
307
+ def filter_map(...)
308
+ map(...).compact
309
+ end
310
+
311
+ # Number of physical processor cores on the current system.
312
+ def physical_processor_count
313
+ @physical_processor_count ||= begin
314
+ ppc =
315
+ case RbConfig::CONFIG["target_os"]
316
+ when /darwin[12]/
317
+ IO.popen("/usr/sbin/sysctl -n hw.physicalcpu").read.to_i
318
+ when /linux/
319
+ cores = {} # unique physical ID / core ID combinations
320
+ phy = 0
321
+ File.read("/proc/cpuinfo").scan(/^physical id.*|^core id.*/) do |ln|
322
+ if ln.start_with?("physical")
323
+ phy = ln[/\d+/]
324
+ elsif ln.start_with?("core")
325
+ cid = "#{phy}:#{ln[/\d+/]}"
326
+ cores[cid] = true unless cores[cid]
327
+ end
328
+ end
329
+ cores.count
330
+ when /mswin|mingw/
331
+ physical_processor_count_windows
332
+ else
333
+ processor_count
334
+ end
335
+ # fall back to logical count if physical info is invalid
336
+ ppc > 0 ? ppc : processor_count
337
+ end
338
+ end
339
+
340
+ # Number of processors seen by the OS or value considering CPU quota if the process is inside a cgroup,
341
+ # used for process scheduling
342
+ def processor_count
343
+ @processor_count ||= Integer(ENV['PARALLEL_PROCESSOR_COUNT'] || available_processor_count)
308
344
  end
309
345
 
310
346
  def worker_number
@@ -318,8 +354,35 @@ module Parallel
318
354
 
319
355
  private
320
356
 
357
+ def physical_processor_count_windows
358
+ # Get-CimInstance introduced in PowerShell 3 or earlier: https://learn.microsoft.com/en-us/previous-versions/powershell/module/cimcmdlets/get-ciminstance?view=powershell-3.0
359
+ result = run(
360
+ 'powershell -command "Get-CimInstance -ClassName Win32_Processor -Property NumberOfCores ' \
361
+ '| Select-Object -Property NumberOfCores"'
362
+ )
363
+ if !result || $?.exitstatus != 0
364
+ # fallback to deprecated wmic for older systems
365
+ result = run("wmic cpu get NumberOfCores")
366
+ end
367
+ if !result || $?.exitstatus != 0
368
+ # Bail out if both commands returned something unexpected
369
+ warn "guessing pyhsical processor count"
370
+ processor_count
371
+ else
372
+ # powershell: "\nNumberOfCores\n-------------\n 4\n\n\n"
373
+ # wmic: "NumberOfCores \n\n4 \n\n\n\n"
374
+ result.scan(/\d+/).map(&:to_i).reduce(:+)
375
+ end
376
+ end
377
+
378
+ def run(command)
379
+ IO.popen(command, &:read)
380
+ rescue Errno::ENOENT
381
+ # Ignore
382
+ end
383
+
321
384
  def add_progress_bar!(job_factory, options)
322
- if progress_options = options[:progress]
385
+ if (progress_options = options[:progress])
323
386
  raise "Progressbar can only be used with array like items" if job_factory.size == Float::INFINITY
324
387
  require 'ruby-progressbar'
325
388
 
@@ -348,7 +411,7 @@ module Parallel
348
411
  results = []
349
412
  exception = nil
350
413
  begin
351
- while set = job_factory.next
414
+ while (set = job_factory.next)
352
415
  item, index = set
353
416
  results << with_instrumentation(item, index, options) do
354
417
  call_with_index(item, index, options, &block)
@@ -371,7 +434,7 @@ module Parallel
371
434
  in_threads(options) do |worker_num|
372
435
  self.worker_number = worker_num
373
436
  # as long as there are more jobs, work on one of them
374
- while !exception && set = job_factory.next
437
+ while !exception && (set = job_factory.next)
375
438
  begin
376
439
  item, index = set
377
440
  result = with_instrumentation item, index, options do
@@ -415,7 +478,7 @@ module Parallel
415
478
 
416
479
  # start
417
480
  ractors.dup.each do |ractor|
418
- if set = job_factory.next
481
+ if (set = job_factory.next)
419
482
  item, index = set
420
483
  instrument_start item, index, options
421
484
  ractor.send [callback, item, index]
@@ -426,7 +489,7 @@ module Parallel
426
489
  end
427
490
 
428
491
  # replace with new items
429
- while set = job_factory.next
492
+ while (set = job_factory.next)
430
493
  item_next, index_next = set
431
494
  done, (exception, result, item, index) = Ractor.select(*ractors)
432
495
  if exception
@@ -600,13 +663,46 @@ module Parallel
600
663
  end
601
664
 
602
665
  def instrument_finish(item, index, result, options)
603
- return unless on_finish = options[:finish]
666
+ return unless (on_finish = options[:finish])
667
+ return instrument_finish_in_order(item, index, result, options) if options[:finish_in_order]
604
668
  options[:mutex].synchronize { on_finish.call(item, index, result) }
605
669
  end
606
670
 
671
+ # yield results in the order of the input items
672
+ # needs to use `options` to store state between executions
673
+ # needs to use `done` index since a nil result would also be valid
674
+ def instrument_finish_in_order(item, index, result, options)
675
+ options[:mutex].synchronize do
676
+ # initialize our state
677
+ options[:finish_done] ||= []
678
+ options[:finish_expecting] ||= 0 # we wait for item at index 0
679
+
680
+ # store current result
681
+ options[:finish_done][index] = [item, result]
682
+
683
+ # yield all results that are now in order
684
+ break unless index == options[:finish_expecting]
685
+ index.upto(options[:finish_done].size).each do |i|
686
+ break unless (done = options[:finish_done][i])
687
+ options[:finish_done][i] = nil # allow GC to free this item and result
688
+ options[:finish].call(done[0], i, done[1])
689
+ options[:finish_expecting] += 1
690
+ end
691
+ end
692
+ end
693
+
607
694
  def instrument_start(item, index, options)
608
- return unless on_start = options[:start]
695
+ return unless (on_start = options[:start])
609
696
  options[:mutex].synchronize { on_start.call(item, index) }
610
697
  end
698
+
699
+ def available_processor_count
700
+ gem 'concurrent-ruby', '>= 1.3.4'
701
+ require 'concurrent-ruby'
702
+ Concurrent.available_processor_count.floor
703
+ rescue LoadError
704
+ require 'etc'
705
+ Etc.nprocessors
706
+ end
611
707
  end
612
708
  end
metadata CHANGED
@@ -1,16 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parallel
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.22.1
4
+ version: 1.27.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Grosser
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2022-03-25 00:00:00.000000000 Z
10
+ date: 2025-04-14 00:00:00.000000000 Z
12
11
  dependencies: []
13
- description:
14
12
  email: michael@grosser.it
15
13
  executables: []
16
14
  extensions: []
@@ -18,17 +16,15 @@ extra_rdoc_files: []
18
16
  files:
19
17
  - MIT-LICENSE.txt
20
18
  - lib/parallel.rb
21
- - lib/parallel/processor_count.rb
22
19
  - lib/parallel/version.rb
23
20
  homepage: https://github.com/grosser/parallel
24
21
  licenses:
25
22
  - MIT
26
23
  metadata:
27
24
  bug_tracker_uri: https://github.com/grosser/parallel/issues
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
25
+ documentation_uri: https://github.com/grosser/parallel/blob/v1.27.0/Readme.md
26
+ source_code_uri: https://github.com/grosser/parallel/tree/v1.27.0
30
27
  wiki_uri: https://github.com/grosser/parallel/wiki
31
- post_install_message:
32
28
  rdoc_options: []
33
29
  require_paths:
34
30
  - lib
@@ -36,15 +32,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
36
32
  requirements:
37
33
  - - ">="
38
34
  - !ruby/object:Gem::Version
39
- version: '2.5'
35
+ version: '2.7'
40
36
  required_rubygems_version: !ruby/object:Gem::Requirement
41
37
  requirements:
42
38
  - - ">="
43
39
  - !ruby/object:Gem::Version
44
40
  version: '0'
45
41
  requirements: []
46
- rubygems_version: 3.1.6
47
- signing_key:
42
+ rubygems_version: 3.6.2
48
43
  specification_version: 4
49
44
  summary: Run any kind of code in parallel processes
50
45
  test_files: []
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
- module Parallel
3
- # TODO: inline this method into parallel.rb and kill physical_processor_count in next major release
4
- module ProcessorCount
5
- # Number of processors seen by the OS, used for process scheduling
6
- def processor_count
7
- require 'etc'
8
- @processor_count ||= Integer(ENV['PARALLEL_PROCESSOR_COUNT'] || Etc.nprocessors)
9
- end
10
-
11
- # Number of physical processor cores on the current system.
12
- def physical_processor_count
13
- @physical_processor_count ||= begin
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
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
38
- end
39
- # fall back to logical count if physical info is invalid
40
- ppc > 0 ? ppc : processor_count
41
- end
42
- end
43
- end
44
- end