Skip to content

Commit e81414a

Browse files
bethune-bryantwookayin
authored andcommitted
Add a flag for fan speed (wookayin#63)
* Add a flag for fan speed. * Addressing review comments and fixing ci failure. * Update fan flag in readme.
1 parent 28299cd commit e81414a

File tree

4 files changed

+39
-7
lines changed

4 files changed

+39
-7
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Options:
2828
* `-u`, `--show-user` : Display username of the process owner
2929
* `-c`, `--show-cmd` : Display the process name
3030
* `-p`, `--show-pid` : Display PID of the process
31+
* `-F`, `--show-fan` : Display GPU fan speed
3132
* `-P`, `--show-power` : Display GPU power usage and/or limit (`draw` or `draw,limit`)
3233
* `--watch`, `-i`, `--interval` : Run in watch mode (equivalent to `watch gpustat`) if given. Denotes interval between updates. ([#41][gh-issue-41])
3334
* `--json` : JSON Output (Experimental, [#10][gh-issue-10])

gpustat/__main__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ def main(*argv):
7979
help='Display username of running process')
8080
parser.add_argument('-p', '--show-pid', action='store_true',
8181
help='Display PID of running process')
82+
parser.add_argument('-F', '--show-fan', action='store_true',
83+
help='Display GPU fan speed')
8284
parser.add_argument('--json', action='store_true', default=False,
8385
help='Print all the information in JSON format')
8486
parser.add_argument('-v', '--version', action='version',

gpustat/core.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,15 @@ def temperature(self):
107107
v = self.entry['temperature.gpu']
108108
return int(v) if v is not None else None
109109

110+
@property
111+
def fan(self):
112+
"""
113+
Returns the fan percentage of GPU as an integer,
114+
or None if the information is not available.
115+
"""
116+
v = self.entry['fan.speed']
117+
return int(v) if v is not None else None
118+
110119
@property
111120
def utilization(self):
112121
"""
@@ -147,6 +156,7 @@ def print_to(self, fp,
147156
show_user=False,
148157
show_pid=False,
149158
show_power=None,
159+
show_fan=None,
150160
gpuname_width=16,
151161
term=Terminal(),
152162
):
@@ -165,6 +175,8 @@ def _conditional(cond_fn, true_value, false_value,
165175
colors['CName'] = term.blue
166176
colors['CTemp'] = _conditional(lambda: self.temperature < 50,
167177
term.red, term.bold_red)
178+
colors['FSpeed'] = _conditional(lambda: self.fan < 50,
179+
term.yellow, term.bold_yellow)
168180
colors['CMemU'] = term.bold_yellow
169181
colors['CMemT'] = term.yellow
170182
colors['CMemP'] = term.yellow
@@ -189,8 +201,12 @@ def _repr(v, none_value='??'):
189201
# temperature and utilization
190202
reps = "%(C1)s[{entry[index]}]%(C0)s " \
191203
"%(CName)s{entry[name]:{gpuname_width}}%(C0)s |" \
192-
"%(CTemp)s{entry[temperature.gpu]:>3}'C%(C0)s, " \
193-
"%(CUtil)s{entry[utilization.gpu]:>3} %%%(C0)s"
204+
"%(CTemp)s{entry[temperature.gpu]:>3}'C%(C0)s, "
205+
206+
if show_fan:
207+
reps += "%(FSpeed)s{entry[fan.speed]:>3} %%%(C0)s, "
208+
209+
reps += "%(CUtil)s{entry[utilization.gpu]:>3} %%%(C0)s"
194210

195211
if show_power:
196212
reps += ", %(CPowU)s{entry[power.draw]:>3}%(C0)s "
@@ -300,6 +316,11 @@ def get_process_info(nv_process):
300316
except N.NVMLError:
301317
temperature = None # Not supported
302318

319+
try:
320+
fan_speed = N.nvmlDeviceGetFanSpeed(handle)
321+
except N.NVMLError:
322+
fan_speed = None # Not supported
323+
303324
try:
304325
memory = N.nvmlDeviceGetMemoryInfo(handle) # in Bytes
305326
except N.NVMLError:
@@ -354,6 +375,7 @@ def get_process_info(nv_process):
354375
'uuid': uuid,
355376
'name': name,
356377
'temperature.gpu': temperature,
378+
'fan.speed': fan_speed,
357379
'utilization.gpu': utilization.gpu if utilization else None,
358380
'power.draw': power // 1000 if power is not None else None,
359381
'enforced.power.limit': power_limit // 1000
@@ -403,7 +425,7 @@ def __repr__(self):
403425

404426
def print_formatted(self, fp=sys.stdout, force_color=False, no_color=False,
405427
show_cmd=False, show_user=False, show_pid=False,
406-
show_power=None, gpuname_width=16,
428+
show_power=None, show_fan=None, gpuname_width=16,
407429
show_header=True,
408430
eol_char=os.linesep,
409431
**kwargs
@@ -453,6 +475,7 @@ def print_formatted(self, fp=sys.stdout, force_color=False, no_color=False,
453475
show_user=show_user,
454476
show_pid=show_pid,
455477
show_power=show_power,
478+
show_fan=show_fan,
456479
gpuname_width=gpuname_width,
457480
term=t_color)
458481
fp.write(eol_char)

gpustat/test_gpustat.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ def _decorated(*args, **kwargs):
8181
mock_handles[2]: 71,
8282
}.get(handle, RuntimeError))
8383

84+
N.nvmlDeviceGetFanSpeed = _raise_ex(lambda handle: {
85+
mock_handles[0]: 16,
86+
mock_handles[1]: 53,
87+
mock_handles[2]: 100,
88+
}.get(handle, RuntimeError))
89+
8490
N.nvmlDeviceGetPowerUsage = _raise_ex(lambda handle: {
8591
mock_handles[0]: 125000,
8692
mock_handles[1]: N.NVMLError_NotSupported(), # Not Supported
@@ -154,9 +160,9 @@ def _MockedProcess(pid):
154160
""" # noqa: E501
155161

156162
MOCK_EXPECTED_OUTPUT_FULL = """\
157-
[0] GeForce GTX TITAN 0 | 80'C, 76 %, 125 / 250 W | 8000 / 12287 MB | user1:python/48448(4000M) user2:python/153223(4000M)
158-
[1] GeForce GTX TITAN 1 | 36'C, 0 %, ?? / 250 W | 9000 / 12189 MB | user1:torch/192453(3000M) user3:caffe/194826(6000M)
159-
[2] GeForce GTX TITAN 2 | 71'C, ?? %, 250 / ?? W | 0 / 12189 MB | (Not Supported)
163+
[0] GeForce GTX TITAN 0 | 80'C, 16 %, 76 %, 125 / 250 W | 8000 / 12287 MB | user1:python/48448(4000M) user2:python/153223(4000M)
164+
[1] GeForce GTX TITAN 1 | 36'C, 53 %, 0 %, ?? / 250 W | 9000 / 12189 MB | user1:torch/192453(3000M) user3:caffe/194826(6000M)
165+
[2] GeForce GTX TITAN 2 | 71'C, 100 %, ?? %, 250 / ?? W | 0 / 12189 MB | (Not Supported)
160166
""" # noqa: E501
161167

162168

@@ -195,7 +201,7 @@ def test_new_query_mocked(self, N, Process):
195201
fp = StringIO()
196202
gpustats.print_formatted(
197203
fp=fp, no_color=False, show_user=True,
198-
show_cmd=True, show_pid=True, show_power=True
204+
show_cmd=True, show_pid=True, show_power=True, show_fan=True
199205
)
200206

201207
result = fp.getvalue()

0 commit comments

Comments
 (0)