Commit af71d06a authored by Azat Khuziyakhmetov's avatar Azat Khuziyakhmetov
Browse files

faster attribute computation

parent 5d60482d
...@@ -13,27 +13,60 @@ def printd(*args, **kargs): ...@@ -13,27 +13,60 @@ def printd(*args, **kargs):
def rcmattr(func): def rcmattr(func):
'''Function decorator. Checks and sets arguments''' '''Function decorator. Checks and sets arguments'''
def wrapper(Aggr, *args, **kwargs): def wrapper(*args, **kwargs):
if type(Aggr) != Aggregator:
raise TypeError("argument of attribute should be Aggregator")
printd(" - evaluation of the attribute {:s} - ".format(func.__name__)) printd(" - evaluation of the attribute {:s} - ".format(func.__name__))
res = func(Aggr, *args, **kwargs) res = func(*args, **kwargs)
printd(" - finished the attribute {:s} ({}) - ".format(func.__name__, res)) printd(" - finished the attribute {:s} ({}) - ".format(func.__name__, res))
return res return res
wrapper.decorator = rcmattr
wrapper.__name__ = func.__name__
wrapper.__doc__ = func.__doc__
return wrapper return wrapper
class Attributes: class Attributes:
_aggr = None
attr_values = {}
def __init__(self, Aggr):
# init function
if type(Aggr) != Aggregator:
raise TypeError("argument for init should be Aggregator")
self._aggr = Aggr
return
def calculate_attrs(self):
attr_func = self.get_attribute_funcs()
for func in attr_func:
self.attr_values[func.__name__] = func()
return self.attr_values
def get_attribute_funcs(self):
"""
Returns all methods with attribute decorator
"""
methods = []
for maybeDecorated in dir(self):
cur_attr = self.__getattribute__(maybeDecorated)
if hasattr(cur_attr, 'decorator'):
if cur_attr.decorator is rcmattr:
methods.append(cur_attr)
return methods
@rcmattr @rcmattr
def cpu_usage_total_max(Aggr): def cpu_usage_total_max(self):
cpu_usage = 0 cpu_usage = 0
cpu_usage_sum = 0 cpu_usage_sum = 0
alloc_cu_sum = 0 alloc_cu_sum = 0
cpu_requested = Aggr.job.requested_cu cpu_requested = self._aggr.job.requested_cu
for node in Aggr.nodes: for node in self._aggr.nodes:
thr_cnt = node.virt_thr_core + node.phys_thr_core thr_cnt = node.virt_thr_core + node.phys_thr_core
cpu_usage_sum += node.proc.cpu_usage cpu_usage_sum += node.proc.cpu_usage
...@@ -47,45 +80,42 @@ class Attributes: ...@@ -47,45 +80,42 @@ class Attributes:
res = "NORM" res = "NORM"
if cpu_usage < 50 and alloc_cu_sum <= 8: if cpu_usage < 50 and alloc_cu_sum <= 8:
res = "LOW" res = "LOW"
break
elif cpu_usage < 80 and alloc_cu_sum > 8: elif cpu_usage < 80 and alloc_cu_sum > 8:
res = "LOW" res = "LOW"
break
return res return res
@rcmattr @rcmattr
def mem_usage_total(Aggr): def mem_usage_total(self):
MEMDIFF_HIGH = 1024*1024*1024 MEM_LOW_BOUND = 32*1024*1024*1024 # 32 GiB
mem_req_total = 0 mem_req_total = 0
mem_used_max = 0 mem_used_max = 0
for node in Aggr.nodes: for node in self._aggr.nodes:
mem_per_core = node.main_mem / (node.sockets * node.cores_per_socket) thr_cnt = node.virt_thr_core + node.phys_thr_core
mem_req_total += mem_per_core * node.alloc_cu mem_per_core = node.main_mem / (node.sockets * node.cores_per_socket * thr_cnt)
mem_req_total += mem_per_core * node.alloc_cu
mem_used_max += node.proc.mem_rss_max mem_used_max += node.proc.mem_rss_max
mem_ratio = mem_used_max / mem_req_total mem_ratio = mem_used_max / mem_req_total
mem_diff = abs(mem_used_max - mem_req_total)
printd("mem total = {}, mem used {}, ratio {}". printd("mem total = {}, mem used {}, ratio {}".
format(mem_req_total, mem_used_max, mem_ratio)) format(mem_req_total, mem_used_max, mem_ratio))
res = "MED" res = "NORM"
if mem_ratio > 1 and mem_diff > MEMDIFF_HIGH: if mem_used_max > MEM_LOW_BOUND and mem_ratio > 1:
res = "HIGH" res = "HIGH"
elif mem_ratio < 0.5 and mem_diff > MEMDIFF_HIGH: elif mem_used_max > MEM_LOW_BOUND and mem_ratio < 0.3:
res = "LOW" res = "LOW"
return res return res
@rcmattr @rcmattr
def node_cpu_usage_max(Aggr): def node_cpu_usage_max(self):
res = ["ZERO", "LOW", "NORM", "HIGH"] res = ["ZERO", "LOW", "NORM", "HIGH"]
max_res = res.index("ZERO") max_res = res.index("ZERO")
for node in Aggr.nodes: for node in self._aggr.nodes:
thr_cnt = node.virt_thr_core + node.phys_thr_core thr_cnt = node.virt_thr_core + node.phys_thr_core
node_proc_ratio = node.proc.cpu_usage / (node.alloc_cu / thr_cnt) node_proc_ratio = node.proc.cpu_usage / (node.alloc_cu / thr_cnt)
...@@ -107,10 +137,11 @@ class Attributes: ...@@ -107,10 +137,11 @@ class Attributes:
return res[max_res] return res[max_res]
def node_cpu_usage_min(Aggr): @rcmattr
def node_cpu_usage_min(self):
res = ["ZERO", "LOW", "NORM", "HIGH"] res = ["ZERO", "LOW", "NORM", "HIGH"]
min_res = res.index("HIGH") min_res = res.index("HIGH")
for node in Aggr.nodes: for node in self._aggr.nodes:
thr_cnt = node.virt_thr_core + node.phys_thr_core thr_cnt = node.virt_thr_core + node.phys_thr_core
node_proc_ratio = node.proc.cpu_usage / (node.alloc_cu / thr_cnt) node_proc_ratio = node.proc.cpu_usage / (node.alloc_cu / thr_cnt)
...@@ -128,20 +159,20 @@ class Attributes: ...@@ -128,20 +159,20 @@ class Attributes:
elif node_proc_ratio > 80 and node.alloc_cu > 8: elif node_proc_ratio > 80 and node.alloc_cu > 8:
node_res = res.index("NORM") node_res = res.index("NORM")
min_res = min(node_res, max_res) min_res = min(node_res, min_res)
return res[min_res] return res[min_res]
@rcmattr @rcmattr
def req_walltime(Aggr): def req_walltime(self):
MIN_R = 4 MIN_R = 4
SHIFT_R = 6 SHIFT_R = 6
rtime = Aggr.job.run_time rtime = self._aggr.job.run_time
utime = Aggr.job.requested_time utime = self._aggr.job.requested_time
res = "UNKNOWN" res = "UNKNOWN"
if rtime > MIN_R && utime < rtime/2 && utime < (rtime - SHIFT_R): if rtime > MIN_R and utime < rtime/2 and utime < (rtime - SHIFT_R):
res = "HIGH" res = "HIGH"
else: else:
res = "NORM" res = "NORM"
...@@ -149,9 +180,9 @@ class Attributes: ...@@ -149,9 +180,9 @@ class Attributes:
return res return res
@rcmattr @rcmattr
def overloaded_node_exists(Aggr): def overloaded_node_exists(self):
exists = False exists = False
for node in Aggr.nodes: for node in self._aggr.nodes:
if node.proc.cpu_usage > 90: if node.proc.cpu_usage > 90:
continue continue
...@@ -162,8 +193,8 @@ class Attributes: ...@@ -162,8 +193,8 @@ class Attributes:
return exists return exists
@rcmattr @rcmattr
def job_nodes_amount(Aggr): def job_nodes_amount(self):
nnodes = count(Aggr.nodes) nnodes = len(self._aggr.nodes)
res = "ERR" res = "ERR"
if nnodes == 1: if nnodes == 1:
...@@ -172,3 +203,13 @@ class Attributes: ...@@ -172,3 +203,13 @@ class Attributes:
res = "MULT" res = "MULT"
return res return res
@rcmattr
def mem_swap_used(self):
swap_used = False
for node in self._aggr.nodes:
if node.proc.mem_swap_max > 0:
swap_used = True
return swap_used
from .rules import RULES from .rules import RULES
from rcm import attributes from rcm import attributes
attributes.DEBUG = True attributes.DEBUG = False
def attributes_match(attrs, Aggr): def attributes_match(attrs, values):
matches = True matches = True
for rule_attr, attr_value in attrs.items(): for attr_name, attr_expected in attrs.items():
try: if attr_name not in values:
attr_func = getattr(attributes.Attributes, rule_attr) raise ValueError("Attribute is not found", attr_name)
except AttributeError:
raise AttributeError("there is no attribute " + rule_attr)
attr_value = values[attr_name]
expected_vals = [] expected_vals = []
if isinstance(attr_value, list):
expected_vals.extend(attr_value) if isinstance(attr_expected, list):
expected_vals.extend(attr_expected)
else: else:
expected_vals.append(attr_value) expected_vals.append(attr_expected)
if attr_func(Aggr) not in expected_vals: if attr_value not in expected_vals:
matches = False matches = False
break break
...@@ -25,9 +25,13 @@ def attributes_match(attrs, Aggr): ...@@ -25,9 +25,13 @@ def attributes_match(attrs, Aggr):
def get_recommendations(Aggr): def get_recommendations(Aggr):
Attrs = attributes.Attributes(Aggr)
attr_values = Attrs.calculate_attrs()
rcms = [] rcms = []
for rule in RULES: for rule in RULES:
if attributes_match(rule["attrs"], Aggr): if attributes_match(rule["attrs"], attr_values):
rcms.append(rule["msg"]) rcms.append(rule["msg"])
return rcms return rcms
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment