class Processor
Computes interval CPU utilization from cumulative process metrics.
Nested Classes and Modules
class SampleAn immutable measurement of process CPU usage over an interval.
Definitions
def self.quota
The processor capacity available to the current process.
This takes cgroup v2 CPU bandwidth limits into account, and otherwise returns Process::Metrics::Processor.count as a Float.
Signature
-
returns
Float The available processor capacity in core units.
Implementation
def self.quota
count = self.count.to_f
if quota = Linux.quota
if quota < count
return quota
end
end
return count
end
def self.quota
The processor capacity available to the current process.
On Linux, this takes cgroup v2 CPU bandwidth limits into account. Otherwise, it returns Process::Metrics::Processor.count as a Float.
Signature
-
returns
Float The available processor capacity in core units.
Implementation
def self.quota
return self.count.to_f
end
def self.count
The number of processors available to the current process. On Linux, this takes the process CPU affinity into account.
Signature
-
returns
Integer The number of available processors.
Implementation
def self.count
Etc.nprocessors
end
Snapshot = Struct.new(:start_time, :processor_time, :timestamp)
- private
Signature
- private
def initialize(capture: General.method(:capture))
Initialize a process metrics sampler.
Signature
-
parameter
captureInterface(:call) The process snapshot capture callable.
Implementation
def initialize(capture: General.method(:capture))
@capture = capture
@snapshots = {}
end
def sample(process_ids)
Sample CPU utilization for the given processes. The first observation of each process establishes a baseline and does not produce a sample.
Signature
-
parameter
process_idsInteger | Array(Integer) The process IDs to sample.
-
returns
Hash(Integer, Processor::Sample) The valid interval samples keyed by process ID.
Implementation
def sample(process_ids)
processes = @capture.call(pid: process_ids, memory: false)
timestamp = now
return {} unless finite?(timestamp)
samples = {}
snapshots = {}
processes.each do |process_id, process|
start_time = process.start_time
processor_time = process.processor_time
next unless finite?(start_time) && finite?(processor_time)
current = Snapshot.new(start_time, processor_time, timestamp)
snapshots[process_id] = current
if previous = @snapshots[process_id]
next unless previous.start_time == current.start_time
duration = current.timestamp - previous.timestamp
processor_time = current.processor_time - previous.processor_time
next unless finite?(duration) && duration > 0.0
next unless finite?(processor_time) && processor_time >= 0.0
utilization = processor_time / duration
next unless finite?(utilization)
samples[process_id] = Sample.new(process_id, duration, processor_time, utilization).freeze
end
end
@snapshots = snapshots
return samples
end
def now
Get the current monotonic time.
Implementation
def now
Process.clock_gettime(Process::CLOCK_MONOTONIC)
end
def finite?(value)
Whether the value is a finite number.
Implementation
def finite?(value)
value.is_a?(Numeric) && value.finite?
end