compute_unit 0.3.1 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0ff898fab62731ca014ea856033f2e7d024e1013703af0cba4b194844b1fc683
4
- data.tar.gz: 9c9e7b32eca6a7313c823ef6c96171f939882550c3f5ecd405c359640ed71e2d
3
+ metadata.gz: f51ca7c79e4dfb8a97f4221a0798a59169ddf4c925978bd2cff52633412573ab
4
+ data.tar.gz: 8e5b1cf6adad26fac8c54cdc54f3178af1a8e2325657e997419f98d8306d9e86
5
5
  SHA512:
6
- metadata.gz: '08fb1888538fefb976eb66350eb1fb1afaac4901bc847b60532228ffbcdd565dc72448f318566dc5781d0a69b53c81ef47b32836e5b51eee0c4925c200e95792'
7
- data.tar.gz: ea1603c5ff7aa307e7d566b50420a31e582b437255e2a6eff57f44981732e0c98865684035dec64da270bd59a457fd30250950e5cc5a28dad33cb338ef329a06
6
+ metadata.gz: b18314336244f86d4c7417def3bf97d90762696df44cd9a1f6bced80b6dca142afe6deb5cd8954795e773c974061d8737bf7635307305e96d9018e1a59fcef17
7
+ data.tar.gz: 9f3bc81ca0e1a0694733880217b6574e959e4eef538ab24bae28b081dafc962d763aa02214598bfa846603d9d83dbf497e1559a690e707655a7dc9cfae532544
@@ -1,6 +1,29 @@
1
1
  # Compute Unit Changelog
2
2
 
3
- ## Unrleased
3
+ ## 0.5.1
4
+ Released 12/30/2020
5
+
6
+ * Add cpu uuid and bios methods
7
+ * Remove formatters module
8
+ * Fix stack level when root? is used
9
+ * Ensure cpu power is an integer
10
+ ## 0.5.0
11
+ Released 10/5/2020
12
+
13
+ * Add more method docs
14
+ * Allow overriding of cpu power
15
+ * Update cpu name using model
16
+ * Add new search methods
17
+ * Add voltage and other cpu metrics
18
+ ## 0.4.0
19
+ * Adds ability to list attached processes to compute_unit sorted by field
20
+ * Adds ability to list top_x attached processes sorted by field
21
+
22
+ ## 0.3.3
23
+ * Fix bug with default database not being created
24
+
25
+ ## 0.3.2
26
+ * Fix bug with default database not being created
4
27
 
5
28
  ## 0.3.1
6
29
  Released 9/24/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'
@@ -138,7 +144,7 @@ Instead of monkey patching this code you can simply subclass the objects. The f
138
144
 
