summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbin/eval-online-model-accuracy.py95
-rw-r--r--lib/codegen.py33
2 files changed, 102 insertions, 26 deletions
diff --git a/bin/eval-online-model-accuracy.py b/bin/eval-online-model-accuracy.py
index 3391d89..8ddae65 100755
--- a/bin/eval-online-model-accuracy.py
+++ b/bin/eval-online-model-accuracy.py
@@ -28,8 +28,8 @@ import runner
import sys
import time
import io
+import itertools
import yaml
-from aspectc import Repo
from automata import PTA
from codegen import *
from harness import OnboardTimerHarness
@@ -52,22 +52,49 @@ if __name__ == '__main__':
'sleep= '
'timer-pin= '
'trace-filter= '
+ 'timer-freq= '
+ 'timer-type= '
+ 'timestamp-type= '
+ 'energy-type= '
+ 'power-type= '
+ 'timestamp-granularity= '
+ 'energy-granularity= '
+ 'power-granularity= '
)
raw_opts, args = getopt.getopt(sys.argv[1:], "", optspec.split(' '))
+ opt_default = {
+ 'depth' : 3,
+ 'sleep' : 0,
+ 'timer-freq' : 1e6,
+ 'timer-type' : 'uint16_t',
+ 'timestamp-type' : 'uint16_t',
+ 'energy-type' : 'uint32_t',
+ 'power-type' : 'uint16_t',
+ 'timestamp-granularity' : 1e-6,
+ 'power-granularity' : 1e-6,
+ 'energy-granularity' : 1e-12,
+ }
+
for option, parameter in raw_opts:
optname = re.sub(r'^--', '', option)
opt[optname] = parameter
- if 'depth' in opt:
- opt['depth'] = int(opt['depth'])
- else:
- opt['depth'] = 3
+ for key in 'depth sleep'.split():
+ if key in opt:
+ opt[key] = int(opt[key])
+ else:
+ opt[key] = opt_default[key]
- if 'sleep' in opt:
- opt['sleep'] = int(opt['sleep'])
- else:
- opt['sleep'] = 0
+ for key in 'timer-freq timestamp-granularity energy-granularity power-granularity'.split():
+ if key in opt:
+ opt[key] = float(opt[key])
+ else:
+ opt[key] = opt_default[key]
+
+ for key in 'timer-type timestamp-type energy-type power-type'.split():
+ if key not in opt:
+ opt[key] = opt_default[key]
if 'trace-filter' in opt:
trace_filter = []
@@ -96,8 +123,6 @@ if __name__ == '__main__':
if 'dummygen' in driver_definition and 'enum' in driver_definition['dummygen']:
enum = driver_definition['dummygen']['enum']
- repo = Repo('/home/derf/var/projects/multipass/build/repo.acp')
-
pta.set_random_energy_model()
runs = list(pta.dfs(opt['depth'], with_arguments = True, with_parameters = True, trace_filter = opt['trace-filter'], sleep = opt['sleep']))
@@ -112,15 +137,59 @@ if __name__ == '__main__':
real_durations = list()
model_energies = list()
for run in runs:
- accounting_method = get_simulated_accountingmethod(opt['accounting'])(pta, 1e6, 'uint32_t', 'uint32_t', 'uint32_t', 'uint32_t')
+ accounting_method = get_simulated_accountingmethod(opt['accounting'])(pta, opt['timer-freq'], opt['timer-type'], opt['timestamp-type'],
+ opt['power-type'], opt['energy-type'])
real_energy, real_duration, _, _ = pta.simulate(run, accounting = accounting_method)
model_energy = accounting_method.get_energy()
real_energies.append(real_energy)
real_durations.append(real_duration)
model_energies.append(model_energy)
- print('actual energy {:.0f} µJ, modeled energy {:.0f} µJ'.format(real_energy / 1e6, model_energy / 1e6))
measures = regression_measures(np.array(model_energies), np.array(real_energies))
print('SMAPE {:.0f}%, MAE {}'.format(measures['smape'], measures['mae']))
+
+ timer_freqs = [1e3, 2e3, 5e3, 1e4, 2e4, 5e4, 1e5, 2e5, 5e5, 1e6, 2e6, 5e6]
+ timer_types = timestamp_types = power_types = energy_types = 'uint8_t uint16_t uint32_t uint64_t'.split()
+
+ def config_weight(timer_freq, timer_type, ts_type, power_type, energy_type):
+ base_weight = 0
+ for var_type in timer_type, ts_type, power_type, energy_type:
+ if var_type == 'uint8_t':
+ base_weight += 1
+ elif var_type == 'uint16_t':
+ base_weight += 2
+ elif var_type == 'uint32_t':
+ base_weight += 4
+ elif var_type == 'uint64_t':
+ base_weight += 8
+ return base_weight
+
+ mean_errors = list()
+ for timer_freq, timer_type, ts_type, power_type, energy_type in itertools.product(timer_freqs, timer_types, timestamp_types, power_types, energy_types):
+ real_energies = list()
+ real_durations = list()
+ model_energies = list()
+ # duration in µs
+ for sleep_duration in [1e2, 1e3, 1e4, 1e5, 1e6]:
+ runs = pta.dfs(opt['depth'], with_arguments = True, with_parameters = True, trace_filter = opt['trace-filter'], sleep = sleep_duration)
+ for run in runs:
+ accounting_method = get_simulated_accountingmethod(opt['accounting'])(pta, timer_freq, timer_type, ts_type, power_type, energy_type)
+ real_energy, real_duration, _, _ = pta.simulate(run, accounting = accounting_method)
+ model_energy = accounting_method.get_energy()
+ real_energies.append(real_energy)
+ real_durations.append(real_duration)
+ model_energies.append(model_energy)
+ measures = regression_measures(np.array(model_energies), np.array(real_energies))
+ mean_errors.append(((timer_freq, timer_type, ts_type, power_type, energy_type), config_weight(timer_freq, timer_type, ts_type, power_type, energy_type), measures))
+
+ mean_errors.sort(key = lambda x: x[1])
+ mean_errors.sort(key = lambda x: x[2]['mae'])
+
+ for result in mean_errors:
+ config, weight, measures = result
+ print('{} -> {:.0f}% / {}'.format(
+ config,
+ measures['smape'], measures['mae']))
+
sys.exit(0)
diff --git a/lib/codegen.py b/lib/codegen.py
index 1f4bd6f..98eeafe 100644
--- a/lib/codegen.py
+++ b/lib/codegen.py
@@ -98,7 +98,7 @@ class SimulatedAccountingMethod:
* timer counter size (e.g. a 16-bit timer at 1MHz will overflow after 65us)
* variable size for accounting of durations, power and energy values
"""
- def __init__(self, pta: PTA, timer_freq_hz, timer_type, ts_type, power_type, energy_type):
+ def __init__(self, pta: PTA, timer_freq_hz, timer_type, ts_type, power_type, energy_type, ts_granularity = 1e-6, power_granularity = 1e-6, energy_granularity = 1e-12):
"""
Simulate Online Accounting for a given PTA.
@@ -117,14 +117,18 @@ class SimulatedAccountingMethod:
self.energy_class = simulate_int_type(energy_type)
self.current_state = pta.state['UNINITIALIZED']
+ self.ts_granularity = ts_granularity
+ self.power_granularity = power_granularity
+ self.energy_granularity = energy_granularity
+
self.energy = self.energy_class(0)
def _sleep_duration(self, duration_us):
- """
- Return the sleep duration a timer with the classes timer frequency would measure.
+ u"""
+ Return the sleep duration a timer with the configured timer frequency would measure, in µs
I.e., for a 35us sleep with a 50kHz timer (-> one tick per 20us), the OS would likely measure one tick == 20us.
- This is based on the assumption that the timer is reset at each transition.
+ This is based on the assumption that the timer is reset at each transition, so the duration of states may be under-, but not over-estimated
"""
us_per_tick = 1000000 / self.timer_freq_hz
ticks = self.timer_class(int(duration_us // us_per_tick))
@@ -137,18 +141,21 @@ class SimulatedAccountingMethod:
self.current_state = transition.destination
def get_energy(self):
- return self.energy.val
+ """
+ Return total energy in pJ
+ """
+ return self.energy.val * self.energy_granularity * 1e12
class SimulatedStaticStateOnlyAccountingImmediateCalculation(SimulatedAccountingMethod):
- def __init__(self, pta: PTA, timer_freq_hz, timer_type, ts_type, power_type, energy_type):
- super().__init__(pta, timer_freq_hz, timer_type, ts_type, power_type, energy_type)
+ def __init__(self, pta: PTA, *args, **kwargs):
+ super().__init__(pta, *args, **kwargs)
def sleep(self, duration_us):
self.energy += self.ts_class(self._sleep_duration(duration_us)) * self.power_class(int(self.current_state.power))
class SimulatedStaticAccountingImmediateCalculation(SimulatedAccountingMethod):
- def __init__(self, pta: PTA, timer_freq_hz, timer_type, ts_type, power_type, energy_type):
- super().__init__(pta, timer_freq_hz, timer_type, ts_type, power_type, energy_type)
+ def __init__(self, pta: PTA, *args, **kwargs):
+ super().__init__(pta, *args, **kwargs)
def sleep(self, duration_us):
self.energy += self.ts_class(self._sleep_duration(duration_us)) * self.power_class(int(self.current_state.power))
@@ -158,8 +165,8 @@ class SimulatedStaticAccountingImmediateCalculation(SimulatedAccountingMethod):
super().pass_transition(transition)
class SimulatedStaticAccounting(SimulatedAccountingMethod):
- def __init__(self, pta: PTA, timer_freq_hz, timer_type, ts_type, power_type, energy_type):
- super().__init__(pta, timer_freq_hz, timer_type, ts_type, power_type, energy_type)
+ def __init__(self, pta: PTA, *args, **kwargs):
+ super().__init__(pta, *args, **kwargs)
self.time_in_state = dict()
for state_name in pta.state.keys():
self.time_in_state[state_name] = self.ts_class(0)
@@ -185,8 +192,8 @@ class SimulatedStaticAccounting(SimulatedAccountingMethod):
class SimulatedStaticStateOnlyAccounting(SimulatedAccountingMethod):
- def __init__(self, pta: PTA, timer_freq_hz, timer_type, ts_type, power_type, energy_type):
- super().__init__(pta, timer_freq_hz, timer_type, ts_type, power_type, energy_type)
+ def __init__(self, pta: PTA, *args, **kwargs):
+ super().__init__(pta, *args, **kwargs)
self.time_in_state = dict()
for state_name in pta.state.keys():
self.time_in_state[state_name] = self.ts_class(0)