Secondly, the polling time was far too slow for my taste, so changed it to 4 seconds.
Thirdly the temperature adjustment was not to my liking so I've tied it to the actual temperature of the hottest gpu on a card. To do this I've removed the low temp and high temp variables and added a new one called fan_overTemp. My default setting is '10', which thus sets the fanspeed at ten percent over the temperature in degrees C. ==> 70C = 80% fan speed; 80C = 90% fanspeed. This is a simple mechanism but quite effective for me. It of course maxes out at 100%.
Happy mining
# -*- coding: utf-8 -*-
import sys
import time
import re
import os
import subprocess
import psutil
import signal
from string import split
settings = {
'poll_time': 4,
'fan_overTemp': 10,
'temp_fan_100': 90.0,
'temp_cooldown': 93.0,
'cooldown_period': 10,
'ati_card_exclude': '4250',
'aticonfig_path': '/usr/bin/aticonfig'
}
class GPU():
def __init__(self, id, cardid, description):
self.id = id
self.cardid = cardid
self.description = description
def temp(self):
os.environ['DISPLAY'] = ":0"
try:
line = subprocess.check_output(
[settings['aticonfig_path'],
"--adapter=" + str(self.id),
"--odgt"])
m = re.search('Temperature - (\d+\.\d+)\s*C', line)
if m is None:
return None
return float(m.group(1))
except OSError:
return None
class Card():
def __init__(self, id, gpus):
self.id = id
self.x11_id = ':0.' + str(id)
self.gpus = gpus
def fanspeed(self, val=None):
os.environ['DISPLAY'] = self.x11_id
if val is not None:
subprocess.call( [settings['aticonfig_path'],
"--pplib-cmd", "set fanspeed 0 " + str(val)])
else:
line = subprocess.check_output(
[settings['aticonfig_path'],
"--pplib-cmd", "get fanspeed 0"])
m = re.search('Fan Speed: (\d+)%', line)
if m is None:
return False
return int(m.group(1))
class Watcher():
def __init__(self):
self.cards = []
os.environ['DISPLAY'] = ":0"
out = subprocess.check_output([settings['aticonfig_path'],
"--list-adapters"])
if settings['ati_card_exclude'] == '':
card = 0
else: card = 1
gpu = 0
lines = split(out, "\n")
for line in lines:
r = re.search('^[ \*]+(\d+)\. [\d:\.]+ (.+)$', line)
if r is None:
continue
gpuid = int(r.group(1))
desc = r.group(2)
if settings['ati_card_exclude'] in desc:
continue
if gpu % 2 == 0:
self.cards.append(Card(card, []))
self.cards[-1].gpus.append(GPU(gpuid, card, desc))
print "gpu %d card %d desc %s" % (gpuid, card, desc)
if gpu % 2 == 1:
card = card + 1
gpu = gpu + 1
def watch(self):
while True:
for card in self.cards:
if card is None:
continue
self.watch_card(card)
time.sleep(settings['poll_time'])
# print "----------------------------------"
def watch_card(self, card):
fanspeed = card.fanspeed()
fandelta = 0
print "Card %d: fan speed %d%%" % (card.id, fanspeed)
for gpu in card.gpus:
temp = gpu.temp()
# print "Card %d GPU %d: Temperature %.2f °C" % (
# card.id, gpu.id, temp)
if temp >= settings['temp_cooldown']:
for pid in psutil.get_pid_list():
try:
p = psutil.Process(pid)
if len(p.cmdline) > 2 and 'poclbm' in p.cmdline[1]:
n = int(p.cmdline[2][2])
if n == gpu.id:
print "Suspending GPU %d for 10 seconds" % (n)
os.kill(p.pid, signal.SIGSTOP)
time.sleep(settings['cooldown_period'])
os.kill(p.pid, signal.SIGCONT)
except:
pass
if gpu.id % 2 != 0 : # first gpu on card - usually the cooler one
fanspeed2 = int(temp) + settings['fan_overTemp']
continue #check the temp on the second gpu of this card
else: # second gpu on card
fanspeed = int(temp) + settings['fan_overTemp']
if fanspeed2 > fanspeed: # use the highest speed
fanspeed = fanspeed2
if fanspeed > 100:
fanspeed = 100
card.fanspeed(fanspeed)
if __name__ == '__main__':
if len(sys.argv) > 1:
try:
f = open(sys.argv[1])
for line in f:
m = re.search('^(\w+)\s*=\s*(\S.*)$', line)
if m is None:
continue
settings[m.group(1)] = m.group(2)
f.close()
except (OSError, IOError), e:
pass
settings['poll_time'] = int(settings['poll_time'])
print repr(settings)
w = Watcher()
w.watch()