From 16aa392a3c08c8769cc30bdbc1830a31f9b0808e Mon Sep 17 00:00:00 2001 From: bd Date: Sat, 11 Oct 2025 14:05:13 -0400 Subject: try blacklisting _read_from_self from results Still determining the reason _read_from_self has an abnormally large average callback and biases the results. --- nemesis/causal_event_loop.py | 28 ++++++++++++++++++++++++---- nemesis/html_gen.py | 23 ++++++++++++++++++----- nemesis/nemesis.py | 9 +++++++-- 3 files changed, 49 insertions(+), 11 deletions(-) diff --git a/nemesis/causal_event_loop.py b/nemesis/causal_event_loop.py index 3586b18..2fe8435 100644 --- a/nemesis/causal_event_loop.py +++ b/nemesis/causal_event_loop.py @@ -74,12 +74,12 @@ class TimeAwareMixin: def create_subclass(base_class): - class NewSubclass(base_class, TimeAwareMixin): + class CausalHandle(base_class, TimeAwareMixin): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) TimeAwareMixin.__init__(self) - return NewSubclass + return CausalHandle # make all the subclasses inherit from TimeAwareHandle as well @@ -271,7 +271,8 @@ class CausalEventLoop(asyncio.SelectorEventLoop): except AssertionError as e: print(f"Assertion failed: {e}") sys.exit(1) - self._completed_coros.append((_format_handle(handle), wait_time)) + if not self._is_blacklisted(handle): + self._completed_coros.append((_format_handle(handle), wait_time)) except Exception: traceback.print_exc() finally: @@ -331,7 +332,15 @@ class CausalEventLoop(asyncio.SelectorEventLoop): def _get_pause_for_io(self, handle, io_time): time_interval = (handle.register_time, io_time) - return self._get_pause_time(time_interval) + p_time = self._get_pause_time(time_interval) + + try: + assert p_time >= 0, f"calculated pause time on {_format_handle(handle)} was found to be {p_time:.4f}!" + except AssertionError as e: + print(f"Assertion failed: {e}") + sys.exit(1) + + return p_time def _get_pause_for_pause_time(self, handle, exit_time): time_interval = (handle.time_entered_pause_buffer, exit_time) @@ -361,6 +370,17 @@ class CausalEventLoop(asyncio.SelectorEventLoop): overlap_end = min(a_end, b_end) return overlap_end - overlap_start + def _is_blacklisted(self, handle): + blacklist = ['_read_from_self'] + cb = handle._callback + if isinstance(getattr(cb, '__self__', None), asyncio.tasks.Task): + if cb.__self__.get_coro().__name__ in blacklist: + return True + else: + if getattr(cb, '__name__', None) in blacklist: + return True + return False + class CausalEventLoopPolicy(asyncio.DefaultEventLoopPolicy): def new_event_loop(self): diff --git a/nemesis/html_gen.py b/nemesis/html_gen.py index 841fbbc..60d0c06 100644 --- a/nemesis/html_gen.py +++ b/nemesis/html_gen.py @@ -7,7 +7,7 @@ def get_color(name): color_index = int(hash_object.hexdigest(), 16) % 360 return f'hsl({color_index}, 100%, 50%)' -def plot_results(results, filename): +def plot_results(results, output_file, input_file): fig = make_subplots(rows=4, cols=1) for i, (coro_name, x_values) in enumerate(results.items(), start=1): @@ -16,6 +16,8 @@ def plot_results(results, filename): y_throughput_list = [] y_max_latency_list = [] y_num_callbacks_list = [] + latency_hover_text = [] + max_latency_hover_text = [] for speedup, experiments in x_values.items(): for experiment in experiments: @@ -29,16 +31,24 @@ def plot_results(results, filename): y_num_callbacks_list.append(num_callbacks) # handle average latency graph + if num_callbacks > 0: + breakdown = "
".join([f" {cb[0]}: {round(cb[1], 4)}" for cb in completed_callbacks]) total_wait = sum([cb[1] for cb in completed_callbacks]) - max_wait = max([cb[1] for cb in completed_callbacks]) + max_cb = max(completed_callbacks, key=lambda cb: cb[1]) + latency = total_wait / num_callbacks - y_max_latency_list.append(max_wait) + y_max_latency_list.append(max_cb[1]) y_latency_list.append(latency) + else: - y_latency_list.append(0) + latency = 0 + y_latency_list.append(latency) + + latency_hover_text.append(f"{coro_name}
Speedup: {speedup}
Average Wait: {round(latency, 4)}
Breakdown:
{breakdown}") + max_latency_hover_text.append(f"{coro_name}
Speedup: {speedup}
Max Wait: {round(max_cb[1], 4)}
Handle: {max_cb[0]}") # handle throughput graph throughput = num_callbacks / virtual_run_time @@ -50,6 +60,7 @@ def plot_results(results, filename): mode='markers', name=coro_name, marker=dict(color=get_color(coro_name)), + hovertext=latency_hover_text, showlegend=True, ), row=1, col=1) @@ -68,6 +79,7 @@ def plot_results(results, filename): mode='markers', name=coro_name, marker=dict(color=get_color(coro_name)), + hovertext=max_latency_hover_text, showlegend=False, ), row=3, col=1) @@ -80,10 +92,11 @@ def plot_results(results, filename): showlegend=False, ), row=4, col=1) + fig.update_layout(title=input_file) fig.update_xaxes(title_text="speedup (% optimized away)", row=4, col=1) fig.update_yaxes(title_text="average latency (seconds)", row=1, col=1) fig.update_yaxes(title_text="throughput (handles per second)", row=2, col=1) fig.update_yaxes(title_text="maximum latency (seconds)", row=3, col=1) fig.update_yaxes(title_text="# of callbacks", row=4, col=1) - fig.write_html(filename) + fig.write_html(output_file) diff --git a/nemesis/nemesis.py b/nemesis/nemesis.py index b7a9877..2e3837a 100755 --- a/nemesis/nemesis.py +++ b/nemesis/nemesis.py @@ -52,6 +52,9 @@ class Experiment: class Nemesis(object): + # the name of the target program + prog = None + # the (ideal) interval between samples signal_interval = 0.0 # the timestamp which the last sample was taken @@ -79,10 +82,11 @@ class Nemesis(object): prev_coro = defaultdict(lambda: None) @staticmethod - def __init__(e_duration, filename, signal_interval=0.01): + def __init__(e_duration, filename, prog, signal_interval=0.01): Nemesis.signal_interval = signal_interval Nemesis.e_duration = e_duration Nemesis.filename = filename + Nemesis.prog = prog @staticmethod def start(): @@ -111,7 +115,7 @@ class Nemesis(object): @staticmethod def stop(): signal.setitimer(signal.ITIMER_REAL, 0) - plot_results(Nemesis.results, Nemesis.filename) + plot_results(Nemesis.results, Nemesis.filename, Nemesis.prog) print(f"Wrote {Nemesis.filename}") @staticmethod @@ -338,6 +342,7 @@ if __name__ == "__main__": code = compile(fp.read(), args.prog[0], "exec") Nemesis(args.experiment_duration, args.filename, + args.prog[0], args.interval).start() exec(code, the_globals) Nemesis.stop() -- cgit v1.2.3