File: processor_counter.rb

package info (click to toggle)
ruby-concurrent 1.1.6%2Bdfsg-5
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 30,284 kB
  • sloc: ruby: 30,875; java: 6,117; javascript: 1,114; ansic: 288; makefile: 10; sh: 6
file content (163 lines) | stat: -rw-r--r-- 6,915 bytes parent folder | download | duplicates (2)
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