summaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
Diffstat (limited to 'bin')
-rwxr-xr-xbin/analyze-trace.py290
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):