139
145
  ```ruby
140
146
  # coolproject/vegagpu.rb
141
- require 'cool_project/vega_gpu'
147
+ require 'compute_unit/gpu/amdgpu'
142
148
  module CoolProject
143
149
  class VegaGpu < ::ComputeUnit::Gpu::AmdGpu
144
150
  include CoolProject::Metrics
@@ -183,7 +189,7 @@ The opencl_ruby_ffi gem is optionally used to gather additional info about the G
183
189
 
184
190
  There is some caution when using OpenCL. If a GPU is dead, OpenCL tends to hang/freeze thus forcing a reboot. This is not a bug in the compute_unit or opencl_ruby_ffi gem but in the OpenCL library instead.
185
191
 
186
- Getting data from OpenCL also takes a long time! So if you have multiple GPUs in your system this could takes multiple seconds to return data. Because of this we cache the responses for the next query. This response is cached until the system checksum changes. This checksum is a special calculate of all the devices present in the system. So if you ever added or changed the equipment in the system, a new OpenCL query would ensue.
192
+ Getting data from OpenCL also takes a long time! So if you have multiple GPUs in your system this could takes multiple seconds to return data. Because of this we cache the responses for the next query. This response is cached until the system checksum changes. This checksum is a special calculatulation of all the devices present in the system. So if you ever added or changed the equipment in the system, a new OpenCL query would ensue.
187
193
 
188
194
  To enable OpenCL you just need to pass a boolean value to any of the find_all methods:
189
195
 
@@ -201,7 +207,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
201
207
 
202
208
  ## Contributing
203
209
 
204
- Bug reports and pull requests are welcome on GitHub at https://github.com/logicminds/compute_unit. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
210
+ Bug reports and pull requests are welcome on GitHub at https://gitlab.com/blockops/compute_unit. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
205
211
 
206
212
  ## License
207
213
 
@@ -209,4 +215,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
209
215
 
210
216
  ## Code of Conduct
211
217
 
212
- Everyone interacting in the ComputeUnit project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/logicminds/compute_unit/blob/master/CODE_OF_CONDUCT.md).
218
+ Everyone interacting in the ComputeUnit project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://gitlab.com/blockops/compute_unit/-/blob/master/CODE_OF_CONDUCT.md).
@@ -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'
@@ -11,7 +11,7 @@ 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/hwdata/pci.ids'
14
+ DEFAULT_PCIDB_PATH = '/usr/share/misc/pci.ids'
15
15
 
16
16
  # @param use_opencl [Boolean]
17
17
  # @return [Array] - return a list of compute units
@@ -20,16 +20,36 @@ module ComputeUnit
20
20
  require 'compute_unit/cpu'
21
21
  require 'compute_unit/asic'
22
22
 
23
- copy_default_database unless File.exist?(PCI_DATABASE_PATH)
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)
24
25
 
25
- ComputeUnit::Gpu.find_all(use_opencl) +
26
- ComputeUnit::Cpu.find_all +
27
- ComputeUnit::Asic.find_all
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
28
48
  end
29
49
 
30
50
  # copies the default pci database from linux filesystem over to the gem path
31
- def copy_default_database
32
- File.cp(DEFAULT_PCIDB_PATH, PCI_DATABASE_PATH)
51
+ def self.copy_default_database
52
+ FileUtils.cp(DEFAULT_PCIDB_PATH, PCI_DATABASE_PATH) if File.exist?(DEFAULT_PCIDB_PATH)
33
53
  end
34
54
 
35
55
  # get a fresh copy of the database and then use find_all
@@ -40,7 +60,7 @@ module ComputeUnit
40
60
  find_all(use_opencl)
41
61
  end
42
62
 
43
- # downloads the pci database
63
+ # downloads the pci database
44
64
  def self.refresh_pci_database
45
65
  ComputeUnit::Utils.check_for_root
46
66
  require 'net/http'
@@ -1,16 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'time'
4
- require 'compute_unit/formatters'
5
4
  require 'compute_unit/logger'
6
5
  require 'compute_unit/device'
6
+ require 'sys/proctable'
7
7
 
8
8
  module ComputeUnit
9
9
  class ComputeBase < Device
10
+ include Sys
11
+
10
12
  attr_reader :type, :serial, :meta, :uuid, :timestamp, :index, :compute_type
11
13
  attr_accessor :power_offset
12
14
 
13
- include ComputeUnit::Formatters
14
15
  include ComputeUnit::Logger
15
16
 
16
17
  # timeout value
@@ -26,6 +27,24 @@ module ComputeUnit
26
27
  end
27
28
  end
28
29
 
30
+ # @summary Finds all cpu attached processes and sorts by pctcpu
31
+ # param filter [Regex] - if supplied filter out devices from fd list
32
+ # @param field [Symbol] - the field to sort by
33
+ # @return [Array] - an array of attached processes
34
+ def attached_processes(field = :pctcpu, filter = nil)
35
+ raise NotImplementedError unless self.class.respond_to?(:attached_processes)
36
+
37
+ self.class.attached_processes(field, filter)
38
+ end
39
+
40
+ # @summary Find the processes consuming the most cpu
41
+ # @param x [Integer] the number of processes to return, defaults to 1
42
+ # @param field [Symbol] - the field to sort by
43
+ # @return [Array] - an array of attached processes
44
+ def top_processes(x = 1, field = :pctcpu)
45
+ attached_processes(field).last(x)
46
+ end
47
+
29
48
  # @param value [Float] a value to offset the power calculation, either a whole number, or decimal
30
49
  # @return [Integer] the value set as the offset
31
50
  def power_offset=(value)
@@ -52,24 +71,5 @@ module ComputeUnit
52
71
  def expired_metadata?
53
72
  (timestamp + CACHE_TIMEOUT) < Time.now.to_i
54
73
  end
55
-
56
- def experimental_on?
57
- unless ENV['XB_EXPERIMENTAL'].to_s == '1'
58
- logger.warn('You must set environment variable XB_EXPERIMENTAL=1 to use this feature')
59
- return false
60
- end
61
- true
62
- end
63
-
64
- def micro_formatter(item, add_unit = false)
65
- data = {}
66
- item.each do |key, value|
67
- if %i[hourly_cost hourly_earnings kwh_cost].include?(key)
68
- v = (value * 1_000_000).round(4)
69
- data[key] = add_unit ? "#{v} \u00B5BTC" : v
70
- end
71
- end
72
- item.merge(data)
73
- end
74
74
  end
75
75
  end
@@ -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,87 @@ module ComputeUnit
15
15
  end
16
16
  end
17
17
 
18
+ def bios
19
+ 'N/A'
20
+ end
21
+
22
+ def utilization
23
+ 1 # until we can calculate this. Is loadavg a good metric? or load / cpus
24
+ end
25
+
26
+ # @summary Finds all cpu attached processes and sorts by pctcpu
27
+ # @param field [Symbol] - the field to sort by
28
+ # @param filter [Regex] - if supplied filter out devices from fd list
29
+ # @return [Array] - an array of attached processes
30
+ def self.attached_processes(field = :pctcpu, _filter = nil)
31
+ Sys::ProcTable.ps(smaps: false).sort_by(&field)
32
+ end
33
+
34
+ # @summary [Float] - returns the voltage of the cpu
35
+ # @param processor_id [Integer] - the id of the cpu
36
+ def voltage(processor_id = 0)
37
+ file = "/dev/cpu/#{processor_id}/msr"
38
+ return 0 unless File.exist?(file)
39
+
40
+ # read 8 bytes, then unpack it by turning it into an integer
41
+ # make it a binary string, pluck out some specific bits, then
42
+ # convert it back to an integer
43
+ # divide by 8192 to give the voltage
44
+ # lowbit = 32
45
+ # highbit = 47
46
+ # bits = 47 - 32 + 1 # we want to read bits 32-47
47
+ msr = IO.new IO.sysopen(file, 'rb')
48
+ msr.sysseek(VOLTAGE_MSR)
49
+ data, = msr.sysread(8).unpack('q')
50
+ format('%.2f', ((data >> 32) / 8192.to_f))
51
+ end
52
+
53
+ # @return [String] - the model / name of the cpu
18
54
  def model
19
55
  raw_cpu_data[:model_name]
20
56
  end
21
57
 
58
+ # @return [String] - the model / name of the cpu
59
+ def name
60
+ model
61
+ end
62
+
63
+ # @return [String] - the maker of the cpu
22
64
  def make
23
65
  raw_cpu_data[:vendor_id]
24
66
  end
25
67
 
68
+ # @return [Integer] - the number of cores
26
69
  def num_cores
27
70
  raw_cpu_data[:cores_per_socket].to_i
28
71
  end
29
72
 
73
+ # @return [Integer] - the number of threads
30
74
  def num_threads
31
75
  raw_cpu_data[:threads_per_core].to_i
32
76
  end
33
77
 
78
+ # @return [Integer] - the number of cpus
34
79
  def nproc
35
80
  raw_cpu_data[:cpus].to_i
36
81
  end
37
82
 
83
+ # @return [Float] - current mhz of cpu
38
84
  def current_freq_mhz
39
85
  raw_cpu_data[:cpu_mhz].to_f.round(0)
40
86
  end
41
87
 
88
+ # @return [Float] - max mhz of cpu
42
89
  def max_freq_mhz
43
90
  raw_cpu_data[:cpu_max_mhz].to_f.round(0)
44
91
  end
45
92
 
93
+ # @return [Float] - current min of cpu
46
94
  def min_freq_mhz
47
95
  raw_cpu_data[:cpu_min_mhz].to_f.round(0)
48
96
  end
49
97
 
98
+ # @return [String] the path of the hwmon path for monitoring temps
50
99
  def base_hwmon_path
51
100
  File.join(ComputeUnit::SYSFS_PATH, 'devices/platform/coretemp.0/hwmon')
52
101
  end
@@ -68,8 +117,43 @@ module ComputeUnit
68
117
  read_hwmon_data('temp1_input', 0).to_f.round(0) / 1000
69
118
  end
70
119
 
120
+ def status
121
+ 0
122
+ end
123
+
124
+ def power
125
+ ENV.fetch('CPU_POWER', 60).to_i # until we can calculate power we will just use 60
126
+ end
127
+
128
+ def fan
129
+ 2500 # until we can calculate fan speed
130
+ end
131
+
132
+ def mem_temp
133
+ 0 # until we can get the mem temp
134
+ end
135
+
136
+ def pci_loc
137
+ device_path
138
+ end
139
+
140
+ def status_info
141
+ { index: uuid,
142
+ name: model,
143
+ bios: 'N/A',
144
+ core_clock: current_freq_mhz,
145
+ memory_clock: 'N/A',
146
+ power: power,
147
+ fan: fan,
148
+ core_volt: voltage,
149
+ temp: temp,
150
+ mem_temp: mem_temp,
151
+ status: status }
152
+ end
153
+
71
154
  def metrics
72
155
  {
156
+ uuid: uuid,
73
157
  temp: temp,
74
158
  minFreqMhz: min_freq_mhz,
75
159
  maxFreqMhz: max_freq_mhz,
@@ -87,6 +171,20 @@ module ComputeUnit
87
171
  super.merge(metrics)
88
172
  end
89
173
 
174
+ def initialize(_device_path, opts)
175
+ super
176
+ @type = :CPU
177
+ @pci_loc = device_path
178
+ @index = opts[:index].to_i
179
+ @power_offset = 0
180
+ @uuid = opts[:uuid] || opts[:serial]
181
+ end
182
+
183
+ # @return [String] - the type and index of the device
184
+ def uuid
185
+ @uuid ||= "#{type}#{index}"
186
+ end
187
+
90
188
  def self.create_from_path(device_path, index)
91
189
  opts = {
92
190
  device_class_id: device_class(device_path),
@@ -99,7 +197,7 @@ module ComputeUnit
99
197
  new(device_path, opts)
100
198
  end
101
199
 
102
- def self.find_all
200
+ def self.find_all(_use_opencl = false)
103
201
  devices.sort.map.with_index do |device_path, index|
104
202
  create_from_path(device_path, index)
105
203
  end
@@ -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
@@ -11,7 +11,7 @@ module ComputeUnit
11
11
 
12
12
  # @return [Boolean] - return true if the current user is root
13
13
  def root?
14
- root?
14
+ ::Etc.getpwuid.name == 'root'
15
15
  end
16
16
 
17
17
  # @return [Boolean] - returns true if user is root
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ComputeUnit
4
- VERSION = '0.3.1'
4
+ VERSION = '0.5.1'
5
5
  end
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.3.1
4
+ version: 0.5.1
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-09-24 00:00:00.000000000 Z
11
+ date: 2020-12-30 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
@@ -105,7 +125,6 @@ files:
105
125
  - lib/compute_unit/cpu.rb
106
126
  - lib/compute_unit/device.rb
107
127
  - lib/compute_unit/exceptions.rb
108
- - lib/compute_unit/formatters.rb
109
128
  - lib/compute_unit/gpu.rb
110
129
  - lib/compute_unit/gpus/amd_gpu.rb
111
130
  - lib/compute_unit/gpus/nvidia_gpu.rb
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ComputeUnit
4
- module Formatters
5
- def micro_formatter(item, add_unit = false)
6
- data = {}
7
- item.each do |key, value|
8
- if %i[hourly_cost hourly_earnings kwh_cost].include?(key)
9
- v = (value * 1000000).round(4)
10
- data[key] = add_unit ? "#{v} \u00B5BTC" : v
11
- end
12
- end
13
- item.merge(data)
14
- end
15
-
16
- def value_micro_formatter(value, add_unit = false)
17
- v = (value * 1000000).round(1)
18
- add_unit ? "#{v} \u00B5BTC" : v
19
- end
20
- end
21
- end