/
processor_counter.rb
163 lines (153 loc) · 6.76 KB
/
processor_counter.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
require 'etc'
require 'rbconfig'
require 'concurrent/delay'
module Concurrent
module Utility
# @!visibility private
class ProcessorCounter
def initialize
@processor_count = Delay.new { compute_processor_count }
@physical_processor_count = Delay.new { compute_physical_processor_count }
end
# Number of processors seen by the OS and used for process scheduling. For
# performance reasons the calculated value will be memoized on the first
# call.
#
# When running under JRuby the Java runtime call
# `java.lang.Runtime.getRuntime.availableProcessors` will be used. According
# to the Java documentation this "value may change during a particular
# invocation of the virtual machine... [applications] should therefore
# occasionally poll this property." Subsequently the result will NOT be
# memoized under JRuby.
#
# Ruby's Etc.nprocessors will be used if available (MRI 2.2+).
#
# On Windows the Win32 API will be queried for the
# `NumberOfLogicalProcessors from Win32_Processor`. This will return the
# total number "logical processors for the current instance of the
# processor", which taked into account hyperthreading.
#
# * AIX: /usr/sbin/pmcycles (AIX 5+), /usr/sbin/lsdev
# * Alpha: /usr/bin/nproc (/proc/cpuinfo exists but cannot be used)
# * BSD: /sbin/sysctl
# * Cygwin: /proc/cpuinfo
# * Darwin: /usr/bin/hwprefs, /usr/sbin/sysctl
# * HP-UX: /usr/sbin/ioscan
# * IRIX: /usr/sbin/sysconf
# * Linux: /proc/cpuinfo
# * Minix 3+: /proc/cpuinfo
# * Solaris: /usr/sbin/psrinfo
# * Tru64 UNIX: /usr/sbin/psrinfo
# * UnixWare: /usr/sbin/psrinfo
#
# @return [Integer] number of processors seen by the OS or Java runtime
#
# @see https://github.com/grosser/parallel/blob/4fc8b89d08c7091fe0419ca8fba1ec3ce5a8d185/lib/parallel.rb
#
# @see http://docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html#availableProcessors()
# @see http://msdn.microsoft.com/en-us/library/aa394373(v=vs.85).aspx
def processor_count
@processor_count.value
end
# Number of physical processor cores on the current system. For performance
# reasons the calculated value will be memoized on the first call.
#
# On Windows the Win32 API will be queried for the `NumberOfCores from
# Win32_Processor`. This will return the total number "of cores for the
# current instance of the processor." On Unix-like operating systems either
# the `hwprefs` or `sysctl` utility will be called in a subshell and the
# returned value will be used. In the rare case where none of these methods
# work or an exception is raised the function will simply return 1.
#
# @return [Integer] number physical processor cores on the current system
#
# @see https://github.com/grosser/parallel/blob/4fc8b89d08c7091fe0419ca8fba1ec3ce5a8d185/lib/parallel.rb
#
# @see http://msdn.microsoft.com/en-us/library/aa394373(v=vs.85).aspx
# @see http://www.unix.com/man-page/osx/1/HWPREFS/
# @see http://linux.die.net/man/8/sysctl
def physical_processor_count
@physical_processor_count.value
end
private
def compute_processor_count
if Concurrent.on_jruby?
java.lang.Runtime.getRuntime.availableProcessors
elsif Etc.respond_to?(:nprocessors) && (nprocessor = Etc.nprocessors rescue nil)
nprocessor
else
os_name = RbConfig::CONFIG["target_os"]
if os_name =~ /mingw|mswin/
require 'win32ole'
result = WIN32OLE.connect("winmgmts://").ExecQuery(
"select NumberOfLogicalProcessors from Win32_Processor")
result.to_enum.collect(&:NumberOfLogicalProcessors).reduce(:+)
elsif File.readable?("/proc/cpuinfo") && (cpuinfo_count = IO.read("/proc/cpuinfo").scan(/^processor/).size) > 0
cpuinfo_count
elsif File.executable?("/usr/bin/nproc")
IO.popen("/usr/bin/nproc --all", &:read).to_i
elsif File.executable?("/usr/bin/hwprefs")
IO.popen("/usr/bin/hwprefs thread_count", &:read).to_i
elsif File.executable?("/usr/sbin/psrinfo")
IO.popen("/usr/sbin/psrinfo", &:read).scan(/^.*on-*line/).size
elsif File.executable?("/usr/sbin/ioscan")
IO.popen("/usr/sbin/ioscan -kC processor", &:read).scan(/^.*processor/).size
elsif File.executable?("/usr/sbin/pmcycles")
IO.popen("/usr/sbin/pmcycles -m", &:read).count("\n")
elsif File.executable?("/usr/sbin/lsdev")
IO.popen("/usr/sbin/lsdev -Cc processor -S 1", &:read).count("\n")
elsif File.executable?("/usr/sbin/sysconf") and os_name =~ /irix/i
IO.popen("/usr/sbin/sysconf NPROC_ONLN", &:read).to_i
elsif File.executable?("/usr/sbin/sysctl")
IO.popen("/usr/sbin/sysctl -n hw.ncpu", &:read).to_i
elsif File.executable?("/sbin/sysctl")
IO.popen("/sbin/sysctl -n hw.ncpu", &:read).to_i
else
# TODO (pitr-ch 05-Nov-2016): warn about failures
1
end
end
rescue
return 1
end
def compute_physical_processor_count
ppc = case RbConfig::CONFIG["target_os"]
when /darwin\d\d/
IO.popen("/usr/sbin/sysctl -n hw.physicalcpu", &:read).to_i
when /linux/
cores = {} # unique physical ID / core ID combinations
phy = 0
IO.read("/proc/cpuinfo").scan(/^physical id.*|^core id.*/) do |ln|
if ln.start_with?("physical")
phy = ln[/\d+/]
elsif ln.start_with?("core")
cid = phy + ":" + ln[/\d+/]
cores[cid] = true if not cores[cid]
end
end
cores.count
when /mswin|mingw/
require 'win32ole'
result_set = WIN32OLE.connect("winmgmts://").ExecQuery(
"select NumberOfCores from Win32_Processor")
result_set.to_enum.collect(&:NumberOfCores).reduce(:+)
else
processor_count
end
# fall back to logical count if physical info is invalid
ppc > 0 ? ppc : processor_count
rescue
return 1
end
end
end
# create the default ProcessorCounter on load
@processor_counter = Utility::ProcessorCounter.new
singleton_class.send :attr_reader, :processor_counter
def self.processor_count
processor_counter.processor_count
end
def self.physical_processor_count
processor_counter.physical_processor_count
end
end