diff options
-rwxr-xr-x | bin/analyze-archive.py | 24 | ||||
-rwxr-xr-x | bin/test.py | 24 | ||||
-rwxr-xr-x | lib/dfatool.py | 98 |
3 files changed, 75 insertions, 71 deletions
diff --git a/bin/analyze-archive.py b/bin/analyze-archive.py index f203038..dbfe0d1 100755 --- a/bin/analyze-archive.py +++ b/bin/analyze-archive.py @@ -79,17 +79,17 @@ def print_text_model_data(model, pm, pq, lm, lq, am, ai, aq): print(r'key attribute $1 - \frac{\sigma_X}{...}$') for state_or_tran in model.by_name.keys(): for attribute in model.attributes(state_or_tran): - print('{} {} {:.8f}'.format(state_or_tran, attribute, model.generic_param_dependence_ratio(state_or_tran, attribute))) + print('{} {} {:.8f}'.format(state_or_tran, attribute, model.stats.generic_param_dependence_ratio(state_or_tran, attribute))) print('') print(r'key attribute parameter $1 - \frac{...}{...}$') for state_or_tran in model.by_name.keys(): for attribute in model.attributes(state_or_tran): for param in model.parameters(): - print('{} {} {} {:.8f}'.format(state_or_tran, attribute, param, model.param_dependence_ratio(state_or_tran, attribute, param))) + print('{} {} {} {:.8f}'.format(state_or_tran, attribute, param, model.stats.param_dependence_ratio(state_or_tran, attribute, param))) if state_or_tran in model._num_args: for arg_index in range(model._num_args[state_or_tran]): - print('{} {} {:d} {:.8f}'.format(state_or_tran, attribute, arg_index, model.arg_dependence_ratio(state_or_tran, attribute, arg_index))) + print('{} {} {:d} {:.8f}'.format(state_or_tran, attribute, arg_index, model.stats.arg_dependence_ratio(state_or_tran, attribute, arg_index))) if __name__ == '__main__': @@ -168,20 +168,20 @@ if __name__ == '__main__': print('{:10s}: {:.0f} µW ({:.2f})'.format( state, static_model(state, 'power'), - model.generic_param_dependence_ratio(state, 'power'))) + model.stats.generic_param_dependence_ratio(state, 'power'))) for param in model.parameters(): print('{:10s} dependence on {:15s}: {:.2f}'.format( '', param, - model.param_dependence_ratio(state, 'power', param))) + model.stats.param_dependence_ratio(state, 'power', param))) for trans in model.transitions(): print('{:10s}: {:.0f} / {:.0f} / {:.0f} pJ ({:.2f} / {:.2f} / {:.2f})'.format( trans, static_model(trans, 'energy'), static_model(trans, 'rel_energy_prev'), static_model(trans, 'rel_energy_next'), - model.generic_param_dependence_ratio(trans, 'energy'), - model.generic_param_dependence_ratio(trans, 'rel_energy_prev'), - model.generic_param_dependence_ratio(trans, 'rel_energy_next'))) + model.stats.generic_param_dependence_ratio(trans, 'energy'), + model.stats.generic_param_dependence_ratio(trans, 'rel_energy_prev'), + model.stats.generic_param_dependence_ratio(trans, 'rel_energy_next'))) print('{:10s}: {:.0f} µs'.format(trans, static_model(trans, 'duration'))) static_quality = model.assess(static_model) @@ -200,14 +200,14 @@ if __name__ == '__main__': for attribute in model.attributes(state): info = param_info(state, attribute) print('{:10s} {:10s} non-param stddev {:f}'.format( - state, attribute, model.stats[state][attribute]['std_static'] + state, attribute, model.stats.stats[state][attribute]['std_static'] )) print('{:10s} {:10s} param-lut stddev {:f}'.format( - state, attribute, model.stats[state][attribute]['std_param_lut'] + state, attribute, model.stats.stats[state][attribute]['std_param_lut'] )) - for param in sorted(model.stats[state][attribute]['std_by_param'].keys()): + for param in sorted(model.stats.stats[state][attribute]['std_by_param'].keys()): print('{:10s} {:10s} {:10s} stddev {:f}'.format( - state, attribute, param, model.stats[state][attribute]['std_by_param'][param] + state, attribute, param, model.stats.stats[state][attribute]['std_by_param'][param] )) if info != None: for param_name in sorted(info['fit_result'].keys(), key=str): diff --git a/bin/test.py b/bin/test.py index 433b423..0b015f4 100755 --- a/bin/test.py +++ b/bin/test.py @@ -43,18 +43,18 @@ class TestStaticModel(unittest.TestCase): self.assertAlmostEqual(static_model('stopListening', 'duration'), 260, places=0) self.assertAlmostEqual(static_model('write_nb', 'duration'), 510, places=0) - self.assertAlmostEqual(model.param_dependence_ratio('POWERDOWN', 'power', 'datarate'), 0, places=2) - self.assertAlmostEqual(model.param_dependence_ratio('POWERDOWN', 'power', 'txbytes'), 0, places=2) - self.assertAlmostEqual(model.param_dependence_ratio('POWERDOWN', 'power', 'txpower'), 0, places=2) - self.assertAlmostEqual(model.param_dependence_ratio('RX', 'power', 'datarate'), 0.99, places=2) - self.assertAlmostEqual(model.param_dependence_ratio('RX', 'power', 'txbytes'), 0, places=2) - self.assertAlmostEqual(model.param_dependence_ratio('RX', 'power', 'txpower'), 0.01, places=2) - self.assertAlmostEqual(model.param_dependence_ratio('STANDBY1', 'power', 'datarate'), 0.04, places=2) - self.assertAlmostEqual(model.param_dependence_ratio('STANDBY1', 'power', 'txbytes'), 0.35, places=2) - self.assertAlmostEqual(model.param_dependence_ratio('STANDBY1', 'power', 'txpower'), 0.32, places=2) - self.assertAlmostEqual(model.param_dependence_ratio('TX', 'power', 'datarate'), 1, places=2) - self.assertAlmostEqual(model.param_dependence_ratio('TX', 'power', 'txbytes'), 0.09, places=2) - self.assertAlmostEqual(model.param_dependence_ratio('TX', 'power', 'txpower'), 1, places=2) + self.assertAlmostEqual(model.stats.param_dependence_ratio('POWERDOWN', 'power', 'datarate'), 0, places=2) + self.assertAlmostEqual(model.stats.param_dependence_ratio('POWERDOWN', 'power', 'txbytes'), 0, places=2) + self.assertAlmostEqual(model.stats.param_dependence_ratio('POWERDOWN', 'power', 'txpower'), 0, places=2) + self.assertAlmostEqual(model.stats.param_dependence_ratio('RX', 'power', 'datarate'), 0.99, places=2) + self.assertAlmostEqual(model.stats.param_dependence_ratio('RX', 'power', 'txbytes'), 0, places=2) + self.assertAlmostEqual(model.stats.param_dependence_ratio('RX', 'power', 'txpower'), 0.01, places=2) + self.assertAlmostEqual(model.stats.param_dependence_ratio('STANDBY1', 'power', 'datarate'), 0.04, places=2) + self.assertAlmostEqual(model.stats.param_dependence_ratio('STANDBY1', 'power', 'txbytes'), 0.35, places=2) + self.assertAlmostEqual(model.stats.param_dependence_ratio('STANDBY1', 'power', 'txpower'), 0.32, places=2) + self.assertAlmostEqual(model.stats.param_dependence_ratio('TX', 'power', 'datarate'), 1, places=2) + self.assertAlmostEqual(model.stats.param_dependence_ratio('TX', 'power', 'txbytes'), 0.09, places=2) + self.assertAlmostEqual(model.stats.param_dependence_ratio('TX', 'power', 'txpower'), 1, places=2) param_model, param_info = model.get_fitted() self.assertEqual(param_info('POWERDOWN', 'power'), None) diff --git a/lib/dfatool.py b/lib/dfatool.py index 5c55993..1dc5df5 100755 --- a/lib/dfatool.py +++ b/lib/dfatool.py @@ -283,6 +283,52 @@ def _preprocess_measurement(measurement): return processed_data +class ParamStats: + + def __init__(self, by_name, by_param, parameter_names, arg_count): + self.stats = dict() + # Note: This is deliberately single-threaded. The overhead incurred + # by multiprocessing is higher than the speed gained by parallel + # computation of statistics measures. + for state_or_tran in by_name.keys(): + self.stats[state_or_tran] = dict() + for attribute in by_name[state_or_tran]['attributes']: + self.stats[state_or_tran][attribute] = compute_param_statistics(by_name, by_param, parameter_names, arg_count, state_or_tran, attribute) + + def generic_param_independence_ratio(self, state_or_trans, attribute, use_corrcoef = False): + statistics = self.stats[state_or_trans][attribute] + if use_corrcoef: + # not supported + return 0 + if statistics['std_static'] == 0: + return 0 + return statistics['std_param_lut'] / statistics['std_static'] + + def generic_param_dependence_ratio(self, state_or_trans, attribute, use_corrcoef = False): + return 1 - self.generic_param_independence_ratio(state_or_trans, attribute, use_corrcoef) + + def param_independence_ratio(self, state_or_trans, attribute, param, use_corrcoef = False): + statistics = self.stats[state_or_trans][attribute] + if use_corrcoef: + return 1 - np.abs(statistics['corr_by_param'][param]) + if statistics['std_by_param'][param] == 0: + return 0 + return statistics['std_param_lut'] / statistics['std_by_param'][param] + + def param_dependence_ratio(self, state_or_trans, attribute, param, use_corrcoef = False): + return 1 - self.param_independence_ratio(state_or_trans, attribute, param, use_corrcoef) + + def arg_independence_ratio(self, state_or_trans, attribute, arg_index, use_corrcoef = False): + statistics = self.stats[state_or_trans][attribute] + if use_corrcoef: + return 1 - np.abs(statistics['corr_by_arg'][arg_index]) + if statistics['std_by_arg'][arg_index] == 0: + return 0 + return statistics['std_param_lut'] / statistics['std_by_arg'][arg_index] + + def arg_dependence_ratio(self, state_or_trans, attribute, arg_index, use_corrcoef = False): + return 1 - self.arg_independence_ratio(state_or_trans, attribute, arg_index, use_corrcoef) + class RawData: """ Loader for hardware model traces measured with MIMOSA. @@ -733,7 +779,6 @@ class EnergyModel: self.by_name = {} self.by_param = {} self.by_trace = {} - self.stats = {} self.cache = {} np.seterr('raise') self._parameter_names = sorted(self.traces[0]['trace'][0]['parameter'].keys()) @@ -782,20 +827,12 @@ class EnergyModel: def _compute_all_param_statistics(self): - # Note: This is deliberately single-threaded. The overhead incurred - # by multiprocessing is higher than the speed gained by parallel - # computation of statistics measures. - for state_or_trans in self.by_name.keys(): - self.stats[state_or_trans] = {} - for key in self.by_name[state_or_trans]['attributes']: - if key in self.by_name[state_or_trans]: - self.stats[state_or_trans][key] = compute_param_statistics(self.by_name, self.by_param, self._parameter_names, self._num_args, state_or_trans, key) + self.stats = ParamStats(self.by_name, self.by_param, self._parameter_names, self._num_args) @classmethod def from_model(self, model_data, parameter_names): self.by_name = {} self.by_param = {} - self.stats = {} np.seterr('raise') self._parameter_names = parameter_names for state_or_tran, values in model_data.items(): @@ -849,55 +886,22 @@ class EnergyModel: self._add_data_to_aggregate(self.by_name, elem['name'], elem) self._add_data_to_aggregate(self.by_param, (elem['name'], tuple(_elem_param_and_arg_list(elem))), elem) - def generic_param_independence_ratio(self, state_or_trans, key): - statistics = self.stats[state_or_trans][key] - if self._use_corrcoef: - return 0 - if statistics['std_static'] == 0: - return 0 - return statistics['std_param_lut'] / statistics['std_static'] - - def generic_param_dependence_ratio(self, state_or_trans, key): - return 1 - self.generic_param_independence_ratio(state_or_trans, key) - - def param_independence_ratio(self, state_or_trans, key, param): - statistics = self.stats[state_or_trans][key] - if self._use_corrcoef: - return 1 - np.abs(statistics['corr_by_param'][param]) - if statistics['std_by_param'][param] == 0: - return 0 - return statistics['std_param_lut'] / statistics['std_by_param'][param] - - def param_dependence_ratio(self, state_or_trans, key, param): - return 1 - self.param_independence_ratio(state_or_trans, key, param) - # This heuristic is very similar to the "function is not much better than # median" checks in get_fitted. So far, doing it here as well is mostly # a performance and not an algorithm quality decision. # --df, 2018-04-18 def depends_on_param(self, state_or_trans, key, param): if self._use_corrcoef: - return self.param_dependence_ratio(state_or_trans, key, param) > 0.1 + return self.stats.param_dependence_ratio(state_or_trans, key, param, self._use_corrcoef) > 0.1 else: - return self.param_dependence_ratio(state_or_trans, key, param) > 0.5 - - def arg_independence_ratio(self, state_or_trans, key, arg_index): - statistics = self.stats[state_or_trans][key] - if self._use_corrcoef: - return 1 - np.abs(statistics['corr_by_arg'][arg_index]) - if statistics['std_by_arg'][arg_index] == 0: - return 0 - return statistics['std_param_lut'] / statistics['std_by_arg'][arg_index] - - def arg_dependence_ratio(self, state_or_trans, key, arg_index): - return 1 - self.arg_independence_ratio(state_or_trans, key, arg_index) + return self.stats.param_dependence_ratio(state_or_trans, key, param, self._use_corrcoef) > 0.5 # See notes on depends_on_param def depends_on_arg(self, state_or_trans, key, param): if self._use_corrcoef: - return self.arg_dependence_ratio(state_or_trans, key, param) > 0.1 + return self.stats.arg_dependence_ratio(state_or_trans, key, param, self._use_corrcoef) > 0.1 else: - return self.arg_dependence_ratio(state_or_trans, key, param) > 0.5 + return self.stats.arg_dependence_ratio(state_or_trans, key, param, self._use_corrcoef) > 0.5 def _get_model_from_dict(self, model_dict, model_function): model = {} |