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 /darwin1/
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
|