1
1
import numpy as np
2
2
3
- from kernel_tuner .observers .observer import BenchmarkObserver
3
+ from kernel_tuner .observers .observer import BenchmarkObserver , ContinuousObserver
4
4
5
5
# check if pmt is installed
6
6
try :
@@ -28,9 +28,25 @@ class PMTObserver(BenchmarkObserver):
28
28
29
29
:type observables: string,list/dictionary
30
30
31
+
32
+ :param use_continuous_observer:
33
+ Boolean to control whether or not to measure power/energy using
34
+ Kernel Tuner's continuous benchmarking mode. This improves measurement
35
+ accuracy when using internal power sensors, such as NVML or ROCM,
36
+ which have limited sampling frequency and might return averages
37
+ instead of instantaneous power readings. Default value: False.
38
+
39
+ :type use_continuous_observer: boolean
40
+
41
+
42
+ :param continuous_duration:
43
+ Number of seconds to measure continuously for.
44
+
45
+ :type continuous_duration: scalar
46
+
31
47
"""
32
48
33
- def __init__ (self , observable = None ):
49
+ def __init__ (self , observable = None , use_continuous_observer = False , continuous_duration = 1 ):
34
50
if not pmt :
35
51
raise ImportError ("could not import pmt" )
36
52
@@ -54,6 +70,9 @@ def __init__(self, observable=None):
54
70
self .begin_states = [None ] * len (self .pms )
55
71
self .initialize_results (self .pm_names )
56
72
73
+ if use_continuous_observer :
74
+ self .continuous_observer = PMTContinuousObserver ("pmt" , [], self , continuous_duration = continuous_duration )
75
+
57
76
def initialize_results (self , pm_names ):
58
77
self .results = dict ()
59
78
for pm_name in pm_names :
@@ -82,3 +101,39 @@ def get_results(self):
82
101
averages = {key : np .average (values ) for key , values in self .results .items ()}
83
102
self .initialize_results (self .pm_names )
84
103
return averages
104
+
105
+
106
+ class PMTContinuousObserver (ContinuousObserver ):
107
+ """Generic observer that measures power while and continuous benchmarking.
108
+
109
+ To support continuous benchmarking an Observer should support:
110
+ a .read_power() method, which the ContinuousObserver can call to read power in Watt
111
+ """
112
+ def before_start (self ):
113
+ """ Override default method in ContinuousObserver """
114
+ pass
115
+
116
+ def after_start (self ):
117
+ self .parent .after_start ()
118
+
119
+ def during (self ):
120
+ """ Override default method in ContinuousObserver """
121
+ pass
122
+
123
+ def after_finish (self ):
124
+ self .parent .after_finish ()
125
+
126
+ def get_results (self ):
127
+ average_kernel_execution_time_ms = self .results ["time" ]
128
+
129
+ averages = {key : np .average (values ) for key , values in self .results .items ()}
130
+ self .parent .initialize_results (self .parent .pm_names )
131
+
132
+ # correct energy measurement, because current _energy number is collected over the entire duration
133
+ # we estimate energy as the average power over the continuous duration times the kernel execution time
134
+ for pm_name in self .parent .pm_names :
135
+ energy_result_name = f"{ pm_name } _energy"
136
+ power_result_name = f"{ pm_name } _power"
137
+ averages [energy_result_name ] = averages [power_result_name ] * (average_kernel_execution_time_ms / 1e3 )
138
+
139
+ return averages
0 commit comments