parallel 1.20.1 → 1.24.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/version.rb +2 -1
- data/lib/parallel.rb +203 -50
- metadata +6 -7
- data/lib/parallel/processor_count.rb +0 -42
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f7b4a5662ef68724c75c2d655d9bd09ad109edc02d3199a0744093544608f74b
|
4
|
+
data.tar.gz: 247f7f3745c42cdafeb5d403fff250e99b99a56d4dc4948050dcf20ecb60d183
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 18f62ea9fefb30bda9b89f0189074da99572c5c17fb93ba28e553b0eb9f6d7a61bd70ba423004b138b05c3c86f8e04fd32fce81da40a68c0bdca39e263b8b892
|
7
|
+
data.tar.gz: dca61c9e3cab22de0ad064c96c55afc88177214108d6bdbfb8e9a93343f23d6ee321c83aa6c4cfa9ee33da79d01802d7b952f3dd0360f9cca26e5da78a4117fa
|
data/lib/parallel/version.rb
CHANGED
data/lib/parallel.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'rbconfig'
|
2
3
|
require 'parallel/version'
|
3
|
-
require 'parallel/processor_count'
|
4
4
|
|
5
5
|
module Parallel
|
6
|
-
extend ProcessorCount
|
7
|
-
|
8
6
|
Stop = Object.new.freeze
|
9
7
|
|
10
8
|
class DeadWorker < StandardError
|
@@ -12,7 +10,9 @@ module Parallel
|
|
12
10
|
|
13
11
|
class Break < StandardError
|
14
12
|
attr_reader :value
|
13
|
+
|
15
14
|
def initialize(value = nil)
|
15
|
+
super()
|
16
16
|
@value = value
|
17
17
|
end
|
18
18
|
end
|
@@ -22,6 +22,7 @@ module Parallel
|
|
22
22
|
|
23
23
|
class UndumpableException < StandardError
|
24
24
|
attr_reader :backtrace
|
25
|
+
|
25
26
|
def initialize(original)
|
26
27
|
super "#{original.class}: #{original.message}"
|
27
28
|
@backtrace = original.backtrace
|
@@ -30,6 +31,7 @@ module Parallel
|
|
30
31
|
|
31
32
|
class ExceptionWrapper
|
32
33
|
attr_reader :exception
|
34
|
+
|
33
35
|
def initialize(exception)
|
34
36
|
# Remove the bindings stack added by the better_errors gem,
|
35
37
|
# because it cannot be marshalled
|
@@ -40,7 +42,7 @@ module Parallel
|
|
40
42
|
@exception =
|
41
43
|
begin
|
42
44
|
Marshal.dump(exception) && exception
|
43
|
-
rescue
|
45
|
+
rescue StandardError
|
44
46
|
UndumpableException.new(exception)
|
45
47
|
end
|
46
48
|
end
|
@@ -49,8 +51,11 @@ module Parallel
|
|
49
51
|
class Worker
|
50
52
|
attr_reader :pid, :read, :write
|
51
53
|
attr_accessor :thread
|
54
|
+
|
52
55
|
def initialize(read, write, pid)
|
53
|
-
@read
|
56
|
+
@read = read
|
57
|
+
@write = write
|
58
|
+
@pid = pid
|
54
59
|
end
|
55
60
|
|
56
61
|
def stop
|
@@ -77,7 +82,7 @@ module Parallel
|
|
77
82
|
rescue EOFError
|
78
83
|
raise DeadWorker
|
79
84
|
end
|
80
|
-
raise result.exception if ExceptionWrapper
|
85
|
+
raise result.exception if result.is_a?(ExceptionWrapper)
|
81
86
|
result
|
82
87
|
end
|
83
88
|
|
@@ -144,7 +149,7 @@ module Parallel
|
|
144
149
|
end
|
145
150
|
|
146
151
|
def queue_wrapper(array)
|
147
|
-
array.respond_to?(:num_waiting) && array.respond_to?(:pop) &&
|
152
|
+
array.respond_to?(:num_waiting) && array.respond_to?(:pop) && -> { array.pop(false) }
|
148
153
|
end
|
149
154
|
end
|
150
155
|
|
@@ -160,7 +165,7 @@ module Parallel
|
|
160
165
|
|
161
166
|
if @to_be_killed.empty?
|
162
167
|
old_interrupt = trap_interrupt(signal) do
|
163
|
-
|
168
|
+
warn 'Parallel execution interrupted, exiting ...'
|
164
169
|
@to_be_killed.flatten.each { |pid| kill(pid) }
|
165
170
|
end
|
166
171
|
end
|
@@ -204,32 +209,30 @@ module Parallel
|
|
204
209
|
end
|
205
210
|
|
206
211
|
class << self
|
207
|
-
def in_threads(options={:
|
212
|
+
def in_threads(options = { count: 2 })
|
208
213
|
threads = []
|
209
|
-
count,
|
214
|
+
count, = extract_count_from_options(options)
|
210
215
|
|
211
216
|
Thread.handle_interrupt(Exception => :never) do
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
threads << Thread.new { yield(i) }
|
216
|
-
end
|
217
|
-
threads.map(&:value)
|
217
|
+
Thread.handle_interrupt(Exception => :immediate) do
|
218
|
+
count.times do |i|
|
219
|
+
threads << Thread.new { yield(i) }
|
218
220
|
end
|
219
|
-
|
220
|
-
threads.each(&:kill)
|
221
|
+
threads.map(&:value)
|
221
222
|
end
|
223
|
+
ensure
|
224
|
+
threads.each(&:kill)
|
222
225
|
end
|
223
226
|
end
|
224
227
|
|
225
228
|
def in_processes(options = {}, &block)
|
226
229
|
count, options = extract_count_from_options(options)
|
227
230
|
count ||= processor_count
|
228
|
-
map(0...count, options.merge(:
|
231
|
+
map(0...count, options.merge(in_processes: count), &block)
|
229
232
|
end
|
230
233
|
|
231
|
-
def each(array, options={}, &block)
|
232
|
-
map(array, options.merge(:
|
234
|
+
def each(array, options = {}, &block)
|
235
|
+
map(array, options.merge(preserve_results: false), &block)
|
233
236
|
end
|
234
237
|
|
235
238
|
def any?(*args, &block)
|
@@ -242,8 +245,8 @@ module Parallel
|
|
242
245
|
!!each(*args) { |*a| raise Kill unless block.call(*a) }
|
243
246
|
end
|
244
247
|
|
245
|
-
def each_with_index(array, options={}, &block)
|
246
|
-
each(array, options.merge(:
|
248
|
+
def each_with_index(array, options = {}, &block)
|
249
|
+
each(array, options.merge(with_index: true), &block)
|
247
250
|
end
|
248
251
|
|
249
252
|
def map(source, options = {}, &block)
|
@@ -251,13 +254,16 @@ module Parallel
|
|
251
254
|
options[:mutex] = Mutex.new
|
252
255
|
|
253
256
|
if options[:in_processes] && options[:in_threads]
|
254
|
-
raise ArgumentError
|
255
|
-
elsif RUBY_PLATFORM =~ /java/
|
257
|
+
raise ArgumentError, "Please specify only one of `in_processes` or `in_threads`."
|
258
|
+
elsif RUBY_PLATFORM =~ (/java/) && !(options[:in_processes])
|
256
259
|
method = :in_threads
|
257
260
|
size = options[method] || processor_count
|
258
261
|
elsif options[:in_threads]
|
259
262
|
method = :in_threads
|
260
263
|
size = options[method]
|
264
|
+
elsif options[:in_ractors]
|
265
|
+
method = :in_ractors
|
266
|
+
size = options[method]
|
261
267
|
else
|
262
268
|
method = :in_processes
|
263
269
|
if Process.respond_to?(:fork)
|
@@ -278,9 +284,11 @@ module Parallel
|
|
278
284
|
if size == 0
|
279
285
|
work_direct(job_factory, options, &block)
|
280
286
|
elsif method == :in_threads
|
281
|
-
work_in_threads(job_factory, options.merge(:
|
287
|
+
work_in_threads(job_factory, options.merge(count: size), &block)
|
288
|
+
elsif method == :in_ractors
|
289
|
+
work_in_ractors(job_factory, options.merge(count: size), &block)
|
282
290
|
else
|
283
|
-
work_in_processes(job_factory, options.merge(:
|
291
|
+
work_in_processes(job_factory, options.merge(count: size), &block)
|
284
292
|
end
|
285
293
|
|
286
294
|
return result.value if result.is_a?(Break)
|
@@ -288,14 +296,57 @@ module Parallel
|
|
288
296
|
options[:return_results] ? result : source
|
289
297
|
end
|
290
298
|
|
291
|
-
def map_with_index(array, options={}, &block)
|
292
|
-
map(array, options.merge(:
|
299
|
+
def map_with_index(array, options = {}, &block)
|
300
|
+
map(array, options.merge(with_index: true), &block)
|
293
301
|
end
|
294
302
|
|
295
303
|
def flat_map(*args, &block)
|
296
304
|
map(*args, &block).flatten(1)
|
297
305
|
end
|
298
306
|
|
307
|
+
def filter_map(*args, &block)
|
308
|
+
map(*args, &block).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
|
+
require 'win32ole'
|
332
|
+
result_set = WIN32OLE.connect("winmgmts://").ExecQuery(
|
333
|
+
"select NumberOfCores from Win32_Processor"
|
334
|
+
)
|
335
|
+
result_set.to_enum.collect(&:NumberOfCores).reduce(:+)
|
336
|
+
else
|
337
|
+
processor_count
|
338
|
+
end
|
339
|
+
# fall back to logical count if physical info is invalid
|
340
|
+
ppc > 0 ? ppc : processor_count
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
# Number of processors seen by the OS, used for process scheduling
|
345
|
+
def processor_count
|
346
|
+
require 'etc'
|
347
|
+
@processor_count ||= Integer(ENV['PARALLEL_PROCESSOR_COUNT'] || Etc.nprocessors)
|
348
|
+
end
|
349
|
+
|
299
350
|
def worker_number
|
300
351
|
Thread.current[:parallel_worker_number]
|
301
352
|
end
|
@@ -343,7 +394,7 @@ module Parallel
|
|
343
394
|
call_with_index(item, index, options, &block)
|
344
395
|
end
|
345
396
|
end
|
346
|
-
rescue
|
397
|
+
rescue StandardError
|
347
398
|
exception = $!
|
348
399
|
end
|
349
400
|
exception || results
|
@@ -367,7 +418,7 @@ module Parallel
|
|
367
418
|
call_with_index(item, index, options, &block)
|
368
419
|
end
|
369
420
|
results_mutex.synchronize { results[index] = result }
|
370
|
-
rescue
|
421
|
+
rescue StandardError
|
371
422
|
exception = $!
|
372
423
|
end
|
373
424
|
end
|
@@ -376,6 +427,72 @@ module Parallel
|
|
376
427
|
exception || results
|
377
428
|
end
|
378
429
|
|
430
|
+
def work_in_ractors(job_factory, options)
|
431
|
+
exception = nil
|
432
|
+
results = []
|
433
|
+
results_mutex = Mutex.new # arrays are not thread-safe on jRuby
|
434
|
+
|
435
|
+
callback = options[:ractor]
|
436
|
+
if block_given? || !callback
|
437
|
+
raise ArgumentError, "pass the code you want to execute as `ractor: [ClassName, :method_name]`"
|
438
|
+
end
|
439
|
+
|
440
|
+
# build
|
441
|
+
ractors = Array.new(options.fetch(:count)) do
|
442
|
+
Ractor.new do
|
443
|
+
loop do
|
444
|
+
got = receive
|
445
|
+
(klass, method_name), item, index = got
|
446
|
+
break if index == :break
|
447
|
+
begin
|
448
|
+
Ractor.yield [nil, klass.send(method_name, item), item, index]
|
449
|
+
rescue StandardError => e
|
450
|
+
Ractor.yield [e, nil, item, index]
|
451
|
+
end
|
452
|
+
end
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
# start
|
457
|
+
ractors.dup.each do |ractor|
|
458
|
+
if set = job_factory.next
|
459
|
+
item, index = set
|
460
|
+
instrument_start item, index, options
|
461
|
+
ractor.send [callback, item, index]
|
462
|
+
else
|
463
|
+
ractor.send([[nil, nil], nil, :break]) # stop the ractor
|
464
|
+
ractors.delete ractor
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
# replace with new items
|
469
|
+
while set = job_factory.next
|
470
|
+
item_next, index_next = set
|
471
|
+
done, (exception, result, item, index) = Ractor.select(*ractors)
|
472
|
+
if exception
|
473
|
+
ractors.delete done
|
474
|
+
break
|
475
|
+
end
|
476
|
+
instrument_finish item, index, result, options
|
477
|
+
results_mutex.synchronize { results[index] = (options[:preserve_results] == false ? nil : result) }
|
478
|
+
|
479
|
+
instrument_start item_next, index_next, options
|
480
|
+
done.send([callback, item_next, index_next])
|
481
|
+
end
|
482
|
+
|
483
|
+
# finish
|
484
|
+
ractors.each do |ractor|
|
485
|
+
(new_exception, result, item, index) = ractor.take
|
486
|
+
exception ||= new_exception
|
487
|
+
next if new_exception
|
488
|
+
instrument_finish item, index, result, options
|
489
|
+
results_mutex.synchronize { results[index] = (options[:preserve_results] == false ? nil : result) }
|
490
|
+
ractor.send([[nil, nil], nil, :break]) # stop the ractor
|
491
|
+
end
|
492
|
+
|
493
|
+
exception || results
|
494
|
+
end
|
495
|
+
|
379
496
|
def work_in_processes(job_factory, options, &blk)
|
380
497
|
workers = create_workers(job_factory, options, &blk)
|
381
498
|
results = []
|
@@ -407,9 +524,9 @@ module Parallel
|
|
407
524
|
results_mutex.synchronize { results[index] = result } # arrays are not threads safe on jRuby
|
408
525
|
rescue StandardError => e
|
409
526
|
exception = e
|
410
|
-
if Kill
|
527
|
+
if exception.is_a?(Kill)
|
411
528
|
(workers - [worker]).each do |w|
|
412
|
-
w.thread
|
529
|
+
w.thread&.kill
|
413
530
|
UserInterruptHandler.kill(w.pid)
|
414
531
|
end
|
415
532
|
end
|
@@ -420,18 +537,19 @@ module Parallel
|
|
420
537
|
end
|
421
538
|
end
|
422
539
|
end
|
540
|
+
|
423
541
|
exception || results
|
424
542
|
end
|
425
543
|
|
426
|
-
def replace_worker(job_factory, workers,
|
544
|
+
def replace_worker(job_factory, workers, index, options, blk)
|
427
545
|
options[:mutex].synchronize do
|
428
546
|
# old worker is no longer used ... stop it
|
429
|
-
worker = workers[
|
547
|
+
worker = workers[index]
|
430
548
|
worker.stop
|
431
549
|
|
432
550
|
# create a new replacement worker
|
433
551
|
running = workers - [worker]
|
434
|
-
workers[
|
552
|
+
workers[index] = worker(job_factory, options.merge(started_workers: running, worker_number: index), &blk)
|
435
553
|
end
|
436
554
|
end
|
437
555
|
|
@@ -473,14 +591,17 @@ module Parallel
|
|
473
591
|
until read.eof?
|
474
592
|
data = Marshal.load(read)
|
475
593
|
item, index = job_factory.unpack(data)
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
594
|
+
|
595
|
+
result =
|
596
|
+
begin
|
597
|
+
call_with_index(item, index, options, &block)
|
598
|
+
# https://github.com/rspec/rspec-support/blob/673133cdd13b17077b3d88ece8d7380821f8d7dc/lib/rspec/support.rb#L132-L140
|
599
|
+
rescue NoMemoryError, SignalException, Interrupt, SystemExit # rubocop:disable Lint/ShadowedException
|
600
|
+
raise $!
|
601
|
+
rescue Exception # # rubocop:disable Lint/RescueException
|
602
|
+
ExceptionWrapper.new($!)
|
603
|
+
end
|
604
|
+
|
484
605
|
begin
|
485
606
|
Marshal.dump(result, write)
|
486
607
|
rescue Errno::EPIPE
|
@@ -503,21 +624,53 @@ module Parallel
|
|
503
624
|
def call_with_index(item, index, options, &block)
|
504
625
|
args = [item]
|
505
626
|
args << index if options[:with_index]
|
627
|
+
results = block.call(*args)
|
506
628
|
if options[:return_results]
|
507
|
-
|
629
|
+
results
|
508
630
|
else
|
509
|
-
block.call(*args)
|
510
631
|
nil # avoid GC overhead of passing large results around
|
511
632
|
end
|
512
633
|
end
|
513
634
|
|
514
635
|
def with_instrumentation(item, index, options)
|
515
|
-
|
516
|
-
on_finish = options[:finish]
|
517
|
-
options[:mutex].synchronize { on_start.call(item, index) } if on_start
|
636
|
+
instrument_start(item, index, options)
|
518
637
|
result = yield
|
519
|
-
|
638
|
+
instrument_finish(item, index, result, options)
|
520
639
|
result unless options[:preserve_results] == false
|
521
640
|
end
|
641
|
+
|
642
|
+
def instrument_finish(item, index, result, options)
|
643
|
+
return unless (on_finish = options[:finish])
|
644
|
+
return instrument_finish_in_order(item, index, result, options) if options[:finish_in_order]
|
645
|
+
options[:mutex].synchronize { on_finish.call(item, index, result) }
|
646
|
+
end
|
647
|
+
|
648
|
+
# yield results in the order of the input items
|
649
|
+
# needs to use `options` to store state between executions
|
650
|
+
# needs to use `done` index since a nil result would also be valid
|
651
|
+
def instrument_finish_in_order(item, index, result, options)
|
652
|
+
options[:mutex].synchronize do
|
653
|
+
# initialize our state
|
654
|
+
options[:finish_done] ||= []
|
655
|
+
options[:finish_expecting] ||= 0 # we wait for item at index 0
|
656
|
+
|
657
|
+
# store current result
|
658
|
+
options[:finish_done][index] = [item, result]
|
659
|
+
|
660
|
+
# yield all results that are now in order
|
661
|
+
break unless index == options[:finish_expecting]
|
662
|
+
index.upto(options[:finish_done].size).each do |i|
|
663
|
+
break unless (done = options[:finish_done][i])
|
664
|
+
options[:finish_done][i] = nil # allow GC to free this item and result
|
665
|
+
options[:finish].call(done[0], i, done[1])
|
666
|
+
options[:finish_expecting] += 1
|
667
|
+
end
|
668
|
+
end
|
669
|
+
end
|
670
|
+
|
671
|
+
def instrument_start(item, index, options)
|
672
|
+
return unless on_start = options[:start]
|
673
|
+
options[:mutex].synchronize { on_start.call(item, index) }
|
674
|
+
end
|
522
675
|
end
|
523
676
|
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.24.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: 2023-12-16 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email: michael@grosser.it
|
@@ -18,15 +18,14 @@ extra_rdoc_files: []
|
|
18
18
|
files:
|
19
19
|
- MIT-LICENSE.txt
|
20
20
|
- lib/parallel.rb
|
21
|
-
- lib/parallel/processor_count.rb
|
22
21
|
- lib/parallel/version.rb
|
23
22
|
homepage: https://github.com/grosser/parallel
|
24
23
|
licenses:
|
25
24
|
- MIT
|
26
25
|
metadata:
|
27
26
|
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.
|
27
|
+
documentation_uri: https://github.com/grosser/parallel/blob/v1.24.0/Readme.md
|
28
|
+
source_code_uri: https://github.com/grosser/parallel/tree/v1.24.0
|
30
29
|
wiki_uri: https://github.com/grosser/parallel/wiki
|
31
30
|
post_install_message:
|
32
31
|
rdoc_options: []
|
@@ -36,14 +35,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
36
35
|
requirements:
|
37
36
|
- - ">="
|
38
37
|
- !ruby/object:Gem::Version
|
39
|
-
version: '2.
|
38
|
+
version: '2.5'
|
40
39
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
40
|
requirements:
|
42
41
|
- - ">="
|
43
42
|
- !ruby/object:Gem::Version
|
44
43
|
version: '0'
|
45
44
|
requirements: []
|
46
|
-
rubygems_version: 3.1.
|
45
|
+
rubygems_version: 3.1.6
|
47
46
|
signing_key:
|
48
47
|
specification_version: 4
|
49
48
|
summary: Run any kind of code in parallel processes
|
@@ -1,42 +0,0 @@
|
|
1
|
-
require 'etc'
|
2
|
-
|
3
|
-
module Parallel
|
4
|
-
# TODO: inline this method into parallel.rb and kill physical_processor_count in next major release
|
5
|
-
module ProcessorCount
|
6
|
-
# Number of processors seen by the OS, used for process scheduling
|
7
|
-
def processor_count
|
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 = case RbConfig::CONFIG["target_os"]
|
15
|
-
when /darwin1/
|
16
|
-
IO.popen("/usr/sbin/sysctl -n hw.physicalcpu").read.to_i
|
17
|
-
when /linux/
|
18
|
-
cores = {} # unique physical ID / core ID combinations
|
19
|
-
phy = 0
|
20
|
-
IO.read("/proc/cpuinfo").scan(/^physical id.*|^core id.*/) do |ln|
|
21
|
-
if ln.start_with?("physical")
|
22
|
-
phy = ln[/\d+/]
|
23
|
-
elsif ln.start_with?("core")
|
24
|
-
cid = phy + ":" + ln[/\d+/]
|
25
|
-
cores[cid] = true if not cores[cid]
|
26
|
-
end
|
27
|
-
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
|
-
# fall back to logical count if physical info is invalid
|
38
|
-
ppc > 0 ? ppc : processor_count
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|