Skip to content

Commit 78f5130

Browse files
Merge pull request #196 from KernelTuner/add-pmt-observer
Add pmt observer
2 parents 7c6f709 + d635714 commit 78f5130

File tree

3 files changed

+131
-2
lines changed

3 files changed

+131
-2
lines changed

examples/cuda/going_green_performance_model.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
raise e
3434

3535
from kernel_tuner.energy import energy
36-
from kernel_tuner.nvml import get_nvml_gr_clocks
36+
from kernel_tuner.observers.nvml import get_nvml_gr_clocks
3737

3838
def get_default_parser():
3939
parser = argparse.ArgumentParser(
@@ -60,7 +60,7 @@ def get_default_parser():
6060
args = parser.parse_args()
6161

6262
ridge_frequency, freqs, nvml_power, fitted_params, scaling = energy.create_power_frequency_model(device=args.device,
63-
n_samples=args.samples,
63+
n_samples=int(args.samples),
6464
verbose=True,
6565
nvidia_smi_fallback=args.nvidia_smi_fallback,
6666
use_locked_clocks=args.locked_clocks)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#!/usr/bin/env python
2+
"""This is the minimal example from the README"""
3+
4+
import json
5+
from collections import OrderedDict
6+
7+
import numpy
8+
from kernel_tuner import tune_kernel
9+
from kernel_tuner.observers.pmt import PMTObserver
10+
11+
def tune():
12+
13+
kernel_string = """
14+
__global__ void vector_add(float *c, float *a, float *b, int n) {
15+
int i = blockIdx.x * block_size_x + threadIdx.x;
16+
if (i<n) {
17+
c[i] = a[i] + b[i];
18+
}
19+
}
20+
"""
21+
22+
size = 80000000
23+
24+
a = numpy.random.randn(size).astype(numpy.float32)
25+
b = numpy.random.randn(size).astype(numpy.float32)
26+
c = numpy.zeros_like(b)
27+
n = numpy.int32(size)
28+
29+
args = [c, a, b, n]
30+
31+
tune_params = dict()
32+
tune_params["block_size_x"] = [128+64*i for i in range(15)]
33+
34+
pmtobserver = PMTObserver(["nvml", "rapl"])
35+
36+
metrics = OrderedDict()
37+
metrics["GPU W"] = lambda p: p["nvml_power"]
38+
metrics["CPU W"] = lambda p: p["rapl_power"]
39+
40+
results, env = tune_kernel("vector_add", kernel_string, size, args, tune_params, observers=[pmtobserver], metrics=metrics, iterations=32)
41+
42+
with open("vector_add.json", 'w') as fp:
43+
json.dump(results, fp)
44+
45+
return results
46+
47+
48+
if __name__ == "__main__":
49+
tune()

kernel_tuner/observers/pmt.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import numpy as np
2+
3+
from kernel_tuner.observers.observer import BenchmarkObserver
4+
5+
# check if pmt is installed
6+
try:
7+
import pmt
8+
except ImportError:
9+
pmt = None
10+
11+
12+
class PMTObserver(BenchmarkObserver):
13+
"""Observer that uses the PMT library to measure power
14+
15+
:param observables: One of:
16+
- A string specifying a single power meter to use
17+
- A list of string, specifying one or more power meters to use
18+
- A dictionary, specifying one or more power meters to use,
19+
including the device identifier. For arduino this should be for
20+
instance "/dev/ttyACM0". For nvml, it should correspond to the GPU
21+
id (e.g. '0', or '1'). For some sensors (such as rapl) the device
22+
id is not used, it should be 'None' in those cases.
23+
This observer will report "<platform>_energy>" and "<platform>_power" for
24+
all specified platforms.
25+
:type observables: string,list/dictionary
26+
27+
"""
28+
29+
def __init__(self, observable=None):
30+
if not pmt:
31+
raise ImportError("could not import pmt")
32+
33+
# User specifices a dictonary of platforms and corresponding device
34+
if type(observable) is dict:
35+
pass
36+
elif type(observable) is list:
37+
# user specifies a list of platforms as observable
38+
observable = dict([(obs, 0) for obs in observable])
39+
else:
40+
# User specifices a string (single platform) as observable
41+
observable = {observable: None}
42+
supported = ["arduino", "jetson", "likwid", "nvml", "rapl", "rocm", "xilinx"]
43+
for obs in observable.keys():
44+
if not obs in supported:
45+
raise ValueError(f"Observable {obs} not in supported: {supported}")
46+
47+
self.pms = [pmt.get_pmt(obs[0], obs[1]) for obs in observable.items()]
48+
self.pm_names = list(observable.keys())
49+
50+
self.begin_states = [None] * len(self.pms)
51+
self.initialize_results(self.pm_names)
52+
53+
def initialize_results(self, pm_names):
54+
self.results = dict()
55+
for pm_name in pm_names:
56+
energy_result_name = f"{pm_name}_energy"
57+
power_result_name = f"{pm_name}_power"
58+
self.results[energy_result_name] = []
59+
self.results[power_result_name] = []
60+
61+
def after_start(self):
62+
self.begin_states = [pm.read() for pm in self.pms]
63+
64+
def after_finish(self):
65+
end_states = [pm.read() for pm in self.pms]
66+
for i in range(len(self.pms)):
67+
begin_state = self.begin_states[i]
68+
end_state = end_states[i]
69+
measured_energy = pmt.joules(begin_state, end_state)
70+
measured_power = pmt.watts(begin_state, end_state)
71+
pm_name = self.pm_names[i]
72+
energy_result_name = f"{pm_name}_energy"
73+
power_result_name = f"{pm_name}_power"
74+
self.results[energy_result_name].append(measured_energy)
75+
self.results[power_result_name].append(measured_power)
76+
77+
def get_results(self):
78+
averages = {key: np.average(values) for key, values in self.results.items()}
79+
self.initialize_results(self.pm_names)
80+
return averages

0 commit comments

Comments
 (0)