compute_unit 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +23 -1
- data/README.md +6 -0
- data/compute_unit.gemspec +2 -2
- data/lib/compute_unit.rb +37 -4
- data/lib/compute_unit/compute_base.rb +21 -0
- data/lib/compute_unit/cpu.rb +89 -2
- data/lib/compute_unit/device.rb +2 -2
- data/lib/compute_unit/gpu.rb +13 -0
- data/lib/compute_unit/version.rb +1 -1
- metadata +23 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e7454de36f403e2803ee5b87ee989781de8b663d32cfe290b3c22ab0c18f598d
|
4
|
+
data.tar.gz: 6b6afa144e05011bec545ddbda688ae663650dce4d09070be9a3922f84bc9606
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3821595267038d8424a7ae3558437339f87b871ce4e294ffe80cce1c928924a9ae1ebd7fa9b293c08a2b40217a71f7a4358e8a943456dd0c978cf7e4bfbb5a0a
|
7
|
+
data.tar.gz: 4fa97e267f9fd8c60999d8af1dd96e15e9e77435c98ece688e53386147e0e7470b1f9a7e149f702fbcb237fbf6c41085f051db9ad441e1388dbde6ce9d2469a5
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,28 @@
|
|
1
1
|
# Compute Unit Changelog
|
2
2
|
|
3
|
-
##
|
3
|
+
## 0.5.0
|
4
|
+
Released 10/5/2020
|
5
|
+
|
6
|
+
* Add more method docs
|
7
|
+
* Allow overriding of cpu power
|
8
|
+
* Update cpu name using model
|
9
|
+
* Add new search methods
|
10
|
+
* Add voltage and other cpu metrics
|
11
|
+
## 0.4.0
|
12
|
+
* Adds ability to list attached processes to compute_unit sorted by field
|
13
|
+
* Adds ability to list top_x attached processes sorted by field
|
14
|
+
|
15
|
+
## 0.3.3
|
16
|
+
* Fix bug with default database not being created
|
17
|
+
|
18
|
+
## 0.3.2
|
19
|
+
* Fix bug with default database not being created
|
20
|
+
|
21
|
+
## 0.3.1
|
22
|
+
Released 9/24/2020
|
23
|
+
|
24
|
+
* Use the default system pcidb file instead of throwing error
|
25
|
+
* Return default data if no hmon value exist
|
4
26
|
|
5
27
|
## 0.3.0
|
6
28
|
Released 6/16/2020
|
data/README.md
CHANGED
@@ -35,6 +35,12 @@ Or install it yourself as:
|
|
35
35
|
|
36
36
|
## Usage
|
37
37
|
|
38
|
+
Ensure the pci database exist on your system. You can update it at anytime with the built in linux command.
|
39
|
+
|
40
|
+
`/usr/sbin/update-pciids` or the command from this gem `update_pcidb` installed in your gem bin directory.
|
41
|
+
|
42
|
+
Additionally, when using the `ComputeUnit.find_all_with_database` method.
|
43
|
+
|
38
44
|
Find all compute devices
|
39
45
|
```
|
40
46
|
require 'compute_unit'
|
data/compute_unit.gemspec
CHANGED
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
|
|
25
25
|
|
26
26
|
spec.metadata['homepage_uri'] = spec.homepage
|
27
27
|
spec.metadata['source_code_uri'] = spec.homepage
|
28
|
-
spec.metadata['changelog_uri'] = "#{spec.homepage}/CHANGELOG.md"
|
28
|
+
spec.metadata['changelog_uri'] = "#{spec.homepage}/-/blob/master/CHANGELOG.md"
|
29
29
|
|
30
30
|
# Specify which files should be added to the gem when it is released.
|
31
31
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
@@ -35,7 +35,7 @@ Gem::Specification.new do |spec|
|
|
35
35
|
spec.bindir = 'exe'
|
36
36
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
37
37
|
spec.require_paths = ['lib']
|
38
|
-
|
38
|
+
spec.add_dependency 'sys-proctable', '~> 1.2', '>= 1.2.0'
|
39
39
|
spec.add_dependency 'opencl_ruby_ffi', '~> 1.3.4'
|
40
40
|
spec.add_development_dependency 'bundler', '~> 2.1'
|
41
41
|
spec.add_development_dependency 'rake', '~> 13'
|
data/lib/compute_unit.rb
CHANGED
@@ -11,23 +11,56 @@ module ComputeUnit
|
|
11
11
|
SYS_DEVICE_PATH = File.join(SYSFS_PATH, 'bus/pci/devices')
|
12
12
|
PCI_DATABASE_PATH = File.join(File.dirname(__dir__), 'pci.ids')
|
13
13
|
PCI_DATABASE_URL = 'http://pci-ids.ucw.cz/v2.2/pci.ids'
|
14
|
+
DEFAULT_PCIDB_PATH = '/usr/share/misc/pci.ids'
|
14
15
|
|
16
|
+
# @param use_opencl [Boolean]
|
17
|
+
# @return [Array] - return a list of compute units
|
15
18
|
def self.find_all(use_opencl = false)
|
16
19
|
require 'compute_unit/gpu'
|
17
20
|
require 'compute_unit/cpu'
|
18
21
|
require 'compute_unit/asic'
|
19
|
-
raise Exceptions::InvalidPCIDatabase.new('Run: ComputeUnit.refresh_pci_database') unless File.exist?(PCI_DATABASE_PATH)
|
20
22
|
|
21
|
-
|
22
|
-
|
23
|
-
|
23
|
+
# if this file doesn't exist we need to fetch it or copy it
|
24
|
+
refresh_pci_database unless File.exist?(PCI_DATABASE_PATH)
|
25
|
+
|
26
|
+
Gpu.find_all(use_opencl) + Cpu.find_all + Asic.find_all
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param pid [Integer] the pid to search with
|
30
|
+
# @param field [Symbol] - the field to sort by
|
31
|
+
# @return [Array] - a array of device paths
|
32
|
+
# @example usage
|
33
|
+
# device_paths_by_process(3748) => ["/sys/bus/pci/devices/0000:00:00.0"]
|
34
|
+
def self.device_paths_by_process(pid)
|
35
|
+
# processes = ComputeUnit::Cpu.attached_processes(field).last(1) + ComputeUnit::Gpu.attached_processes(field)
|
36
|
+
# processes.find_all { |process| process.pid == pid }
|
37
|
+
# We can get the utilized devices but it appears cubersome to convert to a device path
|
38
|
+
# This method uses more resources
|
39
|
+
find_by_process(pid).map(&:device_path)
|
40
|
+
end
|
41
|
+
|
42
|
+
# @param pid [Integer] the pid to search with
|
43
|
+
# @param use_opencl [Boolean] use opencl on gpu devices
|
44
|
+
def self.find_by_process(pid, use_opencl = false)
|
45
|
+
find_all(use_opencl).find_all do |unit|
|
46
|
+
unit.top_processes.first.pid.to_i == pid.to_i
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# copies the default pci database from linux filesystem over to the gem path
|
51
|
+
def self.copy_default_database
|
52
|
+
FileUtils.cp(DEFAULT_PCIDB_PATH, PCI_DATABASE_PATH) if File.exist?(DEFAULT_PCIDB_PATH)
|
24
53
|
end
|
25
54
|
|
55
|
+
# get a fresh copy of the database and then use find_all
|
56
|
+
# @param use_opencl [Boolean]
|
57
|
+
# @return [Array] - return a list of compute units
|
26
58
|
def self.find_all_with_database(use_opencl = false)
|
27
59
|
refresh_pci_database
|
28
60
|
find_all(use_opencl)
|
29
61
|
end
|
30
62
|
|
63
|
+
# downloads the pci database
|
31
64
|
def self.refresh_pci_database
|
32
65
|
ComputeUnit::Utils.check_for_root
|
33
66
|
require 'net/http'
|
@@ -4,9 +4,12 @@ require 'time'
|
|
4
4
|
require 'compute_unit/formatters'
|
5
5
|
require 'compute_unit/logger'
|
6
6
|
require 'compute_unit/device'
|
7
|
+
require 'sys/proctable'
|
7
8
|
|
8
9
|
module ComputeUnit
|
9
10
|
class ComputeBase < Device
|
11
|
+
include Sys
|
12
|
+
|
10
13
|
attr_reader :type, :serial, :meta, :uuid, :timestamp, :index, :compute_type
|
11
14
|
attr_accessor :power_offset
|
12
15
|
|
@@ -26,6 +29,24 @@ module ComputeUnit
|
|
26
29
|
end
|
27
30
|
end
|
28
31
|
|
32
|
+
# @summary Finds all cpu attached processes and sorts by pctcpu
|
33
|
+
# param filter [Regex] - if supplied filter out devices from fd list
|
34
|
+
# @param field [Symbol] - the field to sort by
|
35
|
+
# @return [Array] - an array of attached processes
|
36
|
+
def attached_processes(field = :pctcpu, filter = nil)
|
37
|
+
raise NotImplementedError unless self.class.respond_to?(:attached_processes)
|
38
|
+
|
39
|
+
self.class.attached_processes(field, filter)
|
40
|
+
end
|
41
|
+
|
42
|
+
# @summary Find the processes consuming the most cpu
|
43
|
+
# @param x [Integer] the number of processes to return, defaults to 1
|
44
|
+
# @param field [Symbol] - the field to sort by
|
45
|
+
# @return [Array] - an array of attached processes
|
46
|
+
def top_processes(x = 1, field = :pctcpu)
|
47
|
+
attached_processes(field).last(x)
|
48
|
+
end
|
49
|
+
|
29
50
|
# @param value [Float] a value to offset the power calculation, either a whole number, or decimal
|
30
51
|
# @return [Integer] the value set as the offset
|
31
52
|
def power_offset=(value)
|
data/lib/compute_unit/cpu.rb
CHANGED
@@ -6,8 +6,8 @@ module ComputeUnit
|
|
6
6
|
class Cpu < ComputeBase
|
7
7
|
DEVICE_CLASS = '060000'
|
8
8
|
DEVICE_CLASS_NAME = 'CPU'
|
9
|
+
VOLTAGE_MSR = 0x198
|
9
10
|
# @return [Array] - returns a list of device paths of all devices considered for display
|
10
|
-
alias name model
|
11
11
|
|
12
12
|
def self.devices
|
13
13
|
ComputeUnit::ComputeBase.devices.find_all do |device|
|
@@ -15,38 +15,83 @@ module ComputeUnit
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
+
def utilization
|
19
|
+
1 # until we can calculate this. Is loadavg a good metric? or load / cpus
|
20
|
+
end
|
21
|
+
|
22
|
+
# @summary Finds all cpu attached processes and sorts by pctcpu
|
23
|
+
# @param field [Symbol] - the field to sort by
|
24
|
+
# @param filter [Regex] - if supplied filter out devices from fd list
|
25
|
+
# @return [Array] - an array of attached processes
|
26
|
+
def self.attached_processes(field = :pctcpu, _filter = nil)
|
27
|
+
Sys::ProcTable.ps(smaps: false).sort_by(&field)
|
28
|
+
end
|
29
|
+
|
30
|
+
# @summary [Float] - returns the voltage of the cpu
|
31
|
+
# @param processor_id [Integer] - the id of the cpu
|
32
|
+
def voltage(processor_id = 0)
|
33
|
+
file = "/dev/cpu/#{processor_id}/msr"
|
34
|
+
return 0 unless File.exist?(file)
|
35
|
+
|
36
|
+
# read 8 bytes, then unpack it by turning it into an integer
|
37
|
+
# make it a binary string, pluck out some specific bits, then
|
38
|
+
# convert it back to an integer
|
39
|
+
# divide by 8192 to give the voltage
|
40
|
+
# lowbit = 32
|
41
|
+
# highbit = 47
|
42
|
+
# bits = 47 - 32 + 1 # we want to read bits 32-47
|
43
|
+
msr = IO.new IO.sysopen(file, 'rb')
|
44
|
+
msr.sysseek(VOLTAGE_MSR)
|
45
|
+
data, = msr.sysread(8).unpack('q')
|
46
|
+
format('%.2f', ((data >> 32) / 8192.to_f))
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [String] - the model / name of the cpu
|
18
50
|
def model
|
19
51
|
raw_cpu_data[:model_name]
|
20
52
|
end
|
21
53
|
|
54
|
+
# @return [String] - the model / name of the cpu
|
55
|
+
def name
|
56
|
+
model
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [String] - the maker of the cpu
|
22
60
|
def make
|
23
61
|
raw_cpu_data[:vendor_id]
|
24
62
|
end
|
25
63
|
|
64
|
+
# @return [Integer] - the number of cores
|
26
65
|
def num_cores
|
27
66
|
raw_cpu_data[:cores_per_socket].to_i
|
28
67
|
end
|
29
68
|
|
69
|
+
# @return [Integer] - the number of threads
|
30
70
|
def num_threads
|
31
71
|
raw_cpu_data[:threads_per_core].to_i
|
32
72
|
end
|
33
73
|
|
74
|
+
# @return [Integer] - the number of cpus
|
34
75
|
def nproc
|
35
76
|
raw_cpu_data[:cpus].to_i
|
36
77
|
end
|
37
78
|
|
79
|
+
# @return [Float] - current mhz of cpu
|
38
80
|
def current_freq_mhz
|
39
81
|
raw_cpu_data[:cpu_mhz].to_f.round(0)
|
40
82
|
end
|
41
83
|
|
84
|
+
# @return [Float] - max mhz of cpu
|
42
85
|
def max_freq_mhz
|
43
86
|
raw_cpu_data[:cpu_max_mhz].to_f.round(0)
|
44
87
|
end
|
45
88
|
|
89
|
+
# @return [Float] - current min of cpu
|
46
90
|
def min_freq_mhz
|
47
91
|
raw_cpu_data[:cpu_min_mhz].to_f.round(0)
|
48
92
|
end
|
49
93
|
|
94
|
+
# @return [String] the path of the hwmon path for monitoring temps
|
50
95
|
def base_hwmon_path
|
51
96
|
File.join(ComputeUnit::SYSFS_PATH, 'devices/platform/coretemp.0/hwmon')
|
52
97
|
end
|
@@ -68,6 +113,40 @@ module ComputeUnit
|
|
68
113
|
read_hwmon_data('temp1_input', 0).to_f.round(0) / 1000
|
69
114
|
end
|
70
115
|
|
116
|
+
def status
|
117
|
+
0
|
118
|
+
end
|
119
|
+
|
120
|
+
def power
|
121
|
+
ENV.fetch('CPU_POWER', 60) # until we can calculate power we will just use 60
|
122
|
+
end
|
123
|
+
|
124
|
+
def fan
|
125
|
+
2500 # until we can calculate fan speed
|
126
|
+
end
|
127
|
+
|
128
|
+
def mem_temp
|
129
|
+
0 # until we can get the mem temp
|
130
|
+
end
|
131
|
+
|
132
|
+
def pci_loc
|
133
|
+
device_path
|
134
|
+
end
|
135
|
+
|
136
|
+
def status_info
|
137
|
+
{ index: "CPU#{index}",
|
138
|
+
name: model,
|
139
|
+
bios: 'N/A',
|
140
|
+
core_clock: current_freq_mhz,
|
141
|
+
memory_clock: 'N/A',
|
142
|
+
power: power,
|
143
|
+
fan: fan,
|
144
|
+
core_volt: voltage,
|
145
|
+
temp: temp,
|
146
|
+
mem_temp: mem_temp,
|
147
|
+
status: status }
|
148
|
+
end
|
149
|
+
|
71
150
|
def metrics
|
72
151
|
{
|
73
152
|
temp: temp,
|
@@ -87,6 +166,14 @@ module ComputeUnit
|
|
87
166
|
super.merge(metrics)
|
88
167
|
end
|
89
168
|
|
169
|
+
def initialize(_device_path, opts)
|
170
|
+
super
|
171
|
+
@type = :CPU
|
172
|
+
@pci_loc = device_path,
|
173
|
+
@index = opts[:index].to_i
|
174
|
+
@power_offset = 0
|
175
|
+
end
|
176
|
+
|
90
177
|
def self.create_from_path(device_path, index)
|
91
178
|
opts = {
|
92
179
|
device_class_id: device_class(device_path),
|
@@ -99,7 +186,7 @@ module ComputeUnit
|
|
99
186
|
new(device_path, opts)
|
100
187
|
end
|
101
188
|
|
102
|
-
def self.find_all
|
189
|
+
def self.find_all(_use_opencl = false)
|
103
190
|
devices.sort.map.with_index do |device_path, index|
|
104
191
|
create_from_path(device_path, index)
|
105
192
|
end
|
data/lib/compute_unit/device.rb
CHANGED
@@ -140,9 +140,9 @@ module ComputeUnit
|
|
140
140
|
# @param item [String] - the name of the hwmon file to read from
|
141
141
|
# @param default [Object] - the default value to return if the file is empty or not readable
|
142
142
|
# @return [String] - the value of the item looked up
|
143
|
-
def read_hwmon_data(item,
|
143
|
+
def read_hwmon_data(item, default = nil)
|
144
144
|
path = File.join(hwmon_path, item)
|
145
|
-
read_file(path)
|
145
|
+
read_file(path, default)
|
146
146
|
end
|
147
147
|
|
148
148
|
# @param item [String] - the name of the hwmon file to write to
|
data/lib/compute_unit/gpu.rb
CHANGED
@@ -13,6 +13,19 @@ module ComputeUnit
|
|
13
13
|
type
|
14
14
|
end
|
15
15
|
|
16
|
+
# @summary Finds all cpu attached processes and sorts by pctcpu
|
17
|
+
# @param filter [Regex] - if supplied filter out devices from fd list
|
18
|
+
# @param field [Symbol] - the field to sort by
|
19
|
+
# @return [Array] - an array of attached processes
|
20
|
+
def self.attached_processes(field = :pctcpu, filter = %r{/dev/dri|nvidia\d+})
|
21
|
+
filter ||= %r{/dev/dri|nvidia\d+}
|
22
|
+
# looks for any fd device with dri or nvidia in the name
|
23
|
+
p = Sys::ProcTable.ps(smaps: false).find_all do |p|
|
24
|
+
p.fd.values.find { |f| f =~ filter }
|
25
|
+
end
|
26
|
+
p.sort_by(&field)
|
27
|
+
end
|
28
|
+
|
16
29
|
# @return [OpenCL_Device]
|
17
30
|
def opencl_device
|
18
31
|
@opencl_device ||= self.class.opencl_devices.find_all { |cu| cu[:type] == make }[index] if use_opencl
|
data/lib/compute_unit/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,35 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: compute_unit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Corey Osman
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-11-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: sys-proctable
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.2.0
|
20
|
+
- - "~>"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '1.2'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.2.0
|
30
|
+
- - "~>"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '1.2'
|
13
33
|
- !ruby/object:Gem::Dependency
|
14
34
|
name: opencl_ruby_ffi
|
15
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -119,7 +139,7 @@ licenses:
|
|
119
139
|
metadata:
|
120
140
|
homepage_uri: https://gitlab.com/blockops/compute_unit
|
121
141
|
source_code_uri: https://gitlab.com/blockops/compute_unit
|
122
|
-
changelog_uri: https://gitlab.com/blockops/compute_unit/CHANGELOG.md
|
142
|
+
changelog_uri: https://gitlab.com/blockops/compute_unit/-/blob/master/CHANGELOG.md
|
123
143
|
post_install_message:
|
124
144
|
rdoc_options: []
|
125
145
|
require_paths:
|