123456789_123456789_123456789_123456789_123456789_

Class: Concurrent::Utility::ProcessorCounter

Relationships & Source Files
Inherits: Object
Defined in: lib/concurrent-ruby/concurrent/utility/processor_counter.rb

Class Method Summary

Instance Method Summary

Constructor Details

.newProcessorCounter

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/utility/processor_counter.rb', line 11

def initialize
  @processor_count          = Delay.new { compute_processor_count }
  @physical_processor_count = Delay.new { compute_physical_processor_count }
  @cpu_quota                = Delay.new { compute_cpu_quota }
  @cpu_shares               = Delay.new { compute_cpu_shares }
end

Instance Method Details

#available_processor_count

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/utility/processor_counter.rb', line 26

def available_processor_count
  cpu_count = processor_count.to_f
  quota = cpu_quota

  return cpu_count if quota.nil?

  # cgroup cpus quotas have no limits, so they can be set to higher than the
  # real count of cores.
  if quota > cpu_count
    cpu_count
  else
    quota
  end
end

#compute_cpu_quota (private)

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/utility/processor_counter.rb', line 104

def compute_cpu_quota
  if RbConfig::CONFIG["target_os"].include?("linux")
    if File.exist?("/sys/fs/cgroup/cpu.max")
      # cgroups v2: https://docs.kernel.org/admin-guide/cgroup-v2.html#cpu-interface-files
      cpu_max = File.read("/sys/fs/cgroup/cpu.max")
      return nil if cpu_max.start_with?("max ") # no limit
      max, period = cpu_max.split.map(&:to_f)
      max / period
    elsif File.exist?("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us")
      # cgroups v1: https://kernel.googlesource.com/pub/scm/linux/kernel/git/glommer/memcg/+/cpu_stat/Documentation/cgroups/cpu.txt
      max = File.read("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us").to_i
      # If the cpu.cfs_quota_us is -1, cgroup does not adhere to any CPU time restrictions
      # https://docs.kernel.org/scheduler/sched-bwc.html#management
      return nil if max <= 0
      period = File.read("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_period_us").to_f
      max / period
    end
  end
end

#compute_cpu_shares (private)

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/utility/processor_counter.rb', line 124

def compute_cpu_shares
  if RbConfig::CONFIG["target_os"].include?("linux")
    if File.exist?("/sys/fs/cgroup/cpu.weight")
      # cgroups v2: https://docs.kernel.org/admin-guide/cgroup-v2.html#cpu-interface-files
      # Ref: https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/2254-cgroup-v2#phase-1-convert-from-cgroups-v1-settings-to-v2
      weight = File.read("/sys/fs/cgroup/cpu.weight").to_f
      ((((weight - 1) * 262142) / 9999) + 2) / 1024
    elsif File.exist?("/sys/fs/cgroup/cpu/cpu.shares")
      # cgroups v1: https://kernel.googlesource.com/pub/scm/linux/kernel/git/glommer/memcg/+/cpu_stat/Documentation/cgroups/cpu.txt
      File.read("/sys/fs/cgroup/cpu/cpu.shares").to_f / 1024
    end
  end
end

#compute_physical_processor_count (private)

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/utility/processor_counter.rb', line 59

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/
          # 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
          result = run('powershell -command "Get-CimInstance -ClassName Win32_Processor -Property NumberOfCores | Select-Object -Property NumberOfCores"')
          if !result || $?.exitstatus != 0
            # fallback to deprecated wmic for older systems
            result = run("wmic cpu get NumberOfCores")
          end
          if !result || $?.exitstatus != 0
            # Bail out if both commands returned something unexpected
            processor_count
          else
            # powershell: "\nNumberOfCores\n-------------\n            4\n\n\n"
            # wmic:       "NumberOfCores  \n\n4              \n\n\n\n"
            result.scan(/\d/).map(&:to_i).reduce(:)
          end
        else
          processor_count
        end
  # fall back to logical count if physical info is invalid
  ppc > 0 ? ppc : processor_count
rescue
  return 1
end

#compute_processor_count (private)

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/utility/processor_counter.rb', line 51

def compute_processor_count
  if Concurrent.on_jruby?
    java.lang.Runtime.getRuntime.availableProcessors
  else
    Etc.nprocessors
  end
end

#cpu_quota

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/utility/processor_counter.rb', line 41

def cpu_quota
  @cpu_quota.value
end

#cpu_shares

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/utility/processor_counter.rb', line 45

def cpu_shares
  @cpu_shares.value
end

#physical_processor_count

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/utility/processor_counter.rb', line 22

def physical_processor_count
  @physical_processor_count.value
end

#processor_count

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/utility/processor_counter.rb', line 18

def processor_count
  @processor_count.value
end

#run(command) (private)

[ GitHub ]

  
# File 'lib/concurrent-ruby/concurrent/utility/processor_counter.rb', line 99

def run(command)
  IO.popen(command, &:read)
rescue Errno::ENOENT
end