diff options
Diffstat (limited to 'bin')
-rwxr-xr-x | bin/analyze-trace.py | 290 |
1 files changed, 65 insertions, 225 deletions
diff --git a/bin/analyze-trace.py b/bin/analyze-trace.py index 369c5d6..1cc3b89 100755 --- a/bin/analyze-trace.py +++ b/bin/analyze-trace.py @@ -11,6 +11,7 @@ import dfatool.cli import dfatool.plotter import dfatool.utils import dfatool.functions as df +from dfatool.behaviour import SDKBehaviourModel from dfatool.loader import Logfile from dfatool.model import AnalyticModel from dfatool.validation import CrossValidator @@ -34,179 +35,6 @@ def parse_logfile(filename): return loader.load(f, is_trace=True) -def learn_pta(observations, annotation, delta=dict(), delta_param=dict()): - prev_i = annotation.start.offset - prev = "__init__" - prev_non_kernel = prev - meta_observations = list() - n_seen = dict() - - total_latency_us = 0 - - if annotation.kernels: - # ggf. als dict of tuples, für den Fall dass Schleifen verschieden iterieren können? - for i in range(prev_i, annotation.kernels[0].offset): - this = observations[i]["name"] + " @ " + observations[i]["place"] - - if this in n_seen: - if n_seen[this] == 1: - logging.debug( - f"Loop found in {annotation.start.name} {annotation.end.param}: {this} ⟳" - ) - n_seen[this] += 1 - else: - n_seen[this] = 1 - - if not prev in delta: - delta[prev] = set() - delta[prev].add(this) - - # annotation.start.param may be incomplete, for instance in cases - # where DPUs are allocated before the input file is loadeed (and - # thus before the problem size is known). - # Hence, we must use annotation.end.param whenever we deal - # with possibly problem size-dependent behaviour. - if not (prev, this) in delta_param: - delta_param[(prev, this)] = set() - delta_param[(prev, this)].add( - dfatool.utils.param_dict_to_str(annotation.end.param) - ) - - prev = this - prev_i = i + 1 - - total_latency_us += observations[i]["attribute"].get("latency_us", 0) - - meta_observations.append( - { - "name": f"__trace__ {this}", - "param": annotation.end.param, - "attribute": dict( - filter( - lambda kv: not kv[0].startswith("e_"), - observations[i]["param"].items(), - ) - ), - } - ) - prev_non_kernel = prev - - for kernel in annotation.kernels: - prev = prev_non_kernel - for i in range(prev_i, kernel.offset): - this = observations[i]["name"] + " @ " + observations[i]["place"] - - if not prev in delta: - delta[prev] = set() - delta[prev].add(this) - - if not (prev, this) in delta_param: - delta_param[(prev, this)] = set() - delta_param[(prev, this)].add( - dfatool.utils.param_dict_to_str(annotation.end.param) - ) - - # The last iteration (next block) contains a single kernel, - # so we do not increase total_latency_us here. - # However, this means that we will only ever get one latency - # value for each set of kernels with a common problem size, - # despite potentially having far more data at our fingertips. - # We could provide one total_latency_us for each kernel - # (by combining start latency + kernel latency + teardown latency), - # but for that we first need to distinguish between kernel - # components and teardown components in the following block. - - prev = this - prev_i = i + 1 - - meta_observations.append( - { - "name": f"__trace__ {this}", - "param": annotation.end.param, - "attribute": dict( - filter( - lambda kv: not kv[0].startswith("e_"), - observations[i]["param"].items(), - ) - ), - } - ) - - # There is no kernel end signal in the underlying data, so the last iteration also contains a kernel run. - prev = prev_non_kernel - for i in range(prev_i, annotation.end.offset): - this = observations[i]["name"] + " @ " + observations[i]["place"] - - if this in n_seen: - if n_seen[this] == 1: - logging.debug( - f"Loop found in {annotation.start.name} {annotation.end.param}: {this} ⟳" - ) - n_seen[this] += 1 - else: - n_seen[this] = 1 - - if not prev in delta: - delta[prev] = set() - delta[prev].add(this) - - if not (prev, this) in delta_param: - delta_param[(prev, this)] = set() - delta_param[(prev, this)].add( - dfatool.utils.param_dict_to_str(annotation.end.param) - ) - - total_latency_us += observations[i]["attribute"].get("latency_us", 0) - - prev = this - - meta_observations.append( - { - "name": f"__trace__ {this}", - "param": annotation.end.param, - "attribute": dict( - filter( - lambda kv: not kv[0].startswith("e_"), - observations[i]["param"].items(), - ) - ), - } - ) - - if not prev in delta: - delta[prev] = set() - delta[prev].add("__end__") - if not (prev, "__end__") in delta_param: - delta_param[(prev, "__end__")] = set() - delta_param[(prev, "__end__")].add( - dfatool.utils.param_dict_to_str(annotation.end.param) - ) - - for transition, count in n_seen.items(): - meta_observations.append( - { - "name": f"__loop__ {transition}", - "param": annotation.end.param, - "attribute": {"n_iterations": count}, - } - ) - - if total_latency_us: - meta_observations.append( - { - "name": annotation.start.name, - "param": annotation.end.param, - "attribute": {"latency_us": total_latency_us}, - } - ) - - is_loop = dict( - map(lambda kv: (kv[0], True), filter(lambda kv: kv[1] > 1, n_seen.items())) - ) - - return delta, delta_param, meta_observations, is_loop - - def join_annotations(ref, base, new): offset = len(ref) return base + list(map(lambda x: x.apply_offset(offset), new)) @@ -243,68 +71,23 @@ def main(): map(parse_logfile, args.logfiles), ) - delta_by_name = dict() - delta_param_by_name = dict() - is_loop = dict() - for annotation in annotations: - am_tt_param_names = sorted(annotation.start.param.keys()) - if annotation.name not in delta_by_name: - delta_by_name[annotation.name] = dict() - delta_param_by_name[annotation.name] = dict() - _, _, meta_obs, _is_loop = learn_pta( - observations, - annotation, - delta_by_name[annotation.name], - delta_param_by_name[annotation.name], - ) - observations += meta_obs - is_loop.update(_is_loop) + bm = SDKBehaviourModel(observations, annotations) + observations += bm.meta_observations + is_loop = bm.is_loop + am_tt_param_names = bm.am_tt_param_names + delta_by_name = bm.delta_by_name + delta_param_by_name = bm.delta_param_by_name def format_guard(guard): return "∧".join(map(lambda kv: f"{kv[0]}={kv[1]}", guard)) for name in sorted(delta_by_name.keys()): - delta_cond = dict() for t_from, t_to_set in delta_by_name[name].items(): i_to_transition = dict() delta_param_sets = list() to_names = list() transition_guard = dict() - if len(t_to_set) > 1: - am_tt_by_name = { - name: { - "attributes": [t_from], - "param": list(), - t_from: list(), - }, - } - for i, t_to in enumerate(sorted(t_to_set)): - for param in delta_param_by_name[name][(t_from, t_to)]: - am_tt_by_name[name]["param"].append( - dfatool.utils.param_dict_to_list( - dfatool.utils.param_str_to_dict(param), - am_tt_param_names, - ) - ) - am_tt_by_name[name][t_from].append(i) - i_to_transition[i] = t_to - am = AnalyticModel(am_tt_by_name, am_tt_param_names, force_tree=True) - model, info = am.get_fitted() - if type(info(name, t_from)) is df.SplitFunction: - flat_model = info(name, t_from).flatten() - else: - flat_model = list() - logging.warning( - f"Model for {name} {t_from} is {info(name, t_from)}, expected SplitFunction" - ) - - for prefix, output in flat_model: - transition_name = i_to_transition[int(output)] - if transition_name not in transition_guard: - transition_guard[transition_name] = list() - transition_guard[transition_name].append(prefix) - for t_to in sorted(t_to_set): delta_params = delta_param_by_name[name][(t_from, t_to)] delta_param_sets.append(delta_params) @@ -316,7 +99,7 @@ def main(): print(f"{name} {t_from} → {t_to} →") else: print( - f"{name} {t_from} → {t_to} ({' ∨ '.join(map(format_guard, transition_guard.get(t_to, list()))) or '⊤'})" + f"{name} {t_from} → {t_to} ({' ∨ '.join(map(format_guard, bm.transition_guard[t_from].get(t_to, list()))) or '⊤'})" ) for i in range(len(delta_param_sets)): @@ -519,6 +302,63 @@ def main(): ) timing["assess model"] = time.time() - ts + if "paramdetection" in args.show_model or "all" in args.show_model: + for name in model.names: + for attribute in model.attributes(name): + info = param_info(name, attribute) + print( + "{:10s} {:10s} non-param stddev {:f}".format( + name, + attribute, + model.attr_by_name[name][attribute].stats.std_static, + ) + ) + print( + "{:10s} {:10s} param-lut stddev {:f}".format( + name, + attribute, + model.attr_by_name[name][attribute].stats.std_param_lut, + ) + ) + for param in sorted( + model.attr_by_name[name][attribute].stats.std_by_param.keys() + ): + print( + "{:10s} {:10s} {:10s} stddev {:f}".format( + name, + attribute, + param, + model.attr_by_name[name][attribute].stats.std_by_param[ + param + ], + ) + ) + for arg_index in range(model.attr_by_name[name][attribute].arg_count): + print( + "{:10s} {:10s} {:10s} stddev {:f}".format( + name, + attribute, + f"arg{arg_index}", + model.attr_by_name[name][attribute].stats.std_by_arg[ + arg_index + ], + ) + ) + if type(info) is df.AnalyticFunction: + for param_name in sorted(info.fit_by_param.keys(), key=str): + param_fit = info.fit_by_param[param_name]["results"] + for function_type in sorted(param_fit.keys()): + function_rmsd = param_fit[function_type]["rmsd"] + print( + "{:10s} {:10s} {:10s} mean {:10s} RMSD {:.0f}".format( + name, + attribute, + str(param_name), + function_type, + function_rmsd, + ) + ) + if "static" in args.show_model or "all" in args.show_model: print("--- static model ---") for name in sorted(model.names): |