summaryrefslogtreecommitdiff
path: root/nemesis
diff options
context:
space:
mode:
Diffstat (limited to 'nemesis')
-rw-r--r--nemesis/causal_event_loop.py28
-rw-r--r--nemesis/html_gen.py23
-rwxr-xr-xnemesis/nemesis.py9
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 = "<br>".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}<br>Speedup: {speedup}<br>Average Wait: {round(latency, 4)}<br>Breakdown:<br>{breakdown}")
+ max_latency_hover_text.append(f"{coro_name}<br>Speedup: {speedup}<br>Max Wait: {round(max_cb[1], 4)}<br>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()