Skip to content

Commit

Permalink
Merge pull request #2 from powerapi-ng/pr-belgaid
Browse files Browse the repository at this point in the history
Pr belgaid
  • Loading branch information
altor authored Sep 25, 2019
2 parents 608f6ae + 5b73abd commit fd9a028
Show file tree
Hide file tree
Showing 5 changed files with 556 additions and 114 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 2 additions & 1 deletion pyRAPL/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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, Measure, measure, PyRAPLException, PyRAPLNoEnergyConsumptionRecordedException
from pyRAPL.pyRAPL import PyRAPLNoEnergyConsumptionRecordStartedException, PyRAPLCantRecordEnergyConsumption
# from pyRAPL.pyRAPL import measure_energy

__version__ = "0.0.2"
192 changes: 136 additions & 56 deletions pyRAPL/pyRAPL.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,17 @@
# SOFTWARE.
import os
import logging
import time
import functools

from enum import Enum

try:
import psutil
except ImportError:
logging.getLogger().info("psutil is not installed.")


from enum import Enum

class Device(Enum):
PKG = 1
DRAM = 2
Expand Down Expand Up @@ -64,40 +67,46 @@ 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.sys_api = {
Device.PKG: None,
Device.DRAM: None,
Device.GPU: None
}
self.is_record_running = {

self._is_record_running = {
Device.PKG: False,
Device.DRAM: False,
Device.GPU: False
}
self.measure = {
self._measure = {
Device.PKG: [None, None],
Device.DRAM: [None, None],
Device.GPU: [None, None]
}
self.package_id = None
self.siblings_cpu = None

if self.already_init is False:
self.already_init = True
self._package_id = None
self._siblings_cpu = None
if self._already_init is False:
self._already_init = True
self._uniq_init()

def _uniq_init(self):
"""
Initialize the PyRAPL tool
"""
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._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)

@staticmethod
def _get_siblings_cpu():
Expand Down Expand Up @@ -140,17 +149,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):
"""
Expand All @@ -177,40 +186,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
: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 parameter
:raise TypeError: if device is not a Device instance
"""

if not isinstance(device, Device):
raise TypeError()
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 int(api_file.readline())
return int(api_file.readline())/1000000

def record(self, devices):
def _begin_record(self, device):
energy = self.energy(device)
self._measure[device][0] = energy
self._is_record_running[device] = True

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):
"""
Expand All @@ -219,31 +232,98 @@ 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.measure[device][1] = self.energy(device)
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):

def recorded_energy(self, device):
recorded_energy = (self._measure[device][1] - self._measure[device][0])
# print("yolo")
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
: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 parameter
:raise TypeError: if device is not a Device instance
"""
if not isinstance(device, Device):
raise TypeError()
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

measures[device] = self._compute_recorded_energy(device)

if self.measure[device][0] is None or self.measure[device][1] is None:
raise PyRAPLNoEnergyConsumptionRecordedException
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

measure = self.measure[device][1] - self.measure[device][0]
self.measure[device][0] = None
self.measure[device][1] = None

return measure
class Measure:
def __init__(self, function_name, data):
self.function_name = function_name
self.data = data


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):
# print("default handler")
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)
t1 = time.perf_counter()
val = func(*args, **kwargs)
t2 = time.perf_counter()
sensor.stop()

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

# 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

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)
57 changes: 57 additions & 0 deletions test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import pyRAPL
# from pyRAPL import measure
# from pyRAPL import measure_energy
from time import sleep
from sys import argv



def myhandler(measures):
print("myhandler")
print(measures)


@pyRAPL.measure(handler=myhandler)
def testi(n):
sleep(n)
return n

@pyRAPL.measure()
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
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)
# print("Energy PKG: %f , Energy DRAM: %f"%(energy_pkg,energy_dram))
print(sensor.recorded_energy())
sensor = pyRAPL.PyRAPL()
sensor.record()
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())


def main2():
n = int(argv[1]) if len(argv) >1 else 5
# testi(n)
# fun2(n)
# testi(n)
fun3(n)

if __name__=="__main__":
main2()
Loading

0 comments on commit fd9a028

Please sign in to comment.