summaryrefslogtreecommitdiff
path: root/bin/analyze-trace.py
diff options
context:
space:
mode:
Diffstat (limited to 'bin/analyze-trace.py')
-rwxr-xr-xbin/analyze-trace.py129
1 files changed, 110 insertions, 19 deletions
diff --git a/bin/analyze-trace.py b/bin/analyze-trace.py
index 9aa72d8..d7f45e7 100755
--- a/bin/analyze-trace.py
+++ b/bin/analyze-trace.py
@@ -39,25 +39,48 @@ def learn_pta(observations, annotation, delta=dict(), delta_param=dict()):
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.start.param)
+ 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.start.param,
+ "param": annotation.end.param,
"attribute": dict(
filter(
lambda kv: not kv[0].startswith("e_"),
@@ -76,17 +99,30 @@ def learn_pta(observations, annotation, delta=dict(), delta_param=dict()):
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.start.param)
+ 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.start.param,
+ "param": annotation.end.param,
"attribute": dict(
filter(
lambda kv: not kv[0].startswith("e_"),
@@ -101,19 +137,33 @@ def learn_pta(observations, annotation, delta=dict(), delta_param=dict()):
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.start.param)
+ 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.start.param,
+ "param": annotation.end.param,
"attribute": dict(
filter(
lambda kv: not kv[0].startswith("e_"),
@@ -129,9 +179,32 @@ def learn_pta(observations, annotation, delta=dict(), delta_param=dict()):
if not (prev, "__end__") in delta_param:
delta_param[(prev, "__end__")] = set()
delta_param[(prev, "__end__")].add(
- dfatool.utils.param_dict_to_str(annotation.start.param)
+ 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
+
+ return delta, delta_param, meta_observations, is_loop
def join_annotations(ref, base, new):
@@ -172,18 +245,20 @@ def main():
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 = learn_pta(
+ _, _, 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)
def format_guard(guard):
return "∧".join(map(lambda kv: f"{kv[0]}={kv[1]}", guard))
@@ -216,7 +291,13 @@ def main():
i_to_transition[i] = t_to
am = AnalyticModel(am_tt_by_name, am_tt_param_names, force_tree=True)
model, info = am.get_fitted()
- flat_model = info(name, t_from).flatten()
+ 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)]
@@ -229,9 +310,14 @@ def main():
delta_param_sets.append(delta_params)
to_names.append(t_to)
n_confs = len(delta_params)
- print(
- f"{name} {t_from} → {t_to} ({' ∨ '.join(map(format_guard, transition_guard.get(t_to, list()))) or '⊤'})"
- )
+ if is_loop.get(t_from, False) and is_loop.get(t_to, False):
+ print(f"{name} {t_from} → {t_to} ⟳")
+ elif is_loop.get(t_from, False):
+ 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 '⊤'})"
+ )
for i in range(len(delta_param_sets)):
for j in range(i + 1, len(delta_param_sets)):
@@ -239,12 +325,17 @@ def main():
intersection = delta_param_sets[i].intersection(
delta_param_sets[j]
)
- logging.error(
- f"Outbound transitions of <{t_from}> are not deterministic: <{to_names[i]}> and <{to_names[j]}> are both taken for {intersection}"
- )
- raise RuntimeError(
- f"Outbound transitions of <{t_from}> are not deterministic"
- )
+ if is_loop.get(t_from, False):
+ logging.debug(
+ f"Loop transition <{t_from}>: <{to_names[i]}> and <{to_names[j]}> are both taken for {intersection}"
+ )
+ else:
+ logging.error(
+ f"Outbound transitions of <{t_from}> are not deterministic: <{to_names[i]}> and <{to_names[j]}> are both taken for {intersection}"
+ )
+ raise RuntimeError(
+ f"Outbound transitions of <{t_from}> are not deterministic"
+ )
print("")