compute_unit 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5caf721411458c1307f2714ba330c737ddd5987433a51d7753b6004649a92429
4
- data.tar.gz: ef3bacd1c4662e1ffe33dafad9c48f44c6fc3cefe161098957cc476e7ecac78d
3
+ metadata.gz: 0ce131c209f99ed5bd86a2439e56b2fcd8e0d7b85120db149d3c4427e6ea8ef0
4
+ data.tar.gz: 478ef837c6b68adecfc84bb6adc4f6120a8f8e119e371ce13f3503808e92788f
5
5
  SHA512:
6
- metadata.gz: 73b107433ce4ffcb3d6a5d35a8711cf39489ed111ccc368cb66d5f788f36690382242a3437c0bd6d40147c2ca727856d2fffd48b8b5edbaa8f4dd22b5ab228da
7
- data.tar.gz: 2debc1c284e7200efd00ca0e064d3b48f55a935d55409cc77f33db2db9344efabcdaadc3cd2bfd909174e58ba38727ed4ed845dad0395a19e08dc5ee0f54a428
6
+ metadata.gz: 9f1f22c7a80b053fb7612bb0e40bd7f2ea609b4c747dcdf486875289d2edab9090d590ca333d33be763cc3cec55faa6ca3a8568dc8f8f212eea3adf3e629120b
7
+ data.tar.gz: 11b96609eec70c967865ecb476367210a356dfdec6a9b833929c4992e937ad8f568cdf5a01e4f1a7fa77cdcf941a245d20a55a5704385b7de1c4efc11855605d
@@ -1,3 +1,10 @@
1
1
  # Compute Unit Changelog
2
2
 
3
+ ## Unrleased
4
+ ## 0.2.0
5
+ Released 6/5/2020
6
+ * Added more cpu methods for various POI.
7
+ * Ensure constants exist when side loaded
8
+ * Add descendent discovery mechanism to GPU class
9
+
3
10
  ## initial version 0.1.0
data/README.md CHANGED
@@ -40,6 +40,34 @@ devices = ComputeUnit.find_all
40
40
 
