From 4ccecdffe23e2025a6c0c62ead960017ad6408b9 Mon Sep 17 00:00:00 2001 From: Ben van Werkhoven Date: Thu, 3 Oct 2024 17:08:29 +0200 Subject: [PATCH 1/4] base implementation for pmt continuous observer --- kernel_tuner/observers/pmt.py | 39 ++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/kernel_tuner/observers/pmt.py b/kernel_tuner/observers/pmt.py index bb1d76bd..4f85b1b3 100644 --- a/kernel_tuner/observers/pmt.py +++ b/kernel_tuner/observers/pmt.py @@ -30,7 +30,7 @@ class PMTObserver(BenchmarkObserver): """ - def __init__(self, observable=None): + def __init__(self, observable=None, use_continuous_observer=False, continuous_duration=1): if not pmt: raise ImportError("could not import pmt") @@ -54,6 +54,9 @@ def __init__(self, observable=None): self.begin_states = [None] * len(self.pms) self.initialize_results(self.pm_names) + if use_continuous_observer: + self.continuous_observer = ContinuousObserver("pmt", [], self, continuous_duration=continuous_duration) + def initialize_results(self, pm_names): self.results = dict() for pm_name in pm_names: @@ -82,3 +85,37 @@ def get_results(self): averages = {key: np.average(values) for key, values in self.results.items()} self.initialize_results(self.pm_names) return averages + + +class PMTContinuousObserver(ContinuousObserver): + """Generic observer that measures power while and continuous benchmarking. + + To support continuous benchmarking an Observer should support: + a .read_power() method, which the ContinuousObserver can call to read power in Watt + """ + def before_start(self): + pass + + def after_start(self): + self.parent.after_start() + + def during(self): + pass + + def after_finish(self): + self.parent.after_finish() + + def get_results(self): + average_kernel_execution_time_ms = self.results["time"] + + averages = {key: np.average(values) for key, values in self.results.items()} + self.parent.initialize_results(self.pm_names) + + # correct energy measurement, because current _energy number is collected over the entire duration + # we estimate energy as the average power over the continuous duration times the kernel execution time + for pm_name in pm_names: + energy_result_name = f"{pm_name}_energy" + power_result_name = f"{pm_name}_power" + averages[energy_result_name] = averages[power_result_name] * (average_kernel_execution_time_ms / 1e3) + + return averages From 29591abc1f5e597c727596fa91a775545a643490 Mon Sep 17 00:00:00 2001 From: Ben van Werkhoven Date: Thu, 3 Oct 2024 17:12:00 +0200 Subject: [PATCH 2/4] code quality fixes --- kernel_tuner/observers/pmt.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kernel_tuner/observers/pmt.py b/kernel_tuner/observers/pmt.py index 4f85b1b3..4caf5903 100644 --- a/kernel_tuner/observers/pmt.py +++ b/kernel_tuner/observers/pmt.py @@ -1,6 +1,6 @@ import numpy as np -from kernel_tuner.observers.observer import BenchmarkObserver +from kernel_tuner.observers.observer import BenchmarkObserver, ContinuousObserver # check if pmt is installed try: @@ -94,12 +94,14 @@ class PMTContinuousObserver(ContinuousObserver): a .read_power() method, which the ContinuousObserver can call to read power in Watt """ def before_start(self): + """ Override default method in ContinuousObserver """ pass def after_start(self): self.parent.after_start() def during(self): + """ Override default method in ContinuousObserver """ pass def after_finish(self): @@ -113,7 +115,7 @@ def get_results(self): # correct energy measurement, because current _energy number is collected over the entire duration # we estimate energy as the average power over the continuous duration times the kernel execution time - for pm_name in pm_names: + for pm_name in self.parent.pm_names: energy_result_name = f"{pm_name}_energy" power_result_name = f"{pm_name}_power" averages[energy_result_name] = averages[power_result_name] * (average_kernel_execution_time_ms / 1e3) From 6050a9448c9ad851da3560a19ff09c9431436c72 Mon Sep 17 00:00:00 2001 From: Leon Oostrum Date: Thu, 3 Oct 2024 20:40:16 +0200 Subject: [PATCH 3/4] Fix PMTContinuousObserver --- kernel_tuner/observers/pmt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel_tuner/observers/pmt.py b/kernel_tuner/observers/pmt.py index 4caf5903..91e0762c 100644 --- a/kernel_tuner/observers/pmt.py +++ b/kernel_tuner/observers/pmt.py @@ -55,7 +55,7 @@ def __init__(self, observable=None, use_continuous_observer=False, continuous_du self.initialize_results(self.pm_names) if use_continuous_observer: - self.continuous_observer = ContinuousObserver("pmt", [], self, continuous_duration=continuous_duration) + self.continuous_observer = PMTContinuousObserver("pmt", [], self, continuous_duration=continuous_duration) def initialize_results(self, pm_names): self.results = dict() @@ -111,7 +111,7 @@ def get_results(self): average_kernel_execution_time_ms = self.results["time"] averages = {key: np.average(values) for key, values in self.results.items()} - self.parent.initialize_results(self.pm_names) + self.parent.initialize_results(self.parent.pm_names) # correct energy measurement, because current _energy number is collected over the entire duration # we estimate energy as the average power over the continuous duration times the kernel execution time From 0a96c865e0ed964738a8c8674632d87cd447d3f1 Mon Sep 17 00:00:00 2001 From: Ben van Werkhoven Date: Fri, 4 Oct 2024 13:47:34 +0200 Subject: [PATCH 4/4] expand PMTObserver documentation --- kernel_tuner/observers/pmt.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/kernel_tuner/observers/pmt.py b/kernel_tuner/observers/pmt.py index 91e0762c..1a795d15 100644 --- a/kernel_tuner/observers/pmt.py +++ b/kernel_tuner/observers/pmt.py @@ -28,6 +28,22 @@ class PMTObserver(BenchmarkObserver): :type observables: string,list/dictionary + + :param use_continuous_observer: + Boolean to control whether or not to measure power/energy using + Kernel Tuner's continuous benchmarking mode. This improves measurement + accuracy when using internal power sensors, such as NVML or ROCM, + which have limited sampling frequency and might return averages + instead of instantaneous power readings. Default value: False. + + :type use_continuous_observer: boolean + + + :param continuous_duration: + Number of seconds to measure continuously for. + + :type continuous_duration: scalar + """ def __init__(self, observable=None, use_continuous_observer=False, continuous_duration=1):