From d439267e0d1d43ce2a62c74a48ee5bd45f2df17a Mon Sep 17 00:00:00 2001 From: Mohammed-Chakib Belgaid Date: Mon, 23 Sep 2019 18:29:59 +0200 Subject: [PATCH 01/11] puting energy format in human one --- pyRAPL/pyRAPL.py | 3 ++- test.py | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 test.py diff --git a/pyRAPL/pyRAPL.py b/pyRAPL/pyRAPL.py index c84c911..9eee5e2 100644 --- a/pyRAPL/pyRAPL.py +++ b/pyRAPL/pyRAPL.py @@ -23,6 +23,7 @@ # SOFTWARE. import os import logging +from math import ldexp try: import psutil except ImportError: @@ -198,7 +199,7 @@ def energy(self, device): api_file = self.sys_api[device] api_file.seek(0, 0) - return int(api_file.readline()) + return ldexp(int(api_file.readline()),-32) def record(self, devices): """ diff --git a/test.py b/test.py new file mode 100644 index 0000000..d8d344b --- /dev/null +++ b/test.py @@ -0,0 +1,15 @@ +import pyRAPL +from time import sleep +from sys import argv +def fun (n): + sleep(n) + +if __name__=="__main__": + n = int(argv[1]) if len(argv) >1 else 5 + sensor = pyRAPL.PyRAPL() + sensor.record([pyRAPL.Device.PKG, pyRAPL.Device.DRAM]) + fun(n) + sensor.stop() + energy_pkg = sensor.recorded_energy(pyRAPL.Device.PKG) + energy_dram = sensor.recorded_energy(pyRAPL.Device.DRAM) + print("Energy PKG: %f , Energy DRAM: %f"%(energy_pkg,energy_dram)) From 3f0875b5e0da3581998d924f435cb1ba4757010c Mon Sep 17 00:00:00 2001 From: Mohammed-Chakib Belgaid Date: Tue, 24 Sep 2019 17:53:13 +0200 Subject: [PATCH 02/11] adding the decorator, the time measure --- README.md | 5 ++++ pyRAPL/__init__.py | 3 +- pyRAPL/pyRAPL.py | 74 ++++++++++++++++++++++++++++++++++++---------- test.py | 29 ++++++++++++++---- 4 files changed, 89 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 3ddb09a..eda6c55 100644 --- a/README.md +++ b/README.md @@ -12,3 +12,8 @@ To measure the energy consumed during the execution of the function `fun()` run sensor.stop() energy_pkg = sensor.recorded_energy(pyRAPL.Device.PKG) energy_dram = sensor.recorded_energy(pyRAPL.Device.DRAM) + + +# TODO +- [ ] add decorator +- [X] add time measure \ No newline at end of file diff --git a/pyRAPL/__init__.py b/pyRAPL/__init__.py index a1d134f..10b348f 100644 --- a/pyRAPL/__init__.py +++ b/pyRAPL/__init__.py @@ -22,7 +22,8 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from pyRAPL.pyRAPL import PyRAPL, Device, PyRAPLException, PyRAPLNoEnergyConsumptionRecordedException +from pyRAPL.pyRAPL import PyRAPL, Device, PyRAPLException,measure, PyRAPLNoEnergyConsumptionRecordedException from pyRAPL.pyRAPL import PyRAPLNoEnergyConsumptionRecordStartedException, PyRAPLCantRecordEnergyConsumption +# from pyRAPL.pyRAPL import measure_energy __version__ = "0.0.2" diff --git a/pyRAPL/pyRAPL.py b/pyRAPL/pyRAPL.py index 9eee5e2..7019dcb 100644 --- a/pyRAPL/pyRAPL.py +++ b/pyRAPL/pyRAPL.py @@ -24,6 +24,9 @@ import os import logging from math import ldexp +import time +import functools + try: import psutil except ImportError: @@ -33,6 +36,7 @@ from enum import Enum class Device(Enum): + TIME = 0 PKG = 1 DRAM = 2 GPU = 3 @@ -70,16 +74,19 @@ class PyRAPL: def __init__(self): self.sys_api = { + # Device.TIME : None, Device.PKG: None, Device.DRAM: None, Device.GPU: None } self.is_record_running = { + Device.TIME : False, Device.PKG: False, Device.DRAM: False, Device.GPU: False } - self.measure = { + self._measure = { + Device.TIME : [None,None], Device.PKG: [None, None], Device.DRAM: [None, None], Device.GPU: [None, None] @@ -192,11 +199,14 @@ def energy(self, device): :raise PyRAPLCantRecordEnergyConsumption: if no energy consumtion metric is available for the given device :raise TypeError: if device is not a Device parameter """ + if not isinstance(device, Device): raise TypeError() + if device is Device.TIME : + return time.perf_counter() if self.sys_api[device] is None: raise PyRAPLCantRecordEnergyConsumption(device) - + api_file = self.sys_api[device] api_file.seek(0, 0) return ldexp(int(api_file.readline()),-32) @@ -210,7 +220,7 @@ def record(self, devices): """ for device in devices: energy = self.energy(device) - self.measure[device][0] = energy + self._measure[device][0] = energy self.is_record_running[device] = True def stop(self): @@ -224,27 +234,59 @@ def stop(self): if is_running: energy_recorded = True self.is_record_running[device] = False - self.measure[device][1] = self.energy(device) + self._measure[device][1] = self.energy(device) if not energy_recorded: raise PyRAPLNoEnergyConsumptionRecordStartedException() - def recorded_energy(self, device): + def recorded_energy(self, devices=None): """ get the latest energy consumption recorded by PyRAPL for the given device :return: energy (in mJ) consumed between the last record() and stop() function call :raise PyRAPLNoEnergyConsumptionRecordedException: if no energy consumption was recorded :raise TypeError: if device is not a Device parameter """ - if not isinstance(device, Device): - raise TypeError() - - if self.measure[device][0] is None or self.measure[device][1] is None: - raise PyRAPLNoEnergyConsumptionRecordedException - - measure = self.measure[device][1] - self.measure[device][0] - self.measure[device][0] = None - self.measure[device][1] = None - - return measure + if devices is None : + l={} + for dev in self._measure.keys(): + # print("dev {}, dev[0] {} ,dev[1] {}".format(dev,self.measure[dev][0],self.measure[dev][1]) ) + if self._measure[dev][0] and self._measure[dev][1]: + l[dev] = self._measure[dev][1] - self._measure[dev][0] + self._measure[device][0] = None + self._measure[device][1] = None + + if len(l) ==0 : + raise PyRAPLNoEnergyConsumptionRecordedException + return l + measures={} + for device in devices : + if not isinstance(device, Device): + raise TypeError() + + if self._measure[device][0] is None or self._measure[device][1] is None: + raise PyRAPLNoEnergyConsumptionRecordedException + + measures[device] = self._measure[device][1] - self._measure[device][0] + self._measure[device][0] = None + self._measure[device][1] = None + + return measures + +# @staticmethod +def measure(_func=None,*,devices=[Device.TIME,Device.PKG, Device.DRAM]): + def decorator_measure_energy(func): + @functools.wraps(func) + def wrapper(*args,**kwargs): + sensor = PyRAPL() + sensor.record(devices) + val=func(*args,**kwargs) + sensor.stop() + return val,sensor.recorded_energy(devices) + return wrapper + if type(devices) != list : + devices=[devices] + if _func is None: # just to gain one call + return decorator_measure_energy + else: + return decorator_measure_energy(_func) diff --git a/test.py b/test.py index d8d344b..54ce48b 100644 --- a/test.py +++ b/test.py @@ -1,15 +1,34 @@ import pyRAPL +# from pyRAPL import measure +# from pyRAPL import measure_energy from time import sleep from sys import argv -def fun (n): + + + + +@pyRAPL.measure +def fun(n): sleep(n) -if __name__=="__main__": + +def main1(): n = int(argv[1]) if len(argv) >1 else 5 sensor = pyRAPL.PyRAPL() sensor.record([pyRAPL.Device.PKG, pyRAPL.Device.DRAM]) fun(n) sensor.stop() - energy_pkg = sensor.recorded_energy(pyRAPL.Device.PKG) - energy_dram = sensor.recorded_energy(pyRAPL.Device.DRAM) - print("Energy PKG: %f , Energy DRAM: %f"%(energy_pkg,energy_dram)) + # energy_pkg = sensor.recorded_energy(pyRAPL.Device.PKG) + # energy_dram = sensor.recorded_energy(pyRAPL.Device.DRAM) + # print("Energy PKG: %f , Energy DRAM: %f"%(energy_pkg,energy_dram)) + print(sensor.recorded_energy()) + + +def main2(): + n = int(argv[1]) if len(argv) >1 else 5 + _,measures=fun(n) + print(_) + print(measures[pyRAPL.Device.DRAM]) + +if __name__=="__main__": + main2() From 3a733cda931c3fcde3b5d4c102f7b193e4b6f961 Mon Sep 17 00:00:00 2001 From: Mohammed-Chakib Belgaid Date: Tue, 24 Sep 2019 18:47:44 +0200 Subject: [PATCH 03/11] #bugfix multiple instances --- pyRAPL/pyRAPL.py | 33 +++++++++++++++++++-------------- test.py | 20 +++++++++++++++++++- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/pyRAPL/pyRAPL.py b/pyRAPL/pyRAPL.py index 7019dcb..b18f50a 100644 --- a/pyRAPL/pyRAPL.py +++ b/pyRAPL/pyRAPL.py @@ -73,12 +73,7 @@ class PyRAPL: already_init = False def __init__(self): - self.sys_api = { - # Device.TIME : None, - Device.PKG: None, - Device.DRAM: None, - Device.GPU: None - } + print("this is an init") self.is_record_running = { Device.TIME : False, Device.PKG: False, @@ -102,6 +97,12 @@ def _uniq_init(self): """ Initialize the PyRAPL tool """ + self.sys_api = { + # Device.TIME : None, + Device.PKG: None, + Device.DRAM: None, + Device.GPU: None + } self.siblings_cpu = PyRAPL._get_siblings_cpu() self.package_id = PyRAPL._get_package_id() PyRAPL._force_cpu_execution_on(self.siblings_cpu) @@ -200,10 +201,12 @@ def energy(self, device): :raise TypeError: if device is not a Device parameter """ + if not isinstance(device, Device): raise TypeError() if device is Device.TIME : return time.perf_counter() + if self.sys_api[device] is None: raise PyRAPLCantRecordEnergyConsumption(device) @@ -240,26 +243,27 @@ def stop(self): raise PyRAPLNoEnergyConsumptionRecordStartedException() - def recorded_energy(self, devices=None): + def recorded_energy(self, *devices): """ get the latest energy consumption recorded by PyRAPL for the given device :return: energy (in mJ) consumed between the last record() and stop() function call :raise PyRAPLNoEnergyConsumptionRecordedException: if no energy consumption was recorded :raise TypeError: if device is not a Device parameter """ - if devices is None : + if len(devices) == 0 : l={} for dev in self._measure.keys(): # print("dev {}, dev[0] {} ,dev[1] {}".format(dev,self.measure[dev][0],self.measure[dev][1]) ) if self._measure[dev][0] and self._measure[dev][1]: l[dev] = self._measure[dev][1] - self._measure[dev][0] - self._measure[device][0] = None - self._measure[device][1] = None + self._measure[dev][0] = None + self._measure[dev][1] = None if len(l) ==0 : raise PyRAPLNoEnergyConsumptionRecordedException return l measures={} + for device in devices : if not isinstance(device, Device): raise TypeError() @@ -278,12 +282,13 @@ def measure(_func=None,*,devices=[Device.TIME,Device.PKG, Device.DRAM]): def decorator_measure_energy(func): @functools.wraps(func) def wrapper(*args,**kwargs): - sensor = PyRAPL() - sensor.record(devices) + # measure.sensor=PyRAPL() + measure.sensor.record(devices) val=func(*args,**kwargs) - sensor.stop() - return val,sensor.recorded_energy(devices) + measure.sensor.stop() + return val,measure.sensor.recorded_energy() return wrapper + measure.sensor=PyRAPL() # to make an instance only one time if type(devices) != list : devices=[devices] if _func is None: # just to gain one call diff --git a/test.py b/test.py index 54ce48b..2bf74ae 100644 --- a/test.py +++ b/test.py @@ -11,12 +11,25 @@ def fun(n): sleep(n) +@pyRAPL.measure +def fun2(n): + sleep(2*n) + + def main1(): n = int(argv[1]) if len(argv) >1 else 5 sensor = pyRAPL.PyRAPL() sensor.record([pyRAPL.Device.PKG, pyRAPL.Device.DRAM]) - fun(n) + sleep(n) + sensor.stop() + # energy_pkg = sensor.recorded_energy(pyRAPL.Device.PKG) + # energy_dram = sensor.recorded_energy(pyRAPL.Device.DRAM) + # print("Energy PKG: %f , Energy DRAM: %f"%(energy_pkg,energy_dram)) + print(sensor.recorded_energy()) + sensor = pyRAPL.PyRAPL() + sensor.record([pyRAPL.Device.PKG, pyRAPL.Device.DRAM]) + sleep(n) sensor.stop() # energy_pkg = sensor.recorded_energy(pyRAPL.Device.PKG) # energy_dram = sensor.recorded_energy(pyRAPL.Device.DRAM) @@ -26,7 +39,12 @@ def main1(): def main2(): n = int(argv[1]) if len(argv) >1 else 5 + fun2(n) + print("fin f2") + fun(n) + print("fin f1") _,measures=fun(n) + print(_) print(measures[pyRAPL.Device.DRAM]) From 3d3a0f0fc710b4561d1e305211aca287d8d0b09b Mon Sep 17 00:00:00 2001 From: Mohammed-Chakib Belgaid Date: Tue, 24 Sep 2019 18:56:11 +0200 Subject: [PATCH 04/11] adding docstring to the decorator --- pyRAPL/pyRAPL.py | 11 +++++++---- test.py | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/pyRAPL/pyRAPL.py b/pyRAPL/pyRAPL.py index b18f50a..569afff 100644 --- a/pyRAPL/pyRAPL.py +++ b/pyRAPL/pyRAPL.py @@ -73,7 +73,6 @@ class PyRAPL: already_init = False def __init__(self): - print("this is an init") self.is_record_running = { Device.TIME : False, Device.PKG: False, @@ -279,19 +278,23 @@ def recorded_energy(self, *devices): # @staticmethod def measure(_func=None,*,devices=[Device.TIME,Device.PKG, Device.DRAM]): + """ a decorator to measure the energy consumption of a function recorded by PyRAPL + :return (function return , [measure1, measure2 ..etc]) """ + def decorator_measure_energy(func): @functools.wraps(func) - def wrapper(*args,**kwargs): + def wrapper_measure(*args,**kwargs): # measure.sensor=PyRAPL() measure.sensor.record(devices) val=func(*args,**kwargs) measure.sensor.stop() return val,measure.sensor.recorded_energy() - return wrapper + return wrapper_measure measure.sensor=PyRAPL() # to make an instance only one time if type(devices) != list : devices=[devices] - if _func is None: # just to gain one call + + if _func is None: # to ensure the working system when you call it with parameters or without parameters return decorator_measure_energy else: return decorator_measure_energy(_func) diff --git a/test.py b/test.py index 2bf74ae..958b53d 100644 --- a/test.py +++ b/test.py @@ -11,7 +11,7 @@ def fun(n): sleep(n) -@pyRAPL.measure +@pyRAPL.measure(devices=pyRAPL.Device.TIME) def fun2(n): sleep(2*n) From 324a499e4749c72f6d78136fea07921475e44ce8 Mon Sep 17 00:00:00 2001 From: Mohammed-Chakib Belgaid Date: Wed, 25 Sep 2019 10:48:13 +0200 Subject: [PATCH 05/11] changin the decorator to accept a handler --- pyRAPL/pyRAPL.py | 24 +++++++++++++++++------- test.py | 19 ++++++++++--------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/pyRAPL/pyRAPL.py b/pyRAPL/pyRAPL.py index 569afff..dde9d5a 100644 --- a/pyRAPL/pyRAPL.py +++ b/pyRAPL/pyRAPL.py @@ -73,6 +73,7 @@ class PyRAPL: already_init = False def __init__(self): + self.is_record_running = { Device.TIME : False, Device.PKG: False, @@ -277,23 +278,32 @@ def recorded_energy(self, *devices): return measures # @staticmethod -def measure(_func=None,*,devices=[Device.TIME,Device.PKG, Device.DRAM]): +def measure(_func=None,*,devices=[Device.TIME,Device.PKG, Device.DRAM],handler=None): """ a decorator to measure the energy consumption of a function recorded by PyRAPL :return (function return , [measure1, measure2 ..etc]) """ - + def default_handler(measures): + print("default handler") + # print(f"measures got from the function {func.__name__ }") + for mes in measures.keys() : + print(f"{mes } : {measures[mes]}") + def decorator_measure_energy(func): @functools.wraps(func) def wrapper_measure(*args,**kwargs): - # measure.sensor=PyRAPL() - measure.sensor.record(devices) + sensor=PyRAPL() + sensor.record(devices) val=func(*args,**kwargs) - measure.sensor.stop() - return val,measure.sensor.recorded_energy() + sensor.stop() + handle(sensor.recorded_energy(*devices)) + return val return wrapper_measure - measure.sensor=PyRAPL() # to make an instance only one time + + # measure.sensor=PyRAPL() # to make an instance only one time if type(devices) != list : devices=[devices] + handle= default_handler if handler is None else handler + if _func is None: # to ensure the working system when you call it with parameters or without parameters return decorator_measure_energy else: diff --git a/test.py b/test.py index 958b53d..0c7fecf 100644 --- a/test.py +++ b/test.py @@ -6,10 +6,15 @@ +def myhandler(measures): + print("myhandler") + print(measures) -@pyRAPL.measure -def fun(n): + +@pyRAPL.measure(handler=myhandler) +def testi(n): sleep(n) + return n @pyRAPL.measure(devices=pyRAPL.Device.TIME) def fun2(n): @@ -39,14 +44,10 @@ def main1(): def main2(): n = int(argv[1]) if len(argv) >1 else 5 + testi(n) + fun2(n) + testi(n) fun2(n) - print("fin f2") - fun(n) - print("fin f1") - _,measures=fun(n) - - print(_) - print(measures[pyRAPL.Device.DRAM]) if __name__=="__main__": main2() From 0f40f98ca148c63507119a1713d0d5678d52f128 Mon Sep 17 00:00:00 2001 From: Mohammed-Chakib Belgaid Date: Wed, 25 Sep 2019 11:06:57 +0200 Subject: [PATCH 06/11] documentation --- pyRAPL/pyRAPL.py | 4 +++- test.py | 10 +++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/pyRAPL/pyRAPL.py b/pyRAPL/pyRAPL.py index dde9d5a..04b3505 100644 --- a/pyRAPL/pyRAPL.py +++ b/pyRAPL/pyRAPL.py @@ -280,7 +280,9 @@ def recorded_energy(self, *devices): # @staticmethod def measure(_func=None,*,devices=[Device.TIME,Device.PKG, Device.DRAM],handler=None): """ a decorator to measure the energy consumption of a function recorded by PyRAPL - :return (function return , [measure1, measure2 ..etc]) """ + :param [Device] devices: the list of events to monitor by pyrapl + :param function(measure) handler: traitement of the results recorded from pyrapl + """ def default_handler(measures): print("default handler") # print(f"measures got from the function {func.__name__ }") diff --git a/test.py b/test.py index 0c7fecf..dbfa494 100644 --- a/test.py +++ b/test.py @@ -16,7 +16,7 @@ def testi(n): sleep(n) return n -@pyRAPL.measure(devices=pyRAPL.Device.TIME) +@pyRAPL.measure() def fun2(n): sleep(2*n) @@ -33,7 +33,7 @@ def main1(): # print("Energy PKG: %f , Energy DRAM: %f"%(energy_pkg,energy_dram)) print(sensor.recorded_energy()) sensor = pyRAPL.PyRAPL() - sensor.record([pyRAPL.Device.PKG, pyRAPL.Device.DRAM]) + sensor.record() sleep(n) sensor.stop() # energy_pkg = sensor.recorded_energy(pyRAPL.Device.PKG) @@ -44,9 +44,9 @@ def main1(): def main2(): n = int(argv[1]) if len(argv) >1 else 5 - testi(n) - fun2(n) - testi(n) + # testi(n) + # fun2(n) + # testi(n) fun2(n) if __name__=="__main__": From 82affc4ff0b721279b02c6b2902333ee2d19fdbe Mon Sep 17 00:00:00 2001 From: Mohammed-Chakib Belgaid Date: Wed, 25 Sep 2019 11:37:20 +0200 Subject: [PATCH 07/11] addinc the caller function name to the default handler --- pyRAPL/pyRAPL.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pyRAPL/pyRAPL.py b/pyRAPL/pyRAPL.py index 04b3505..da55edb 100644 --- a/pyRAPL/pyRAPL.py +++ b/pyRAPL/pyRAPL.py @@ -26,6 +26,8 @@ from math import ldexp import time import functools +import sys + try: import psutil @@ -283,11 +285,13 @@ def measure(_func=None,*,devices=[Device.TIME,Device.PKG, Device.DRAM],handler=N :param [Device] devices: the list of events to monitor by pyrapl :param function(measure) handler: traitement of the results recorded from pyrapl """ + def default_handler(measures): - print("default handler") - # print(f"measures got from the function {func.__name__ }") + # print("default handler") + func = sys._getframe(1).f_locals['func'].__name__ + print(f"measures got from the function {func } :") for mes in measures.keys() : - print(f"{mes } : {measures[mes]}") + print(f"{mes } : {measures[mes]:.4}") def decorator_measure_energy(func): @functools.wraps(func) From 24d322273bf67f485ca12e3aefad292f400ebb80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arthur=20d=27Az=C3=A9mar?= Date: Wed, 25 Sep 2019 15:47:56 +0200 Subject: [PATCH 08/11] fix: fix refactoring --- pyRAPL/pyRAPL.py | 196 +++++++++++++++++++++++-------------------- test.py | 6 +- tests/test_pyRAPL.py | 98 +++++++++++----------- 3 files changed, 161 insertions(+), 139 deletions(-) diff --git a/pyRAPL/pyRAPL.py b/pyRAPL/pyRAPL.py index da55edb..1372a91 100644 --- a/pyRAPL/pyRAPL.py +++ b/pyRAPL/pyRAPL.py @@ -23,11 +23,11 @@ # SOFTWARE. import os import logging -from math import ldexp -import time +import time import functools -import sys +from math import ldexp +from enum import Enum try: import psutil @@ -35,10 +35,7 @@ logging.getLogger().info("psutil is not installed.") -from enum import Enum - class Device(Enum): - TIME = 0 PKG = 1 DRAM = 2 GPU = 3 @@ -71,44 +68,47 @@ class PyRAPL: """ singleton that force the execution of the running process on a unique package and retrieve package power consumption """ - instance = None - already_init = False + _instance = None + _already_init = False + + def __new__(cls): + """use only one instance of PyRAPL""" + if cls._instance is None: + cls._instance = object.__new__(cls) + return cls._instance def __init__(self): - self.is_record_running = { - Device.TIME : False, + self._is_record_running = { Device.PKG: False, Device.DRAM: False, Device.GPU: False } self._measure = { - Device.TIME : [None,None], Device.PKG: [None, None], Device.DRAM: [None, None], Device.GPU: [None, None] } - self.package_id = None - self.siblings_cpu = None + self._package_id = None + self._siblings_cpu = None - if self.already_init is False: - self.already_init = True + if self._already_init is False: + self._already_init = True self._uniq_init() def _uniq_init(self): """ Initialize the PyRAPL tool """ - self.sys_api = { - # Device.TIME : None, + self._sys_api = { Device.PKG: None, Device.DRAM: None, Device.GPU: None } - self.siblings_cpu = PyRAPL._get_siblings_cpu() - self.package_id = PyRAPL._get_package_id() - PyRAPL._force_cpu_execution_on(self.siblings_cpu) - self._open_rapl_files(self.package_id) + self._siblings_cpu = PyRAPL._get_siblings_cpu() + self._package_id = PyRAPL._get_package_id() + PyRAPL._force_cpu_execution_on(self._siblings_cpu) + self._open_rapl_files(self._package_id) @staticmethod def _get_siblings_cpu(): @@ -151,17 +151,17 @@ def _open_rapl_files(self, package_id): """ pkg_dir_name, pkg_rapl_id = self._get_pkg_directory_name(package_id) if pkg_dir_name is None: - self.sys_api[Device.PKG] = None - self.sys_api[Device.DRAM] = None + self._sys_api[Device.PKG] = None + self._sys_api[Device.DRAM] = None return - self.sys_api[Device.PKG] = open(pkg_dir_name + '/energy_uj', 'r') + self._sys_api[Device.PKG] = open(pkg_dir_name + '/energy_uj', 'r') dram_dir_name = self._get_dram_directory_name(pkg_dir_name, pkg_rapl_id) if dram_dir_name is None: - self.sys_api[Device.DRAM] = None + self._sys_api[Device.DRAM] = None return - self.sys_api[Device.DRAM] = open(dram_dir_name + '/energy_uj', 'r') + self._sys_api[Device.DRAM] = open(dram_dir_name + '/energy_uj', 'r') def _get_pkg_directory_name(self, package_id): """ @@ -188,45 +188,44 @@ def _get_dram_directory_name(self, pkg_directory_name, rapl_id): sub_id += 1 return None - def __new__(cls): - """use only one instance of PyRAPL""" - if cls.instance is None: - cls.instance = object.__new__(cls) - return cls.instance - def energy(self, device): """ return the amount of consumed energy by the device given in parameter since last CPU reset :param Device device: the device to get the power consumption :return int: the amount of consumed energy of the device since last CPU reset in mJ :raise PyRAPLCantRecordEnergyConsumption: if no energy consumtion metric is available for the given device - :raise TypeError: if device is not a Device parameter + :raise TypeError: if device is not a Device instance """ - if not isinstance(device, Device): raise TypeError() - if device is Device.TIME : - return time.perf_counter() - - if self.sys_api[device] is None: + + if self._sys_api[device] is None: raise PyRAPLCantRecordEnergyConsumption(device) - - api_file = self.sys_api[device] + + api_file = self._sys_api[device] api_file.seek(0, 0) - return ldexp(int(api_file.readline()),-32) + return ldexp(int(api_file.readline()), -32) + + def _begin_record(self, device): + energy = self.energy(device) + self._measure[device][0] = energy + self._is_record_running[device] = True - def record(self, devices): + def record(self, *devices): """ start recording the power consumption of the given devices :param [Device] devices: list of device to record the power consumption :raise PyRAPLCantRecordEnergyConsumption: if no energy consumtion metric is available for the given device - :raise TypeError: if a device in devices list is not a Device parameter + :raise TypeError: if a device in devices list is not a Device instance """ - for device in devices: - energy = self.energy(device) - self._measure[device][0] = energy - self.is_record_running[device] = True + if devices: + for device in devices: + self._begin_record(device) + else: + for device, api_file in self._sys_api.items(): + if api_file: + self._begin_record(device) def stop(self): """ @@ -235,82 +234,97 @@ def stop(self): """ energy_recorded = False - for device, is_running in self.is_record_running.items(): + for device, is_running in self._is_record_running.items(): if is_running: energy_recorded = True - self.is_record_running[device] = False + self._is_record_running[device] = False self._measure[device][1] = self.energy(device) if not energy_recorded: raise PyRAPLNoEnergyConsumptionRecordStartedException() + def _compute_recorded_energy(self, device): + + recorded_energy = self._measure[device][1] - self._measure[device][0] + self._measure[device][0] = None + self._measure[device][1] = None + return recorded_energy def recorded_energy(self, *devices): """ get the latest energy consumption recorded by PyRAPL for the given device :return: energy (in mJ) consumed between the last record() and stop() function call :raise PyRAPLNoEnergyConsumptionRecordedException: if no energy consumption was recorded - :raise TypeError: if device is not a Device parameter + :raise TypeError: if device is not a Device instance """ - if len(devices) == 0 : - l={} - for dev in self._measure.keys(): - # print("dev {}, dev[0] {} ,dev[1] {}".format(dev,self.measure[dev][0],self.measure[dev][1]) ) - if self._measure[dev][0] and self._measure[dev][1]: - l[dev] = self._measure[dev][1] - self._measure[dev][0] - self._measure[dev][0] = None - self._measure[dev][1] = None - - if len(l) ==0 : - raise PyRAPLNoEnergyConsumptionRecordedException - return l - measures={} + measures = {} + if devices: + for device in devices: + if not isinstance(device, Device): + raise TypeError() + + if self._measure[device][0] is None or self._measure[device][1] is None: + raise PyRAPLNoEnergyConsumptionRecordedException - for device in devices : - if not isinstance(device, Device): - raise TypeError() + measures[device] = self._compute_recorded_energy(device) - if self._measure[device][0] is None or self._measure[device][1] is None: + return measures + else: + for device in self._measure: + # print("dev {}, dev[0] {} ,dev[1] {}".format(dev,self.measure[dev][0],self.measure[dev][1]) ) + if self._measure[device][0] and self._measure[device][1]: + measures[device] = self._compute_recorded_energy(device) + if measures: + return measures + else: raise PyRAPLNoEnergyConsumptionRecordedException - measures[device] = self._measure[device][1] - self._measure[device][0] - self._measure[device][0] = None - self._measure[device][1] = None - return measures +class Measure: + def __init__(self, function_name, data): + self.function_name = function_name + self.data = data -# @staticmethod -def measure(_func=None,*,devices=[Device.TIME,Device.PKG, Device.DRAM],handler=None): - """ a decorator to measure the energy consumption of a function recorded by PyRAPL - :param [Device] devices: the list of events to monitor by pyrapl + +def measure(_func=None, *, devices=None, handler=None): + """ a decorator to measure the energy consumption of a function recorded by PyRAPL + :param [Device] devices: the list of devices to monitor by pyrapl :param function(measure) handler: traitement of the results recorded from pyrapl """ - def default_handler(measures): + def default_handler(measures): # print("default handler") - func = sys._getframe(1).f_locals['func'].__name__ - print(f"measures got from the function {func } :") - for mes in measures.keys() : - print(f"{mes } : {measures[mes]:.4}") + print(f"measures got from the function {measures.function_name } :") + for mes, val in measures.data.items(): + print(f"{mes } : {val:.4}") def decorator_measure_energy(func): @functools.wraps(func) - def wrapper_measure(*args,**kwargs): - sensor=PyRAPL() - sensor.record(devices) - val=func(*args,**kwargs) + def wrapper_measure(*args, **kwargs): + sensor = PyRAPL() + sensor.record(*devices) + t1 = time.perf_counter() + val = func(*args, **kwargs) + t2 = time.perf_counter() sensor.stop() - handle(sensor.recorded_energy(*devices)) + + data = { + device._name_: mes for device, mes in sensor.recorded_energy(*devices).items() + } + data['TIME'] = t2 - t1 + + handle(Measure(func.__name__, data)) return val - return wrapper_measure + return wrapper_measure - # measure.sensor=PyRAPL() # to make an instance only one time - if type(devices) != list : - devices=[devices] + # measure.sensor=PyRAPL() # to make an instance only one time + if not isinstance(devices, list): + devices = [devices] if devices else [] - handle= default_handler if handler is None else handler + handle = default_handler if handler is None else handler - if _func is None: # to ensure the working system when you call it with parameters or without parameters + if _func is None: + # to ensure the working system when you call it with parameters or without parameters return decorator_measure_energy else: return decorator_measure_energy(_func) diff --git a/test.py b/test.py index dbfa494..9d66bdf 100644 --- a/test.py +++ b/test.py @@ -21,6 +21,10 @@ def fun2(n): sleep(2*n) +@pyRAPL.measure(devices=[pyRAPL.Device.PKG, pyRAPL.Device.DRAM]) +def fun3(n): + sleep(2*n) + def main1(): n = int(argv[1]) if len(argv) >1 else 5 @@ -47,7 +51,7 @@ def main2(): # testi(n) # fun2(n) # testi(n) - fun2(n) + fun3(n) if __name__=="__main__": main2() diff --git a/tests/test_pyRAPL.py b/tests/test_pyRAPL.py index 98e06e3..88d604d 100644 --- a/tests/test_pyRAPL.py +++ b/tests/test_pyRAPL.py @@ -58,10 +58,12 @@ def mocked_pyRAPL(): """ return a PyRAPL instance with mocked psutils function """ + PyRAPL._instance = None + PyRAPL._aldready_init = False pyrapl = PyRAPL() yield pyrapl - PyRAPL.instance = None - PyRAPL.aldready_init = False + PyRAPL._instance = None + PyRAPL._aldready_init = False ############# # INIT TEST # @@ -128,8 +130,8 @@ def test_open_rapl_files_no_file(base_fs, mocked_pyRAPL): - no file for package metric was found - no file for dram metric was found """ - assert mocked_pyRAPL.sys_api[Device.PKG] is None - assert mocked_pyRAPL.sys_api[Device.DRAM] is None + assert mocked_pyRAPL._sys_api[Device.PKG] is None + assert mocked_pyRAPL._sys_api[Device.DRAM] is None def test_open_rapl_files_simple(rapl_fs, mocked_pyRAPL): @@ -141,10 +143,10 @@ def test_open_rapl_files_simple(rapl_fs, mocked_pyRAPL): - the file /sys/class/powercap/intel-rapl/intel-rapl:0/energy_uj was open by the PyRAPL instance - no file for dram metric was found """ - assert mocked_pyRAPL.sys_api[Device.PKG] is not None + assert mocked_pyRAPL._sys_api[Device.PKG] is not None - assert mocked_pyRAPL.sys_api[Device.DRAM] is None - assert mocked_pyRAPL.sys_api[Device.PKG].name == '/sys/class/powercap/intel-rapl/intel-rapl:0/energy_uj' + assert mocked_pyRAPL._sys_api[Device.DRAM] is None + assert mocked_pyRAPL._sys_api[Device.PKG].name == '/sys/class/powercap/intel-rapl/intel-rapl:0/energy_uj' def test_open_rapl_files_simple_pkg1(rapl_fs, mocked_pyRAPL): @@ -157,10 +159,10 @@ def test_open_rapl_files_simple_pkg1(rapl_fs, mocked_pyRAPL): - no file for dram metric was found """ mocked_pyRAPL._open_rapl_files(1) - assert mocked_pyRAPL.sys_api[Device.PKG] is not None + assert mocked_pyRAPL._sys_api[Device.PKG] is not None - assert mocked_pyRAPL.sys_api[Device.DRAM] is None - assert mocked_pyRAPL.sys_api[Device.PKG].name == '/sys/class/powercap/intel-rapl/intel-rapl:1/energy_uj' + assert mocked_pyRAPL._sys_api[Device.DRAM] is None + assert mocked_pyRAPL._sys_api[Device.PKG].name == '/sys/class/powercap/intel-rapl/intel-rapl:1/energy_uj' def test_open_rapl_files_with_core(rapl_fs_with_core, mocked_pyRAPL): @@ -172,10 +174,10 @@ def test_open_rapl_files_with_core(rapl_fs_with_core, mocked_pyRAPL): - the file /sys/class/powercap/intel-rapl/intel-rapl:0/energy_uj was open by the PyRAPL instance - no file for dram metric was found """ - assert mocked_pyRAPL.sys_api[Device.PKG] is not None + assert mocked_pyRAPL._sys_api[Device.PKG] is not None - assert mocked_pyRAPL.sys_api[Device.DRAM] is None - assert mocked_pyRAPL.sys_api[Device.PKG].name == '/sys/class/powercap/intel-rapl/intel-rapl:0/energy_uj' + assert mocked_pyRAPL._sys_api[Device.DRAM] is None + assert mocked_pyRAPL._sys_api[Device.PKG].name == '/sys/class/powercap/intel-rapl/intel-rapl:0/energy_uj' def test_open_rapl_files_with_dram(rapl_fs_with_dram, mocked_pyRAPL): @@ -187,11 +189,11 @@ def test_open_rapl_files_with_dram(rapl_fs_with_dram, mocked_pyRAPL): - the file /sys/class/powercap/intel-rapl/intel-rapl:0/energy_uj was open by the PyRAPL instance - the file /sys/class/powercap/intel-rapl/intel-rapl:0/intel-rapl:0:1/energy_uj was open by the PyRAPL instance """ - assert mocked_pyRAPL.sys_api[Device.DRAM] is not None - assert mocked_pyRAPL.sys_api[Device.PKG] is not None + assert mocked_pyRAPL._sys_api[Device.DRAM] is not None + assert mocked_pyRAPL._sys_api[Device.PKG] is not None - assert mocked_pyRAPL.sys_api[Device.DRAM].name == '/sys/class/powercap/intel-rapl/intel-rapl:0/intel-rapl:0:1/energy_uj' - assert mocked_pyRAPL.sys_api[Device.PKG].name == '/sys/class/powercap/intel-rapl/intel-rapl:0/energy_uj' + assert mocked_pyRAPL._sys_api[Device.DRAM].name == '/sys/class/powercap/intel-rapl/intel-rapl:0/intel-rapl:0:1/energy_uj' + assert mocked_pyRAPL._sys_api[Device.PKG].name == '/sys/class/powercap/intel-rapl/intel-rapl:0/energy_uj' ####################### @@ -230,7 +232,7 @@ def test_energy(rapl_fs, mocked_pyRAPL): - the returned value is an int and is greater than 0 """ val = mocked_pyRAPL.energy(Device.PKG) - assert isinstance(val, int) + assert isinstance(val, float) assert val > 0 @@ -247,7 +249,7 @@ def test_start_record_energy_pkg_without_rapl(base_fs, mocked_pyRAPL): - a PyRAPLCantRecordEnergyConsumption is raise """ with pytest.raises(PyRAPLCantRecordEnergyConsumption): - mocked_pyRAPL.record([Device.PKG]) + mocked_pyRAPL.record(Device.PKG) def test_record_bad_type_device(rapl_fs, mocked_pyRAPL): @@ -258,7 +260,7 @@ def test_record_bad_type_device(rapl_fs, mocked_pyRAPL): - A TypeError is raise """ with pytest.raises(TypeError): - mocked_pyRAPL.record([Device.PKG, 5]) + mocked_pyRAPL.record(Device.PKG, 5) def test_start_record_energy_pkg(rapl_fs, mocked_pyRAPL): @@ -268,15 +270,15 @@ def test_start_record_energy_pkg(rapl_fs, mocked_pyRAPL): create an instance of PyRAPL with the file system base_fs and run the start_record_energy_pkg function Test if: - - before runing the function the measure[Device.PKG][0] attribute is None and is_record_running[Device.PKG] is + - before runing the function the _measure[Device.PKG][0] attribute is None and is_record_running[Device.PKG] is False - - the measure[Device.PKG][0] attribute is not None and is_record_running[Device.PKG] is True + - the _measure[Device.PKG][0] attribute is not None and is_record_running[Device.PKG] is True """ - assert mocked_pyRAPL.measure[Device.PKG][0] is None - assert mocked_pyRAPL.is_record_running[Device.PKG] is False - mocked_pyRAPL.record([Device.PKG]) - assert mocked_pyRAPL.measure[Device.PKG][0] is not None - assert mocked_pyRAPL.is_record_running[Device.PKG] is True + assert mocked_pyRAPL._measure[Device.PKG][0] is None + assert mocked_pyRAPL._is_record_running[Device.PKG] is False + mocked_pyRAPL.record(Device.PKG) + assert mocked_pyRAPL._measure[Device.PKG][0] is not None + assert mocked_pyRAPL._is_record_running[Device.PKG] is True def test_start_record_energy_pkg_and_dram(rapl_fs_with_dram, mocked_pyRAPL): @@ -286,14 +288,14 @@ def test_start_record_energy_pkg_and_dram(rapl_fs_with_dram, mocked_pyRAPL): create an instance of PyRAPL with the file system base_fs and run the record function Test if: - - before runing the function the measure[Device.PKG][0] attribute is None - - the measure[Device.PKG][0] attribute is not None + - before runing the function the _measure[Device.PKG][0] attribute is None + - the _measure[Device.PKG][0] attribute is not None """ - assert mocked_pyRAPL.measure[Device.PKG][0] is None - assert mocked_pyRAPL.measure[Device.DRAM][0] is None - mocked_pyRAPL.record([Device.PKG, Device.DRAM]) - assert mocked_pyRAPL.measure[Device.PKG][0] is not None - assert mocked_pyRAPL.measure[Device.DRAM][0] is not None + assert mocked_pyRAPL._measure[Device.PKG][0] is None + assert mocked_pyRAPL._measure[Device.DRAM][0] is None + mocked_pyRAPL.record(Device.PKG, Device.DRAM) + assert mocked_pyRAPL._measure[Device.PKG][0] is not None + assert mocked_pyRAPL._measure[Device.DRAM][0] is not None ################################## # STOP RECORDING PACKAGE ENERGY # @@ -319,17 +321,17 @@ def test_stop_record_energy_pkg(rapl_fs, mocked_pyRAPL): stop_record_energy_pkg functions Test if: - - before runing the function the measure[Device.PKG][1] attribute is None and is_record_running[Device.PKG] is + - before runing the function the _measure[Device.PKG][1] attribute is None and is_record_running[Device.PKG] is True - - the measure[Device.PKG][1] attribute is not None and is_record_running[Device.PKG] is False + - the _measure[Device.PKG][1] attribute is not None and is_record_running[Device.PKG] is False """ - mocked_pyRAPL.record([Device.PKG]) + mocked_pyRAPL.record(Device.PKG) - assert mocked_pyRAPL.measure[Device.PKG][1] is None - assert mocked_pyRAPL.is_record_running[Device.PKG] is True + assert mocked_pyRAPL._measure[Device.PKG][1] is None + assert mocked_pyRAPL._is_record_running[Device.PKG] is True mocked_pyRAPL.stop() - assert mocked_pyRAPL.measure[Device.PKG][1] is not None - assert mocked_pyRAPL.is_record_running[Device.PKG] is False + assert mocked_pyRAPL._measure[Device.PKG][1] is not None + assert mocked_pyRAPL._is_record_running[Device.PKG] is False ################################# # GET RECORDED PACKAGE ENERGY # @@ -346,6 +348,7 @@ def test_get_record_pkg_result_without_measure(rapl_fs, mocked_pyRAPL): with pytest.raises(PyRAPLNoEnergyConsumptionRecordedException): mocked_pyRAPL.recorded_energy(Device.PKG) + def test_get_recorded_energy_bad_type_device(rapl_fs, mocked_pyRAPL): """ call the PyRAPL.recorded_energy function with a non Device argument @@ -366,12 +369,13 @@ def test_get_record_energy_pkg(rapl_fs, mocked_pyRAPL): Test if: - the returned value is an integer - - the returned value is equls to th difference between the measure[Device.PKG][0] attribute and the - measure[Device.PKG][1] attribute + - the returned value is equls to th difference between the _measure[Device.PKG][0] attribute and the + _measure[Device.PKG][1] attribute """ - mocked_pyRAPL.record([Device.PKG]) + mocked_pyRAPL.record(Device.PKG) mocked_pyRAPL.stop() - correct_value = mocked_pyRAPL.measure[Device.PKG][1] - mocked_pyRAPL.measure[Device.PKG][0] + correct_value = mocked_pyRAPL._measure[Device.PKG][1] - mocked_pyRAPL._measure[Device.PKG][0] val = mocked_pyRAPL.recorded_energy(Device.PKG) - assert isinstance(val, int) - assert val == correct_value + assert isinstance(val, dict) + assert Device.PKG in val + assert val[Device.PKG] == correct_value From 58c4b4077099a88662e10957c102cf04be5785c8 Mon Sep 17 00:00:00 2001 From: Mohammed-Chakib Belgaid Date: Wed, 25 Sep 2019 17:08:18 +0200 Subject: [PATCH 09/11] fix: rapl value --- pyRAPL/pyRAPL.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyRAPL/pyRAPL.py b/pyRAPL/pyRAPL.py index 1372a91..8e82328 100644 --- a/pyRAPL/pyRAPL.py +++ b/pyRAPL/pyRAPL.py @@ -25,7 +25,6 @@ import logging import time import functools -from math import ldexp from enum import Enum @@ -205,7 +204,7 @@ def energy(self, device): api_file = self._sys_api[device] api_file.seek(0, 0) - return ldexp(int(api_file.readline()), -32) + return int(api_file.readline()) def _begin_record(self, device): energy = self.energy(device) @@ -245,7 +244,8 @@ def stop(self): def _compute_recorded_energy(self, device): - recorded_energy = self._measure[device][1] - self._measure[device][0] + recorded_energy = (self._measure[device][1] - self._measure[device][0])/1000000 + # print("yolo") self._measure[device][0] = None self._measure[device][1] = None return recorded_energy From 079a396313d3616ba42d75df7390064b83449a5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arthur=20d=27Az=C3=A9mar?= Date: Wed, 25 Sep 2019 17:31:44 +0200 Subject: [PATCH 10/11] tests: add tests for decorator and new recorded_energy interface function --- pyRAPL/__init__.py | 2 +- pyRAPL/pyRAPL.py | 1 - tests/test_pyRAPL.py | 319 +++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 308 insertions(+), 14 deletions(-) diff --git a/pyRAPL/__init__.py b/pyRAPL/__init__.py index 10b348f..8fb94aa 100644 --- a/pyRAPL/__init__.py +++ b/pyRAPL/__init__.py @@ -22,7 +22,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from pyRAPL.pyRAPL import PyRAPL, Device, PyRAPLException,measure, PyRAPLNoEnergyConsumptionRecordedException +from pyRAPL.pyRAPL import PyRAPL, Device, Measure, measure, PyRAPLException, PyRAPLNoEnergyConsumptionRecordedException from pyRAPL.pyRAPL import PyRAPLNoEnergyConsumptionRecordStartedException, PyRAPLCantRecordEnergyConsumption # from pyRAPL.pyRAPL import measure_energy diff --git a/pyRAPL/pyRAPL.py b/pyRAPL/pyRAPL.py index 1372a91..eb0c616 100644 --- a/pyRAPL/pyRAPL.py +++ b/pyRAPL/pyRAPL.py @@ -91,7 +91,6 @@ def __init__(self): } self._package_id = None self._siblings_cpu = None - if self._already_init is False: self._already_init = True self._uniq_init() diff --git a/tests/test_pyRAPL.py b/tests/test_pyRAPL.py index 88d604d..668e4bc 100644 --- a/tests/test_pyRAPL.py +++ b/tests/test_pyRAPL.py @@ -4,7 +4,7 @@ from mock import patch, Mock -from pyRAPL import PyRAPL, Device, PyRAPLCantRecordEnergyConsumption +from pyRAPL import PyRAPL, Device, measure, Measure, PyRAPLCantRecordEnergyConsumption from pyRAPL import PyRAPLNoEnergyConsumptionRecordedException, PyRAPLNoEnergyConsumptionRecordStartedException psutil.Process = Mock() @@ -49,7 +49,7 @@ def rapl_fs_with_dram(rapl_fs_with_core): """ rapl_fs_with_core.create_file('/sys/class/powercap/intel-rapl/intel-rapl:0/intel-rapl:0:1/name', contents='dram\n') rapl_fs_with_core.create_file('/sys/class/powercap/intel-rapl/intel-rapl:0/intel-rapl:0:1/energy_uj', - contents='12345\n') + contents='54321\n') return rapl_fs_with_core @@ -85,7 +85,7 @@ def test_uniq_init_executed_once(mock1, mock2): def test_get_siblings_cpu_simple_list(fs): """ - create a /sys/devices/system/cpu/cpu0/topology/core_siblings_list file that contains the following value : + create a /sys/devices/system/cpu/cpu0/topology/core_siblings_list file that contains the following value : 1,2,3,20 use the PyRAPL._get_siblings_cpu to retrieve sibling cpu of cpu0 @@ -98,7 +98,7 @@ def test_get_siblings_cpu_simple_list(fs): def test_get_siblings_cpu_interval_id(fs): """ - create a /sys/devices/system/cpu/cpu0/topology/core_siblings_list file that contains the following value : + create a /sys/devices/system/cpu/cpu0/topology/core_siblings_list file that contains the following value : 1-3 use the PyRAPL._get_siblings_cpu to retrieve sibling cpu of cpu0 @@ -111,7 +111,7 @@ def test_get_siblings_cpu_interval_id(fs): def test_get_package_id(fs): """ - create a /sys/devices/system/cpu/cpu0/topology/physical_package_id file that contains the following value : + create a /sys/devices/system/cpu/cpu0/topology/physical_package_id file that contains the following value : 9 use the PyRAPL._get_package_id to retrieve the package id of cpu0 @@ -252,6 +252,7 @@ def test_start_record_energy_pkg_without_rapl(base_fs, mocked_pyRAPL): mocked_pyRAPL.record(Device.PKG) + def test_record_bad_type_device(rapl_fs, mocked_pyRAPL): """ call the PyRAPL.record function with non Device parameter @@ -297,6 +298,40 @@ def test_start_record_energy_pkg_and_dram(rapl_fs_with_dram, mocked_pyRAPL): assert mocked_pyRAPL._measure[Device.PKG][0] is not None assert mocked_pyRAPL._measure[Device.DRAM][0] is not None +def test_start_record_energy_all(rapl_fs_with_dram, mocked_pyRAPL): + """ + try to start all energy consumtpion recording + + create an instance of PyRAPL with the file system base_fs and run the record function + + Test if: + - before runing the function the _measure[Device.PKG][0] and _measure[Device.DRAM][0] attribute is None + - the _measure[Device.PKG][0] and _measure[Device.DRAM][0] attribute is not None + """ + assert mocked_pyRAPL._measure[Device.PKG][0] is None + assert mocked_pyRAPL._measure[Device.DRAM][0] is None + mocked_pyRAPL.record() + assert mocked_pyRAPL._measure[Device.PKG][0] is not None + assert mocked_pyRAPL._measure[Device.DRAM][0] is not None + + +def test_start_record_energy_all(rapl_fs, mocked_pyRAPL): + """ + try to start all energy consumtpion recording in a file system without dram + + create an instance of PyRAPL with the file system base_fs and run the record function + + Test if: + - before runing the function the _measure[Device.PKG][0] and _measure[Device.DRAM][0] attribute is None + - the _measure[Device.PKG][0] attribute is not None + - the _measure[Device.PKG][0] attribute is None + """ + assert mocked_pyRAPL._measure[Device.PKG][0] is None + assert mocked_pyRAPL._measure[Device.DRAM][0] is None + mocked_pyRAPL.record() + assert mocked_pyRAPL._measure[Device.PKG][0] is not None + assert mocked_pyRAPL._measure[Device.DRAM][0] is None + ################################## # STOP RECORDING PACKAGE ENERGY # ################################## @@ -333,9 +368,9 @@ def test_stop_record_energy_pkg(rapl_fs, mocked_pyRAPL): assert mocked_pyRAPL._measure[Device.PKG][1] is not None assert mocked_pyRAPL._is_record_running[Device.PKG] is False -################################# +################################ # GET RECORDED PACKAGE ENERGY # -################################# +################################ def test_get_record_pkg_result_without_measure(rapl_fs, mocked_pyRAPL): """ try to get cpu package energy consumtpion without starting recording @@ -360,17 +395,29 @@ def test_get_recorded_energy_bad_type_device(rapl_fs, mocked_pyRAPL): mocked_pyRAPL.recorded_energy(5) +def test_get_recorded_energy_bad_type_device_list(rapl_fs, mocked_pyRAPL): + """ + call the PyRAPL.recorded_energy function with a non Device argument and a Device argument + + Test if: + - A TypeError is raise + """ + mocked_pyRAPL.record(Device.PKG) + mocked_pyRAPL.stop() + with pytest.raises(TypeError): + mocked_pyRAPL.recorded_energy(Device.PKG, 5) + + def test_get_record_energy_pkg(rapl_fs, mocked_pyRAPL): """ try to get cpu package energy consumtpion recording result - create an instance of PyRAPL with the file system base_fs and run the start_record_energy_pkg and the - stop_record_energy_pkg functions after that, run the get_record_pkg_result function + create an instance of PyRAPL with the file system base_fs and run the record function and the + stop functions after that, run the recorded_energy function with Device.PKG parameter Test if: - - the returned value is an integer - - the returned value is equls to th difference between the _measure[Device.PKG][0] attribute and the - _measure[Device.PKG][1] attribute + - the returned value is a dictionary + - the dictionary contains the recorded energy consumption measure for package """ mocked_pyRAPL.record(Device.PKG) mocked_pyRAPL.stop() @@ -379,3 +426,251 @@ def test_get_record_energy_pkg(rapl_fs, mocked_pyRAPL): assert isinstance(val, dict) assert Device.PKG in val assert val[Device.PKG] == correct_value + + +def test_get_record_energy_all(rapl_fs_with_dram, mocked_pyRAPL): + """ + try to get all the recorded energy consumtpion recording result + + create an instance of PyRAPL with the file system base_fs and run the record function and the + stop functions after that, run the recorded_energy function + + Test if: + - the returned value is a dictionary + - the dictionary contains the recorded energy consumption measure for package and dram + """ + mocked_pyRAPL.record() + mocked_pyRAPL.stop() + correct_value = { + Device.PKG: mocked_pyRAPL._measure[Device.PKG][1] - mocked_pyRAPL._measure[Device.PKG][0], + Device.DRAM: mocked_pyRAPL._measure[Device.DRAM][1] - mocked_pyRAPL._measure[Device.DRAM][0] + } + result = mocked_pyRAPL.recorded_energy() + assert isinstance(result, dict) + for device in [Device.PKG, Device.DRAM]: + assert device in result + assert result[Device.PKG] == correct_value[device] + + +def test_get_record_energy_dram_pkg(rapl_fs_with_dram, mocked_pyRAPL): + """ + try to get dram and package energy consumtpion recording result + + create an instance of PyRAPL with the file system base_fs and run the record function and the + stop functions after that, run the recorded_energy function + + Test if: + - the returned value is a dictionary + - the dictionary contains the recorded energy consumption measure for package and dram + """ + mocked_pyRAPL.record(Device.PKG, Device.DRAM) + mocked_pyRAPL.stop() + correct_value = { + Device.PKG: mocked_pyRAPL._measure[Device.PKG][1] - mocked_pyRAPL._measure[Device.PKG][0], + Device.DRAM: mocked_pyRAPL._measure[Device.DRAM][1] - mocked_pyRAPL._measure[Device.DRAM][0] + } + result = mocked_pyRAPL.recorded_energy(Device.PKG, Device.DRAM) + assert isinstance(result, dict) + for device in [Device.PKG, Device.DRAM]: + assert device in result + assert result[Device.PKG] == correct_value[device] + + +############## +# DECORATEUR # +############## +def test_function_return_value(rapl_fs): + """ + test if the decorated function return a value + """ + @measure + def fun(): + return 1 + + assert fun() == 1 + +def test_use_parameter(rapl_fs): + """ + decorate a function that take a parameter and return it + + Test if: + - the given parameter are returned by the decorated function + """ + @measure + def fun(a): + return a + + arg = 3 + assert fun(arg) == arg + + +def test_use_two_parameter(rapl_fs): + """ + decorate a function that take two parameter and return them + + Test if: + - the given parameters are returned by the decorated function + """ + @measure + def fun(a, b): + return (a, b) + + arg1 = 3 + arg2 = 7 + assert fun(arg1, arg2) == (arg1, arg2) + + +def test_use_positional_parameter(rapl_fs): + """ + decorate a function that take positional parameter and return them + + Test if: + - the given parameters are returned by the decorated function + """ + @measure + def fun(*a): + return a + + arg1 = 3 + arg2 = 7 + assert fun(arg1, arg2) == (arg1, arg2) + + +def test_use_mutable_parameter(rapl_fs): + """ + decorate a function that take mutable parameter and modify it + + Test if: + - the given parameter is changed + """ + class Mutable: + def __init__(self, val): + self.val = val + + @measure + def fun(mut): + mut.val = 0 + + mut = Mutable(5) + assert mut.val == 5 + fun(mut) + assert mut.val == 0 + + +def test_param_decorator_monitor_pkg(rapl_fs_with_dram, mocked_pyRAPL): + """ + decorate a function to monitor package power consumption + + Test if: + - the result only contains package power consumption the ellipsed time and the functio name + """ + class Res: + def __init__(self): + self.res = None + + result = Res() + def handler(res): + result.res = res + + @measure(devices=Device.PKG, handler=handler) + def fun(): + return None + + fun() + assert len(result.res.data) == 2 + assert 'PKG' in result.res.data + assert 'TIME' in result.res.data + assert result.res.function_name == fun.__name__ + + +def test_param_decorator_monitor_pkg_dram(rapl_fs_with_dram, mocked_pyRAPL): + """ + decorate a function to monitor package and dram power consumption + + Test if: + - the result only contains package and dram power consumption, the ellipsed time and the functio name + """ + class Res: + def __init__(self): + self.res = None + + result = Res() + def handler(res): + result.res = res + + @measure(devices=[Device.PKG, Device.DRAM], handler=handler) + def fun(): + return None + + fun() + assert len(result.res.data) == 3 + assert 'PKG' in result.res.data + assert 'DRAM' in result.res.data + assert 'TIME' in result.res.data + assert result.res.function_name == fun.__name__ + + +def test_param_decorator_monitor_all(rapl_fs_with_dram, mocked_pyRAPL): + """ + decorate a function to monitor all power consumption + + Test if: + - the result only contains package and dram power consumption, the ellipsed time and the functio name + """ + class Res: + def __init__(self): + self.res = None + + result = Res() + def handler(res): + result.res = res + + @measure(handler=handler) + def fun(): + return None + + fun() + assert len(result.res.data) == 3 + assert 'PKG' in result.res.data + assert 'DRAM' in result.res.data + assert 'TIME' in result.res.data + assert result.res.function_name == fun.__name__ + + +def test_handler_call(rapl_fs, mocked_pyRAPL): + """ + decorate a function an use a mocked handler to handle the measures + + Test if: + - The mocked handle function is called with a Measure parameter + """ + mocked_handler = Mock() + @measure(handler=mocked_handler) + def fun(): + return None + + assert mocked_handler.call_count == 0 + + fun() + + assert mocked_handler.call_count == 1 + assert isinstance(mocked_handler.call_args[0][0], Measure) + + +@patch('builtins.print') +def test_handler_call(mocked_print, rapl_fs, mocked_pyRAPL): + """ + decorate a function an use a the default handler + + Test if: + - the print function was called + """ + @measure() + def fun(): + return None + + assert mocked_print.call_count == 0 + + fun() + + assert mocked_print.call_count == 3 From 5b73abda4584db74105f40d94c70377733a65570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arthur=20d=27Az=C3=A9mar?= Date: Wed, 25 Sep 2019 17:42:07 +0200 Subject: [PATCH 11/11] fix: get energy only in J --- pyRAPL/pyRAPL.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyRAPL/pyRAPL.py b/pyRAPL/pyRAPL.py index 3c33411..bb09e7b 100644 --- a/pyRAPL/pyRAPL.py +++ b/pyRAPL/pyRAPL.py @@ -190,7 +190,7 @@ def energy(self, device): """ return the amount of consumed energy by the device given in parameter since last CPU reset :param Device device: the device to get the power consumption - :return int: the amount of consumed energy of the device since last CPU reset in mJ + :return float: the amount of consumed energy of the device since last CPU reset in J :raise PyRAPLCantRecordEnergyConsumption: if no energy consumtion metric is available for the given device :raise TypeError: if device is not a Device instance """ @@ -203,7 +203,7 @@ def energy(self, device): api_file = self._sys_api[device] api_file.seek(0, 0) - return int(api_file.readline()) + return int(api_file.readline())/1000000 def _begin_record(self, device): energy = self.energy(device) @@ -243,7 +243,7 @@ def stop(self): def _compute_recorded_energy(self, device): - recorded_energy = (self._measure[device][1] - self._measure[device][0])/1000000 + recorded_energy = (self._measure[device][1] - self._measure[device][0]) # print("yolo") self._measure[device][0] = None self._measure[device][1] = None @@ -252,7 +252,7 @@ def _compute_recorded_energy(self, device): def recorded_energy(self, *devices): """ get the latest energy consumption recorded by PyRAPL for the given device - :return: energy (in mJ) consumed between the last record() and stop() function call + :return: energy (in J) consumed between the last record() and stop() function call :raise PyRAPLNoEnergyConsumptionRecordedException: if no energy consumption was recorded :raise TypeError: if device is not a Device instance """