41
41
  ```
42
42
 
43
+ ### Subclassing a compute object
44
+ Instead of monkey patching this code you can simply subclass the objects. The find_all methods are intelligent enough to locate your subclass by searching the Objectspace for all decendants of `ComputeUnit::ComputeBase`. This makes it dead simple to add new functionality without having to wrap any code. This gives you the ability to include new modules or override existing methods.
45
+
46
+ ```ruby
47
+ # coolproject/vegagpu.rb
48
+ module CoolProject
49
+ class VegaGpu < ::ComputeUnit::Gpu::AmdGpu
50
+ include CoolProject::Metrics
51
+
52
+ def initialize(device_path, opts)
53
+ super(device_path, opts)
54
+ @is_vega = true
55
+ end
56
+ end
57
+ end
58
+
59
+ ```
60
+
61
+ Once you have your new class created all you need to do is load the class before running find all.
62
+
63
+ ```
64
+ require 'compute_unit'
65
+ require 'coolproject/vegagpu'
66
+ require 'yaml'
67
+
68
+ puts ComputeUnit.find_all.to_yaml
69
+
70
+ ```
43
71
 
44
72
  ## Make and Model information
45
73
  This library uses the pci database maintained here https://pci-ids.ucw.cz/ which is also used by `lspci` and other linux tools. If your device is giving the wrong info the problem is likely due to the database being incorrect.
@@ -51,9 +79,10 @@ If you want to refresh the database every time with finding all the devices you
51
79
  Or you can also run the simple cli tool. `update_pcidb` that comes with this library.
52
80
 
53
81
  ## Kernel support
54
- There will likely be changes with reading and writing information to sysfs as new kerneles are released. This library has been tested against.
82
+ There will likely be changes with reading and writing information to sysfs as new kernels are released. This library has been tested against.
55
83
 
56
84
  * 4.15 - 4.20
85
+ * 5.x
57
86
 
58
87
  ## Development
59
88
 
@@ -9,6 +9,25 @@ module ComputeUnit
9
9
  []
10
10
  end
11
11
 
12
+ def self.create_from_path(device_path, index)
13
+ opts = {
14
+ device_class_id: device_class(device_path),
15
+ device_id: device(device_path),
16
+ device_vendor_id: device_vendor(device_path),
17
+ subsystem_vendor_id: subsystem_vendor(device_path),
18
+ subsystem_device_id: subsystem_device(device_path),
19
+ index: index
20
+ }
21
+ new(device_path, opts)
22
+ end
23
+
24
+ def self.find_all
25
+ return []
26
+ devices.sort.map.with_index do |device_path, index|
27
+ create_from_path(device_path, index)
28
+ end
29
+ end
30
+
12
31
  # /sys/devices/platform $ more coretemp.0/hwmon/hwmon1/temp1_input / 1000
13
32
  end
14
33
  end
@@ -16,6 +16,16 @@ module ComputeUnit
16
16
  # timeout value
17
17
  CACHE_TIMEOUT = 30
18
18
 
19
+ # @return [Array] - find all the decendants of thyself
20
+ def self.compute_classes
21
+ ObjectSpace.each_object(Class).select do |klass|
22
+ # <Class:#<Crossbelt::ComputeUnit::NvidiaGpu:0x00007fddc5c02a10>>
23
+ # We have to filter out these kinds of Ojbects as they don't respond to :new
24
+ # without a singleton error.
25
+ klass < self && !klass.to_s.include?('Class')
26
+ end
27
+ end
28
+
19
29
  # @param value [Float] a value to offset the power calculation, either a whole number, or decimal
20
30
  # @return [Integer] the value set as the offset
21
31
  def power_offset=(value)
@@ -13,6 +13,78 @@ module ComputeUnit
13
13
  end
14
14
  end
15
15
 
16
+ def model
17
+ raw_cpu_data[:model_name]
18
+ end
19
+
20
+ def make
21
+ raw_cpu_data[:vendor_id]
22
+ end
23
+
24
+ def num_cores
25
+ raw_cpu_data[:cores_per_socket].to_i
26
+ end
27
+
28
+ def num_threads
29
+ raw_cpu_data[:threads_per_core].to_i
30
+ end
31
+
32
+ def nproc
33
+ raw_cpu_data[:cpus].to_i
34
+ end
35
+
36
+ def current_freq_mhz
37
+ raw_cpu_data[:cpu_mhz].to_f.round(0)
38
+ end
39
+
40
+ def max_freq_mhz
41
+ raw_cpu_data[:cpu_max_mhz].to_f.round(0)
42
+ end
43
+
44
+ def min_freq_mhz
45
+ raw_cpu_data[:cpu_min_mhz].to_f.round(0)
46
+ end
47
+
48
+ def base_hwmon_path
49
+ File.join(ComputeUnit::SYSFS_PATH, 'devices/platform/coretemp.0/hwmon')
50
+ end
51
+
52
+ # @return [Hash] - a hash of temp readings and their labels
53
+ # @example temps => {:core_0=>31, :core_1=>31, :package_id_0=>27}
54
+ def temps
55
+ Dir.glob(File.join(hwmon_path, 'temp*_label')).each_with_object({}) do |label_file, acc|
56
+ temp_file = label_file.sub('label', 'input')
57
+ label = normalize_name(read_file(label_file))
58
+ reading = read_file(temp_file).to_f.round(0) / 1000
59
+ acc[label] = reading
60
+ acc
61
+ end
62
+ end
63
+
64
+ # @return [Integer] - the temperature of the cpu package in Celsius
65
+ def temp
66
+ read_hwmon_data('temp1_input', 0).to_f.round(0) / 1000
67
+ end
68
+
69
+ def metrics
70
+ {
71
+ temp: temp,
72
+ minFreqMhz: min_freq_mhz,
73
+ maxFreqMhz: max_freq_mhz,
74
+ currentFreqMhz: current_freq_mhz,
75
+ model: model,
76
+ make: make,
77
+ numCores: num_cores,
78
+ numThreads: num_threads,
79
+ nproc: nproc,
80
+ temps: temps
81
+ }
82
+ end
83
+
84
+ def to_h
85
+ super.merge(metrics)
86
+ end
87
+
16
88
  def self.create_from_path(device_path, index)
17
89
  opts = {
18
90
  device_class_id: device_class(device_path),
@@ -26,11 +98,51 @@ module ComputeUnit
26
98
  end
27
99
 
28
100
  def self.find_all
29
- return [] # not quite ready to show cpu data
30
101
  devices.sort.map.with_index do |device_path, index|
31
102
  create_from_path(device_path, index)
32
103
  end
33
104
  end
34
- # /sys/devices/platform $ more coretemp.0/hwmon/hwmon1/temp1_input / 1000
105
+
106
+ private
107
+
108
+ # @param value [String]
109
+ # @return [String]
110
+ # @summary downcases and transforms spaces into underscores
111
+ def normalize_name(value)
112
+ value.downcase.gsub(/\s/, '_').gsub('(s)', 's').to_sym
113
+ end
114
+
115
+ # @return [Hash] - a hash of cpu info returned from lscpu
116
+ # normalizes the key name by downcasing and removing spaces
117
+ # @example
118
+ # raw_cpu_data() =>
119
+ # {"architecture"=>"x86_64", "cpu_op-modes"=>"32-bit, 64-bit", "byte_order"=>"Little Endian",
120
+ # "cpus"=>"4", "on-line_cpus list"=>"0-3", "threads_per core"=>"2", "cores_per socket"=>"2",
121
+ # "sockets"=>"1", "numa_nodes"=>"1", "vendor_id"=>"GenuineIntel", "cpu_family"=>"6",
122
+ # "model"=>"158", "model_name"=>"Intel(R) Core(TM) i3-7100 CPU @ 3.90GHz", "stepping"=>"9",
123
+ # "cpu_mhz"=>"800.545", "cpu_max mhz"=>"3900.0000", "cpu_min mhz"=>"800.0000",
124
+ # "bogomips"=>"7799.87", "virtualization"=>"VT-x", "l1d_cache"=>"32K", "l1i_cache"=>"32K",
125
+ # "l2_cache"=>"256K", "l3_cache"=>"3072K", "numa_node0 cpus"=>"0-3",
126
+ # "flags"=>"fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36
127
+ # clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc
128
+ # art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni
129
+ # pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1
130
+ # sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm
131
+ # 3dnowprefetch cpuid_fault invpcid_single tpr_shadow vnmi flexpriority ept vpid ept_ad
132
+ # fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx rdseed adx smap clflushopt intel_pt
133
+ # xsaveopt xsavec xgetbv1 xsaves dtherm arat pln pts hwp hwp_notify hwp_act_window hwp_epp"
134
+ # }
135
+ def raw_cpu_data
136
+ @raw_cpu_data ||= begin
137
+ # turns each line into a hash of key value pairs
138
+ regex = /(?<name>[\s\w\-\(\)]+)\:\s+(?<value>[@\s\w\-\,\.\(\)]+)$/
139
+ `lscpu`.lines.each_with_object({}) do |line, acc|
140
+ m = line.match(regex)
141
+ name = normalize_name(m.named_captures['name'])
142
+ acc[name] = m.named_captures['value'].chomp if m
143
+ acc
144
+ end
145
+ end
146
+ end
35
147
  end
36
148
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'compute_unit'
3
4
  require 'compute_unit/logger'
4
5
  require 'compute_unit/cache_store'
5
6
  require 'compute_unit/utils'
6
-
7
7
  # This file supports reading from sysfs
8
8
  # More information about sysfs can be found here - https://www.kernel.org/doc/Documentation/filesystems/sysfs-pci.txt
9
9
  module ComputeUnit
@@ -127,7 +127,10 @@ module ComputeUnit
127
127
 
128
128
  def read_file(path, default = nil)
129
129
  File.read(path).chomp
130
- rescue Errno::EINVAL, Errno::EPERM, Errno::ENOENT
130
+ rescue Errno::EINVAL, Errno::EPERM
131
+ default
132
+ rescue Errno::ENOENT
133
+ logger.warn("File #{path} does not exist, using defaults")
131
134
  default
132
135
  rescue Errno::EACCES
133
136
  logger.fatal('run this command as root or with sudo, using default value')
@@ -268,7 +271,7 @@ module ComputeUnit
268
271
  logger.fatal(e.message)
269
272
  default
270
273
  rescue Errno::ENOENT
271
- logger.fatal("File #{path} does not exist")
274
+ logger.warn("File #{path} does not exist")
272
275
  default
273
276
  rescue Errno::EACCES
274
277
  logger.fatal('Run this command as root or with sudo')
@@ -286,7 +289,7 @@ module ComputeUnit
286
289
  rescue Errno::EINVAL, Errno::EPERM => e
287
290
  logger.fatal(e.message)
288
291
  rescue Errno::ENOENT
289
- logger.fatal("File #{path} does not exist")
292
+ logger.warn("File #{path} does not exist")
290
293
  rescue Errno::EACCES
291
294
  logger.fatal('Run this command as root or with sudo')
292
295
  end
@@ -265,7 +265,7 @@ module ComputeUnit
265
265
  def self.find_all(use_opencl = false)
266
266
  require 'compute_unit/gpus/amd_gpu'
267
267
  require 'compute_unit/gpus/nvidia_gpu'
268
- g = ComputeUnit::AmdGpu.find_all(use_opencl) + ComputeUnit::NvidiaGpu.find_all(use_opencl)
268
+ g = compute_classes.map { |klass| klass.find_all(use_opencl) }.flatten
269
269
  g.sort_by(&:index)
270
270
  end
271
271
 
@@ -90,6 +90,16 @@ module ComputeUnit
90
90
  utilization
91
91
  end
92
92
 
93
+ # @return [Integer] - the temperature of the asic chip
94
+ def asic_temp
95
+ read_hwmon_data('temp2_input', 0).to_i / 1000
96
+ end
97
+
98
+ # @return [Integer] - temperature of the memory
99
+ def mem_temp
100
+ read_hwmon_data('temp3_input', 0).to_i / 1000
101
+ end
102
+
93
103
  # @return [Integer] - returns temp of gpu in celius
94
104
  def temp
95
105
  read_hwmon_data('temp1_input', 0).to_i / 1000
@@ -421,16 +431,6 @@ module ComputeUnit
421
431
  vddci_voltage_table.first[:volt]
422
432
  end
423
433
 
424
- # @return [Integer] - the temperature of the asic chip
425
- def asic_temp
426
- read_hwmon_data('temp2_input', 0).to_i / 1000
427
- end
428
-
429
- # @return [Integer] - the temperature of the memory chips
430
- def mem_temp
431
- read_hwmon_data('temp3_input', 0).to_i / 1000
432
- end
433
-
434
434
  def set_mem_clock_and_vddc(mem_clock, mem_volt)
435
435
  return unless experimental_on?
436
436
 
@@ -8,7 +8,7 @@ module ComputeUnit
8
8
  if ENV['LOG_FILENAME'] && File.exist?(ENV['LOG_FILENAME'])
9
9
  ENV['LOG_FILENAME']
10
10
  else
11
- STDOUT
11
+ STDERR
12
12
  end
13
13
  end
14
14
 
@@ -18,7 +18,7 @@ module ComputeUnit
18
18
  log.level = log_level
19
19
  log.progname = 'ComputeUnit'
20
20
  log.formatter = proc do |severity, datetime, progname, msg|
21
- if Logger.log_file == STDOUT
21
+ if Logger.log_file == STDERR
22
22
  "#{severity} - #{progname}: #{msg}\n".send(color(severity))
23
23
  else
24
24
  "#{datetime} #{severity} - #{progname}: #{msg}\n".send(color(severity))
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ComputeUnit
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: compute_unit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.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-05-25 00:00:00.000000000 Z
11
+ date: 2020-06-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: opencl_ruby_ffi