From 6413cfba10e07bfec4d893eecbf486859279ec0e Mon Sep 17 00:00:00 2001 From: Elias Werner Date: Fri, 23 Aug 2024 14:14:38 +0200 Subject: [PATCH 01/13] small fixes (doe history, visualization) --- src/jumper/kernel.py | 13 +++++-- src/jumper/perfdatahandler.py | 5 +-- src/jumper/visualization.py | 70 ++++++++++++++++++----------------- 3 files changed, 46 insertions(+), 42 deletions(-) diff --git a/src/jumper/kernel.py b/src/jumper/kernel.py index 27243fe..81f3eb9 100644 --- a/src/jumper/kernel.py +++ b/src/jumper/kernel.py @@ -626,7 +626,8 @@ async def scorep_execute( # Transmit user persistence and updated sys.path from Jupyter # notebook to subprocess After running the code, transmit subprocess # persistence back to Jupyter notebook - with os.fdopen(os.open(scorep_script_name, os.O_WRONLY | os.O_CREAT), 'w') as file: + with os.fdopen(os.open(scorep_script_name, os.O_WRONLY | os.O_CREAT), + 'w') as file: file.write(self.pershelper.subprocess_wrapper(code)) # For disk mode use implicit synchronization between kernel and @@ -720,7 +721,7 @@ async def scorep_execute( self.cell_output(line) performance_data_nodes, duration = ( - self.perfdata_handler.end_perfmonitor(code) + self.perfdata_handler.end_perfmonitor() ) # In disk mode, subprocess already terminated @@ -810,6 +811,7 @@ async def scorep_execute( self.pershelper.postprocess() if performance_data_nodes: self.report_perfdata(performance_data_nodes, duration) + self.perfdata_handler.append_code(datetime.datetime.now(), code) return self.standard_reply() async def do_execute( @@ -921,7 +923,8 @@ async def do_execute( pd.DataFrame( self.perfdata_handler.get_code_history(), columns=["timestamp", "code"], - ).reset_index() + ).reset_index(), layout={"topStart": "search", "topEnd": None}, + columnDefs=[{"className": 'dt-left', "targets": 2}], ) return self.standard_reply() elif code.startswith("%%perfdata_to_variable"): @@ -1060,10 +1063,12 @@ async def do_execute( cell_id=cell_id, ) performance_data_nodes, duration = ( - self.perfdata_handler.end_perfmonitor(code) + self.perfdata_handler.end_perfmonitor() ) if performance_data_nodes: self.report_perfdata(performance_data_nodes, duration) + self.perfdata_handler.append_code(datetime.datetime.now(), + code) return parent_ret elif self.mode == KernelMode.MULTICELL: return self.append_multicellmode(magics_cleanup(code)) diff --git a/src/jumper/perfdatahandler.py b/src/jumper/perfdatahandler.py index 3e8079c..da43da8 100644 --- a/src/jumper/perfdatahandler.py +++ b/src/jumper/perfdatahandler.py @@ -3,7 +3,6 @@ import pickle import codecs import time -from datetime import datetime import os import subprocess import sys @@ -326,7 +325,7 @@ def start_perfmonitor(self, pid): self.starttime = time.perf_counter() - def end_perfmonitor(self, code): + def end_perfmonitor(self): duration = time.perf_counter() - self.starttime if self.monitor_module: @@ -352,6 +351,4 @@ def end_perfmonitor(self, code): performance_data_nodes = self.parse_perfdata_from_stdout( stdout_data_node ) - if performance_data_nodes: - self.append_code(datetime.now(), code) return performance_data_nodes, duration diff --git a/src/jumper/visualization.py b/src/jumper/visualization.py index 7071621..efe4669 100644 --- a/src/jumper/visualization.py +++ b/src/jumper/visualization.py @@ -32,7 +32,7 @@ def plot_graph(ax, metric, perfdata, time_indices=None, color=None): int(os.environ.get("JUMPER_REPORT_FREQUENCY", 2)), ) ] - print(time_indices) + if metric == perfmetrics["cpu_agg"]: ax.plot( x_scale, perfdata[0][0][-3], label="Mean", color=(0.20, 0.47, 1.00) @@ -148,50 +148,52 @@ def plot_graph(ax, metric, perfdata, time_indices=None, color=None): ax.legend() ax.grid(True) - # in multi node case, we have to iterate over the indices (time_indices) - # and not only 0 here - current_index = 0 - target_index = -1 - transition_offset = (x_scale[1] - x_scale[0]) / 2 - start_offset = 0 - last_idx = time_indices[0][-1][0] - - for cell_idx, n_ms in time_indices[0]: + # colorization of the plot in case of multiple cells + if time_indices: + # in multi node case, we have to iterate over the indices (time_indices) + # and not only 0 here + current_index = 0 + target_index = -1 + transition_offset = (x_scale[1] - x_scale[0]) / 2 + start_offset = 0 + last_idx = time_indices[0][-1][0] - target_index = target_index + n_ms - # don't use offset for last cell - if cell_idx == last_idx: - transition_offset = 0 - ax.axvspan(x_scale[current_index] + start_offset, - x_scale[target_index] + - transition_offset, - facecolor=color[cell_idx], alpha=0.3) + for cell_idx, n_ms in time_indices[0]: - text_x_pos = x_scale[current_index] + start_offset + ( - (x_scale[target_index] + transition_offset - - x_scale[current_index] + start_offset) / 2) - text_y_pos = ax.get_ylim()[0] + (ax.get_ylim()[1]*0.05) + target_index = target_index + n_ms + # don't use offset for last cell + if cell_idx == last_idx: + transition_offset = 0 + ax.axvspan(x_scale[current_index] + start_offset, + x_scale[target_index] + + transition_offset, + facecolor=color[cell_idx], alpha=0.3) - # add cell index to plot - ax.text(text_x_pos, text_y_pos, "#" + str(cell_idx), style='italic', - bbox={ - 'facecolor': 'lightgrey', 'alpha': 0.5, 'pad': 2} - ) - - current_index = target_index - start_offset = transition_offset + text_x_pos = x_scale[current_index] + start_offset + ( + (x_scale[target_index] + transition_offset - + x_scale[current_index] + start_offset) / 2) + text_y_pos = ax.get_ylim()[0] + (ax.get_ylim()[1] * 0.05) + # add cell index to plot + ax.text(text_x_pos, text_y_pos, "#" + str(cell_idx), style='italic', + bbox={ + 'facecolor': 'lightgrey', 'alpha': 0.5, 'pad': 2} + ) + current_index = target_index + start_offset = transition_offset def plot_with_dropdowns(metrics, perfdata, metric_start, time_indices=None): # Create subplots in a 1x2 grid fig, axes = plt.subplots(1, 2, figsize=(10, 3)) dropdowns = [] - - color = [ - "#" + ''.join([random.choice('0123456789ABCDEF') for j in range(6)]) - for i in range(len(time_indices[0]))] + color=None + if time_indices: + color = [ + "#" + ''.join( + [random.choice('0123456789ABCDEF') for j in range(6)]) + for i in range(len(time_indices[0]))] # Plot data and create dropdowns for each subplot for i, ax in enumerate(axes): From 119756ac5215019ffbdaf7e36a4417b7f5bd53b7 Mon Sep 17 00:00:00 2001 From: Elias Werner Date: Fri, 23 Aug 2024 19:01:31 +0200 Subject: [PATCH 02/13] add support for multi cell mode colorization in plot --- src/jumper/kernel.py | 85 ++++++++++++++++++++++++++++++----- src/jumper/perfdatahandler.py | 18 ++++++-- src/jumper/visualization.py | 9 ++-- 3 files changed, 94 insertions(+), 18 deletions(-) diff --git a/src/jumper/kernel.py b/src/jumper/kernel.py index 81f3eb9..e2a675d 100644 --- a/src/jumper/kernel.py +++ b/src/jumper/kernel.py @@ -80,8 +80,8 @@ def __init__(self, **kwargs): self.mode = KernelMode.DEFAULT - self.multicell_cellcount = 0 - self.multicell_code = "" + self.multicell_cellcount = -1 + self.multicell_code = "import time\n" self.writefile_base_name = "jupyter_to_script" self.writefile_bash_name = "" @@ -198,8 +198,9 @@ def set_scorep_env(self, code): """ if self.mode == KernelMode.DEFAULT: for scorep_param in code.split("\n")[1:]: - key, val = scorep_param.split("=") - self.scorep_env[key] = val + if not scorep_param == "": + key, val = scorep_param.split("=") + self.scorep_env[key] = val self.cell_output( "Score-P environment set successfully: " + str(self.scorep_env) ) @@ -264,6 +265,7 @@ def append_multicellmode(self, code): f"print('Executing cell {self.multicell_cellcount}')\n" + f"print('''{code}''')\n" + f"print('-' * {max_line_len})\n" + + f"print('MCM_TS'+str(time.time()))\n" + f"{code}\n" + "print('''\n''')\n" ) @@ -279,8 +281,8 @@ def abort_multicellmode(self): """ if self.mode == KernelMode.MULTICELL: self.mode = KernelMode.DEFAULT - self.multicell_code = "" - self.multicell_cellcount = 0 + self.multicell_code = "import time\n" + self.multicell_cellcount = -1 self.cell_output("Multicell mode aborted.") else: self.cell_output( @@ -705,6 +707,8 @@ async def scorep_execute( # Empty cell output, required for interactive output # e.g. tqdm for-loop progress bar self.cell_output("\0") + + multicellmode_timestamps = [] while True: chunk = b"" + proc.stdout.read(READ_CHUNK_SIZE) if chunk == b"": @@ -718,8 +722,49 @@ async def scorep_execute( else: incomplete_line = "" for line in lines: + if "MCM_TS" in line: + multicellmode_timestamps.append(line) + continue self.cell_output(line) + # for multiple nodes, we have to add more lists here, one list per node + # this is required to be in line with the performance data aggregation + # for the %%display_graph_for_all magic command, which does not have + # explicit timestamps, but aligns the colorization of the plot based + # on the number of perf measurements we have, which is individual per + # node + time_indices = None + if len(multicellmode_timestamps): + # retrieve the index this cell will have in the global history + sub_idx = len(self.perfdata_handler.get_code_history()) + # append to have end of last code fragment + multicellmode_timestamps.append("MCM_TS"+str(time.time())) + time_indices = [[]] + nb_ms = 0.0 + for idx, ts_string in enumerate(multicellmode_timestamps[:-1]): + secs = (float(multicellmode_timestamps[idx+1][6:]) - + float(ts_string[6:])) + nb_ms += (secs / + int(os.environ.get("JUMPER_REPORT_FREQUENCY", 2))) + if nb_ms >= 1.0: + # only consider if we have measurements + time_indices[0].append((str(sub_idx)+"_"+str(idx), nb_ms)) + nb_ms %= 1.0 + # add time for last to last measurement + if nb_ms >= 0.0: + sub_idx, val = time_indices[0][-1] + time_indices[0][-1] = (sub_idx, val + nb_ms) + + nb_ms = 0.0 + for idx, val in enumerate(time_indices[0]): + sub_idx, ms = val + nb_ms += ms % 1.0 + ms = int(ms) + if nb_ms >= 1.0: + ms += 1 + nb_ms %= 1.0 + time_indices[0][idx] = (sub_idx, ms) + performance_data_nodes, duration = ( self.perfdata_handler.end_perfmonitor() ) @@ -811,7 +856,8 @@ async def scorep_execute( self.pershelper.postprocess() if performance_data_nodes: self.report_perfdata(performance_data_nodes, duration) - self.perfdata_handler.append_code(datetime.datetime.now(), code) + self.perfdata_handler.append_code(datetime.datetime.now(), code, + time_indices) return self.standard_reply() async def do_execute( @@ -856,10 +902,21 @@ async def do_execute( return self.standard_reply() """ if code.startswith("%%display_graph_for_last"): + if not len(self.perfdata_handler.get_perfdata_history()): + self.cell_output( + "No performance data available." + ) + time_indices = self.perfdata_handler.get_time_indices()[-1] + if time_indices: + sub_idxs = [x[0] for x in time_indices[0]] + self.cell_output(f"Cell seemed to be tracked in multi cell" + " mode. Got performance data for the" + f"following sub cells: {sub_idxs}") perfvis.draw_performance_graph( self.nodelist, self.perfdata_handler.get_perfdata_history()[-1], self.gpu_avail, + time_indices ) return self.standard_reply() elif code.startswith("%%display_graph_for_index"): @@ -876,10 +933,17 @@ async def do_execute( + " cells. This index is not available." ) else: + time_indices = self.perfdata_handler.get_time_indices()[index] + if time_indices: + sub_idxs = [x[0] for x in time_indices[0]] + self.cell_output(f"Cell seemed to be tracked in multi cell" + " mode. Got performance data for the" + f"following sub cells: {sub_idxs}") perfvis.draw_performance_graph( self.nodelist, self.perfdata_handler.get_perfdata_history()[index], self.gpu_avail, + time_indices ) return self.standard_reply() elif code.startswith("%%display_graph_for_all"): @@ -986,7 +1050,8 @@ async def do_execute( return self.set_scorep_pythonargs(code) elif code.startswith("%%serializer_settings"): self.cell_output( - "Deprecated. Use: %%marshalling_settings\n[MARSHALLER=]\n[MODE=]", + "Deprecated. Use: %%marshalling_settings\n[MARSHALLER=]\n[" + "MODE=]", "stdout", ) return self.standard_reply() @@ -1015,8 +1080,8 @@ async def do_execute( "KernelError: Multicell execution failed.", "stderr" ) return self.standard_reply() - self.multicell_code = "" - self.multicell_cellcount = 0 + self.multicell_code = "import time\n" + self.multicell_cellcount = -1 return reply_status elif self.mode == KernelMode.WRITEFILE: self.writefile_multicell = False diff --git a/src/jumper/perfdatahandler.py b/src/jumper/perfdatahandler.py index da43da8..8f38af9 100644 --- a/src/jumper/perfdatahandler.py +++ b/src/jumper/perfdatahandler.py @@ -27,8 +27,9 @@ class PerformanceDataHandler: def __init__(self): self.code_history = [] self.performance_data_history = [] + self.time_indices = [] self.nodelist = None - # for local it's none, otherwise points to slurm/ssh/... monitor + # for local, it's none, otherwise points to slurm/ssh/... monitor self.monitor_module = None # the object from the monitor module self.monitor = None @@ -77,8 +78,12 @@ def get_perfdata_history(self): def get_code_history(self): return self.code_history - def append_code(self, time, code): - self.code_history.append([time, code]) + def get_time_indices(self): + return self.time_indices + + def append_code(self, time_, code, time_indices=None): + self.code_history.append([time_, code]) + self.time_indices.append(time_indices) def get_perfdata_aggregated(self): perfdata_aggregated = [] @@ -156,7 +161,12 @@ def get_perfdata_aggregated(self): # add cell index and the number of measurements # we will use that in the visualization to generate # a color transition in the graphs and add the cell index - time_indices[node].append((idx, len(perfdata[node][2]))) + if self.time_indices[idx]: + # for cells tracked in multi cell mode, we can use the sub + # indices created + time_indices[node].extend(self.time_indices[idx][node]) + else: + time_indices[node].append((idx, len(perfdata[node][2]))) return perfdata_aggregated, time_indices diff --git a/src/jumper/visualization.py b/src/jumper/visualization.py index efe4669..d3d2806 100644 --- a/src/jumper/visualization.py +++ b/src/jumper/visualization.py @@ -158,11 +158,12 @@ def plot_graph(ax, metric, perfdata, time_indices=None, color=None): start_offset = 0 last_idx = time_indices[0][-1][0] - for cell_idx, n_ms in time_indices[0]: - + # for multi cell mode, we might have sub indices + for cell_idx, values in enumerate(time_indices[0]): + sub_idx, n_ms = values target_index = target_index + n_ms # don't use offset for last cell - if cell_idx == last_idx: + if sub_idx == last_idx: transition_offset = 0 ax.axvspan(x_scale[current_index] + start_offset, x_scale[target_index] + @@ -175,7 +176,7 @@ def plot_graph(ax, metric, perfdata, time_indices=None, color=None): text_y_pos = ax.get_ylim()[0] + (ax.get_ylim()[1] * 0.05) # add cell index to plot - ax.text(text_x_pos, text_y_pos, "#" + str(cell_idx), style='italic', + ax.text(text_x_pos, text_y_pos, "#" + str(sub_idx), style='italic', bbox={ 'facecolor': 'lightgrey', 'alpha': 0.5, 'pad': 2} ) From ad9aaef8add24960688b563794192c7dfaa25894 Mon Sep 17 00:00:00 2001 From: Elias Werner Date: Fri, 23 Aug 2024 19:13:18 +0200 Subject: [PATCH 03/13] add code4history as a basis for multicellmode code history add execute with scorep to code history again fix spelling --- src/jumper/kernel.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/jumper/kernel.py b/src/jumper/kernel.py index e2a675d..ac68252 100644 --- a/src/jumper/kernel.py +++ b/src/jumper/kernel.py @@ -604,6 +604,7 @@ def report_perfdata(self, performance_data_nodes, duration): async def scorep_execute( self, code, + code4history, silent, store_history=True, user_expressions=None, @@ -856,8 +857,8 @@ async def scorep_execute( self.pershelper.postprocess() if performance_data_nodes: self.report_perfdata(performance_data_nodes, duration) - self.perfdata_handler.append_code(datetime.datetime.now(), code, - time_indices) + self.perfdata_handler.append_code(datetime.datetime.now(), + code4history, time_indices) return self.standard_reply() async def do_execute( @@ -911,7 +912,7 @@ async def do_execute( sub_idxs = [x[0] for x in time_indices[0]] self.cell_output(f"Cell seemed to be tracked in multi cell" " mode. Got performance data for the" - f"following sub cells: {sub_idxs}") + f" following sub cells: {sub_idxs}") perfvis.draw_performance_graph( self.nodelist, self.perfdata_handler.get_perfdata_history()[-1], @@ -938,7 +939,7 @@ async def do_execute( sub_idxs = [x[0] for x in time_indices[0]] self.cell_output(f"Cell seemed to be tracked in multi cell" " mode. Got performance data for the" - f"following sub cells: {sub_idxs}") + f" following sub cells: {sub_idxs}") perfvis.draw_performance_graph( self.nodelist, self.perfdata_handler.get_perfdata_history()[index], @@ -1067,7 +1068,9 @@ async def do_execute( if self.mode == KernelMode.MULTICELL: self.mode = KernelMode.DEFAULT try: + # second multicell_code should be cleaned for code history reply_status = await self.scorep_execute( + self.multicell_code, self.multicell_code, silent, store_history, @@ -1098,8 +1101,11 @@ async def do_execute( return self.end_writefile() elif code.startswith("%%execute_with_scorep"): if self.mode == KernelMode.DEFAULT: + # second code argument is for history purposes, we want to keep + # everything return await self.scorep_execute( code.split("\n", 1)[1], + code, silent, store_history, user_expressions, From 3c666b781c866ad20e24334e290a898890abf0cc Mon Sep 17 00:00:00 2001 From: Elias Werner Date: Mon, 26 Aug 2024 10:48:05 +0200 Subject: [PATCH 04/13] add code history parameter for multi cell mode for a more tidy code history add time indices to export (for multi cell mode) --- src/jumper/kernel.py | 53 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/src/jumper/kernel.py b/src/jumper/kernel.py index ac68252..3f0b2bd 100644 --- a/src/jumper/kernel.py +++ b/src/jumper/kernel.py @@ -82,6 +82,7 @@ def __init__(self, **kwargs): self.multicell_cellcount = -1 self.multicell_code = "import time\n" + self.multicell_code_history = "" self.writefile_base_name = "jupyter_to_script" self.writefile_bash_name = "" @@ -269,6 +270,10 @@ def append_multicellmode(self, code): + f"{code}\n" + "print('''\n''')\n" ) + self.multicell_code_history += ( + f"###User code for sub cell {self.multicell_cellcount}\n" + + f"print('''{code}''')\n" + ) self.cell_output( f"Cell marked for multicell mode. It will be executed at " f"position {self.multicell_cellcount}" @@ -282,6 +287,7 @@ def abort_multicellmode(self): if self.mode == KernelMode.MULTICELL: self.mode = KernelMode.DEFAULT self.multicell_code = "import time\n" + self.multicell_code_history = "" self.multicell_cellcount = -1 self.cell_output("Multicell mode aborted.") else: @@ -604,7 +610,7 @@ def report_perfdata(self, performance_data_nodes, duration): async def scorep_execute( self, code, - code4history, + code_for_history, silent, store_history=True, user_expressions=None, @@ -858,7 +864,7 @@ async def scorep_execute( if performance_data_nodes: self.report_perfdata(performance_data_nodes, duration) self.perfdata_handler.append_code(datetime.datetime.now(), - code4history, time_indices) + code_for_history, time_indices) return self.standard_reply() async def do_execute( @@ -889,8 +895,8 @@ async def do_execute( %%display_graphs_for_last cpu_util, ... # displays one graph for all cell, arguments: cpu_util etc. %%display_graphs_for_all cpu_util, ... - # -> would be cool if we can hover the graph and per timepoint, - we see the index of the cell + # -> displays performance data aggregated, indicates different cells + # by color # displays graph for index cell, arguments: cpu_util etc. %%display_graphs_for_index i cpu_util, ... """ @@ -995,23 +1001,45 @@ async def do_execute( elif code.startswith("%%perfdata_to_variable"): if len(code.split(" ")) == 1: self.cell_output( - "No variable to export specified. Use: " + "No variable for export specified. Use: " "%%perfdata_to_variable myvar", "stdout", ) else: varname = code.split(" ")[1] - await super().do_execute( - f"{varname}=" - f"{self.perfdata_handler.get_perfdata_history()}", - silent=True, - ) + # for multi cell mode, we might have time indices which we want + # to communicate to the user, each time_index has the index of + # the overall mult cell and the sub index within this cell. + # In addition, it has a counter for the number of measurements + # each sub cell corresponds to in the list of performance data + # measurements, e.g. (2_0, 5), (2_1, 3), (2_2, 7) + mcm_time_indices = self.perfdata_handler.get_time_indices() + mcm_time_indices = list( + filter(lambda item: item is not None, mcm_time_indices)) + + code = (f"{varname}=" + f"{self.perfdata_handler.get_perfdata_history()}") + + if mcm_time_indices: + code += f"\n{varname}.append({mcm_time_indices})" + + await super().do_execute(code,silent=True) self.cell_output( "Exported performance data to " + str(varname) - + " variable", + + " variable. ", "stdout", ) + if mcm_time_indices: + self.cell_output( + "Detected that cells were executed in multi cell mode." + + f"Last entry in {varname} is a list that contains " + f"the sub indices per cell that were executed in " + f"in multi cell mode and a counter for the number of" + f" performance measurements within this sub cell, " + f"e.g. f{mcm_time_indices[-1]}", + "stdout", + ) return self.standard_reply() elif code.startswith("%%perfdata_to_json"): if len(code.split(" ")) == 1: @@ -1071,7 +1099,7 @@ async def do_execute( # second multicell_code should be cleaned for code history reply_status = await self.scorep_execute( self.multicell_code, - self.multicell_code, + self.multicell_code_history, silent, store_history, user_expressions, @@ -1084,6 +1112,7 @@ async def do_execute( ) return self.standard_reply() self.multicell_code = "import time\n" + self.multicell_code_history = "" self.multicell_cellcount = -1 return reply_status elif self.mode == KernelMode.WRITEFILE: From 3981edaaf71d753a0f2a0a510aaef0fda64e50b3 Mon Sep 17 00:00:00 2001 From: Elias Werner Date: Wed, 20 Nov 2024 15:34:40 +0100 Subject: [PATCH 05/13] merge master and perf monitoring --- ExampleJUmPER.ipynb | 633 ------------------------ README.md | 49 +- examples/ExampleMonitoring.ipynb | 418 ++++++++++++++++ examples/ExampleNotebook.ipynb | 506 ------------------- examples/demonstrator.ipynb | 340 ------------- examples/fairytales_demo.txt | 136 ----- examples/gpt-demo/01-GPT-Training.ipynb | 39 +- src/jumper/kernel.py | 253 +++++----- src/jumper/userpersistence.py | 51 +- tests/kernel/notebook.ipynb | 306 +++++++++--- tests/kernel/persistence.yaml | 9 +- tests/kernel/scorep_env.yaml | 16 +- tests/kernel/writemode.yaml | 29 +- tests/test_kernel.py | 11 +- 14 files changed, 928 insertions(+), 1868 deletions(-) delete mode 100644 ExampleJUmPER.ipynb create mode 100644 examples/ExampleMonitoring.ipynb delete mode 100644 examples/ExampleNotebook.ipynb delete mode 100644 examples/demonstrator.ipynb delete mode 100644 examples/fairytales_demo.txt diff --git a/ExampleJUmPER.ipynb b/ExampleJUmPER.ipynb deleted file mode 100644 index f0fcb97..0000000 --- a/ExampleJUmPER.ipynb +++ /dev/null @@ -1,633 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "34fc5489-608e-434d-93c2-cefe6b33890f", - "metadata": {}, - "source": [ - "### News about PyPerf Jupyter Kernel (Performance Monitoring and Analysis in Jupyter)\n", - "- added metrics to display in Jupyter (mean/max/min, raw values) for: CPU,GPU,Mem\n", - "- use dropdown menus to select metrics (interactively)\n", - "- use in-memory serialization instead of disk-based for Score-P execution\n", - "- modular system for backbone serializer (dill/cloudpickle)\n", - "\n", - "---" - ] - }, - { - "cell_type": "markdown", - "id": "539781f0-7e0c-4988-b5c1-8a369a6de653", - "metadata": {}, - "source": [ - "**Toy Example:** Estimating Pi by Throwing Darts" - ] - }, - { - "cell_type": "code", - "id": "a9e8242b-dd85-48dc-b67f-81b0926bf67e", - "metadata": { - "ExecuteTime": { - "end_time": "2024-07-23T11:44:11.335951Z", - "start_time": "2024-07-23T11:44:10.322227Z" - } - }, - "source": "%env JUMPER_REPORT_FREQUENCY=2", - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "env: JUMPER_REPORT_FREQUENCY=2\n" - ] - } - ], - "execution_count": 5 - }, - { - "cell_type": "code", - "id": "2a10feac-0a7b-478a-ab36-71b3a1844f92", - "metadata": { - "ExecuteTime": { - "end_time": "2024-07-23T11:44:13.374568Z", - "start_time": "2024-07-23T11:44:12.366967Z" - } - }, - "source": "%env JUMPER_REPORTS_MIN=2", - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "env: JUMPER_REPORTS_MIN=2\n" - ] - } - ], - "execution_count": 6 - }, - { - "cell_type": "code", - "id": "519f9bc9-d715-480d-ac6f-cd2771ded5f3", - "metadata": { - "ExecuteTime": { - "end_time": "2024-07-23T11:44:16.675947Z", - "start_time": "2024-07-23T11:44:15.670611Z" - } - }, - "source": [ - "from random import random\n", - "from time import time\n", - "from multiprocessing import Pool\n", - "import sys" - ], - "outputs": [], - "execution_count": 7 - }, - { - "cell_type": "code", - "id": "bbc9a5d8-bfcf-4810-b232-e49b4be5f1e6", - "metadata": { - "ExecuteTime": { - "end_time": "2024-07-23T11:44:17.688886Z", - "start_time": "2024-07-23T11:44:16.677748Z" - } - }, - "source": [ - "def throw_dart(iterations: int) -> int:\n", - " hits = 0\n", - " for i in range(iterations):\n", - " x = random()\n", - " y = random()\n", - " if (x * x) + (y * y) <= 1:\n", - " hits = hits + 1\n", - " return hits\n", - "\n", - "\n", - "def compute_pi(iterations, process_count):\n", - " pool = Pool(processes=process_count)\n", - " trials_per_process = [int(iterations / process_count)] * process_count\n", - "\n", - " start = time()\n", - "\n", - " hits = pool.map(throw_dart, trials_per_process)\n", - " pi = (sum(hits) * 4) / iterations\n", - "\n", - " end = time()\n", - "\n", - " print(pi)\n", - " print(f\"Execution time: {end - start} seconds.\")" - ], - "outputs": [], - "execution_count": 8 - }, - { - "cell_type": "code", - "id": "86b1962f-2053-4df6-b3fd-6f7e4fed48b3", - "metadata": { - "ExecuteTime": { - "end_time": "2024-07-23T11:45:27.484787Z", - "start_time": "2024-07-23T11:44:19.133262Z" - } - }, - "source": [ - "# 10⁹ iterations on 4 CPUs.on_click(partial(generate_plot, x=x, y=y))\n", - "compute_pi(10**9, 8)" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "3.141498828\n", - "Execution time: 67.2501654624939 seconds.\n", - "\n", - "----Performance Data----\n", - "Duration: 67.33\n", - "\n", - "CPU Util (Across CPUs) \tAVG: 99.80\t MIN: 76.50\t MAX: 100.00\n", - "Mem Util in GB (Across nodes)\tAVG: 11.18\t MIN: 10.85\t MAX: 11.36\n", - "IO Ops (excl.) Read \tTotal: 6269\n", - " Write \tTotal: 3565\n", - "IO Bytes (excl.) Read \tTotal: 0.16\n", - " Write \tTotal: 1.06\n" - ] - } - ], - "execution_count": 9 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2024-07-23T11:42:09.272677Z", - "start_time": "2024-07-23T11:42:08.265658Z" - } - }, - "cell_type": "code", - "source": "%env PARALLEL_MARSHALL_DEBUG=10", - "id": "147fdd845d36eeaf", - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "env: PARALLEL_MARSHALL_DEBUG=10\n" - ] - } - ], - "execution_count": 1 - }, - { - "cell_type": "code", - "id": "5c8669db-5262-40dc-bb43-54a71f562966", - "metadata": { - "ExecuteTime": { - "end_time": "2024-07-23T12:18:44.235036Z", - "start_time": "2024-07-23T12:18:44.231070Z" - } - }, - "source": [ - "%%serializer_settings\n", - "MODE=memory\n", - "SERIALIZER=parallel_marshall" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Kernel uses 'parallel_marshall' serializer in 'memory' mode." - ] - } - ], - "execution_count": 1 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2024-07-23T12:18:45.241643Z", - "start_time": "2024-07-23T12:18:44.236728Z" - } - }, - "cell_type": "code", - "source": "a=2", - "id": "971d897afb7d0131", - "outputs": [], - "execution_count": 2 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2024-07-23T12:18:47.701712Z", - "start_time": "2024-07-23T12:18:45.245174Z" - } - }, - "cell_type": "code", - "source": [ - "%%execute_with_scorep\n", - "print(a)" - ], - "id": "9791749f398efefe", - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u00002\n", - "Instrumentation results can be found in /home/eliasw/scorep_jupyter_kernel_python/scorep-20240723_1418_17044002454007\n", - "1418\n", - "\n", - "1418\n" - ] - } - ], - "execution_count": 3 - }, - { - "cell_type": "code", - "id": "b3b5516b-be9c-49e0-8394-3c9fff3943de", - "metadata": { - "ExecuteTime": { - "end_time": "2024-07-23T11:46:43.484312Z", - "start_time": "2024-07-23T11:46:42.943618Z" - } - }, - "source": [ - "%%display_graph_for_last" - ], - "outputs": [ - { - "data": { - "text/plain": [ - "Button(description='Add Display', style=ButtonStyle())" - ], - "application/vnd.jupyter.widget-view+json": { - "version_major": 2, - "version_minor": 0, - "model_id": "0f5ec8888ff44d06a10ef232a2a7982a" - } - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "Output()" - ], - "application/vnd.jupyter.widget-view+json": { - "version_major": 2, - "version_minor": 0, - "model_id": "8098eb63b6524a26931fa7a21a29acf8" - } - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "HBox(children=(Dropdown(description='Metric:', options=('CPU Usage (Min/Max/Mean)', 'CPU Usage (Raw)', 'Mem in…" - ], - "application/vnd.jupyter.widget-view+json": { - "version_major": 2, - "version_minor": 0, - "model_id": "5a544414f17c4bfbb5994e4b11816d4c" - } - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "
" - ], - "image/png": "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Yw/k7b2NK1aswN9//222rHfv3vj777/NbvtQVFSEzz77rEyMjRo1wqxZs5CXl1emDVeuXLljGxs0aICwsDDs2bPnjtsBwMMPP4w333wTH330Edzc3Crdvjr+97//oVevXlAo7vx9WNeuXfH2229jwYIFdxz+aunrDRiHF587dw5LlizBnDlzEBkZiREjRpj97q0d3+DBg7Fjxw6sXbu2zLqsrCxTwSaXyyFJkllhl5GRUebvuzr27dtXo1ltb/fDDz8gPz8fycnJePjhh8s87r//fqxcuRLFxcVITExEVFQU5s6da3abNaDySx4A4/9zvV6PBQsWmC3/4IMPIEmS6f/n9evXy+xb2mNR+vu9/f+vm5sbmjRpAiGEqYAuT1xcHIQQ+O6778yWf/PNNwBQaS+xI7wHFhQUoKioyGy7Ro0awdvbu8x7HxE5F0d4j6kIP2eV79VXX4VOp8OcOXNMbQLK5sW5c+eWu3/nzp2xc+dObNy40VR0+/v7o3Hjxqbr8sv7kmPv3r0AYNXPBESl2NNtZ40aNcKyZcswZMgQNG7cGMOHD0ezZs2g1Wqxfft2rFixAklJSVU+7tNPP41FixbhkUcewahRo9CqVStcu3YN3377LQ4dOoSvvvqq0sLRzc0Ns2bNwogRI9C2bVsMGTIE9evXR1paGhYtWoTmzZvj6aefNm3/5JNP4vr16+jWrRsaNmyIs2fP4sMPP0TLli1N3xref//9mDp1KkaOHImOHTvi4MGDWLp0KaKjo83O/cwzz2DBggV47LHH8OKLLyIkJARLly419baVfisrk8nw+eef47777kPTpk0xcuRINGjQAH///Tc2btwIjUaDNWvW3LGdAwcOxA8//FDpdTw+Pj53HCpdU4WFhdi4cSM+/vjjSreVyWSYNGlSpdtZ+npv2LABH330Ed58803Th4TFixfj3nvvxeTJkzFjxgybxDd+/Hj897//xf3334+kpCS0bt0a+fn5OHjwIL7//ntkZGTA398f/fr1w5w5c9CnTx8MHToUly9fRkpKCmJiYnDgwIFKz1ORvXv34vr16xg4cGC1j3G7pUuXon79+hUm7QEDBuCzzz7Dzz//jIceeggLFy5E//790bJlS4wcORIhISE4duwYDh8+XO6XEbfq378/unbtiv/7v/9DRkYGWrRogV9//RU//vgjxo4da+qhmDp1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2WefxenTp/Hqq6/i2LFj+Oijj/Ddd9/hpZdeslXziIhqhRzGovtKQbadIymLRTcREVEFbr+mGwCiAwFPFVCoBdIvlb8fUVVER0dj37596Nq1K8aNG4dmzZqhZ8+eWL9+PRYuXGjRMXx9fbF161bodDoMGDAALVu2xPz58zFr1iyMHDnStF1UVBR+/vlnrFu3Di1atMDs2bPx+eefo3fv8ofhExE5C6VkvIzmekGWfQMpB6/pJiIiqkB5w8tlMmNv946TwMGzQEKofWIj1xISEoIFCxZgwYIF5a7PyMio9BhxcXFYtWqV2TKDwYCcnByzZffeey/S0mr3HrVERLbmLvNEIYDrRezpJiIichrlDS8HgLtuDjHndd1ERESOQa3wAgBkF+dUsmXtc/iiOzc3F2PHjkVERATUajU6duyI3bt3m9YnJSVBkiSzR58+fewYMRERuYryZi8HbplMrexk0XUaczYREdmLp8J4C7Bcba6dIynL4YeXP/nkkzh06BD+85//IDQ0FF9//TV69OiBI0eOoEGDBgCAPn36YPHixaZ9VCqVvcIlIiIXUjq8XMDY2136vFmY8a4jf98AruYC/rV3q0+HxpxNRET24uWmAbRAno493VVSWFiIlStXYsaMGbjnnnsQExODt956CzExMWYTi6hUKgQHB5sefn5+doyaiIhchVwGyG7emenWydS83IGYIOPPBznEHABzNhER2ZePm/Eb8EJ9np0jKcuhe7pLSkqg1+vh7u5utlytVmPbtm2m55s2bUJgYCD8/PzQrVs3vPPOO6hfv36Fxy0uLja7j2XpBCM6nQ46na5GMZfuX9PjODq207Wwna6F7bQyIaGkBCgqEpDfsrhpQxlOZsqRdkaPu+MMFe5eHc74u7NFzq5KvtbpdBBCwGAwwGCo/PchhDD9a8n2zsoa7TQYDBBCQKfTQS6XV76DnfC9z7Wwna6lNtqpuaXorq3X09LzSKL03dhBdezYEW5ubli2bBmCgoLwzTffYMSIEYiJicHx48exfPlyeHh4ICoqCqdOncLrr78OLy8v7Nixo8LE8NZbb2HKlCllli9btgweHh62bhIRETmRM9n+0BnkCPO+DrXin+T655Uw/PdUIhp6XcPIZtvucISqKygowNChQ5GdnQ2NRmPVY9uStXN2VfK1QqFAcHAwwsLC4ObmZrM21kVarRbn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vr7l7qdQKKBUKqHVaqv78gAAvvjiC/To0QMRERE1Og4RkaPzvmV4+dXCrFo9t6JWz+YC9Abg9zvUnsIAFBQq4KEGJCt/pdEp/p8eFUv9+9//xmuvvYazZ88CAH7//XcsX74cmzZtuuN+ffv2xejRowEAEyZMwAcffICNGzciPj6+OqETEbksXtPtuHQ6Hd59991aP+/rr78ONzc3i7ZNT0+HEAIJCQkWH1+r1WL27NnIzs5Gt27dqhsmLly4gF9++QXLli2r9jGIiJyFu1IOufAAAFwvyK7Vc7PodnEBAQHo168fvvzySwgh0K9fP/j7+1e6X/PmzU0/S5KE4OBgXL582ZahEhE5Jd4yjGqiKr3iEyZMwKRJk1BUVAQvLy+8//776NevX7XPvWTJEvj6+pY7ARsRkStSyT1RAuB6EYtuhyaXGXucK2IwADk5JdBoAJmVe7qr2stdatSoUXj++ecBACkpKRbto1QqzZ5LkgSDgWMniYhux+HljkupVOL1118vd53BYEBubi68vb0hs3LCvj2H3klsbCwkScKxY8cq3Xb8+PFISkqCl5cXgoKCIEmSaZ1GozGNartVVlYW5HJ5maHrQggsWrQIjz/+uMW98kREzk4t90IugOxiFt0O707Fr3RzvVxm/aK7uvr06QOtVgtJktC7d297h0NE5FI4vNxxSZJUYUFpMBigVCrh5uZm9aK7KurVq4fevXsjJSUFY8aMKVMcZ2Vlma7r9vf3R0xMTLnHiY+Px/Lly1FcXAyVSmVanpaWhoiIiDJfBGzevBnp6el44oknrNsgIiIH5qHwRi6A3GLep5usTC6X4+jRozhy5Ajkcrm9wyEicimlb6vVnfCSKCUlBXq9Hu3atcPKlStx8uRJHD16FPPnz0eHDh0sOsawYcMgSRKGDx+OvXv3Ij09HYsWLcK8efPMbjtW6osvvkD79u3RrFkzazeHiMhheSuNt0nOK6ndops93XWERqOxdwhERC6JPd1UU9HR0di3bx+mTZuGcePG4eLFiwgICEDr1q2xcOFCi47h6+uLrVu3YuLEiRgwYACys7MRExODWbNmlbkHd3Z2NlauXIl58+bZojlERA5Lo9IAJUABi26yhi+//PKO61evXm36+faZzDMyMspsv3///hrHRETkim69plsI4JbLbIksFhISggULFmDBggXlri8vN98uLi4Oq1atMltmMBiQk2N+P1ofHx8UFBRUO1YiImcVqG6AwuMPo2ez2Fo9L4tuIiKiGlDccqGW3vBPEU5ERESOxd/DDyXZbeCLqFo9L6/pJiIiqgGZDJDd7N3mDOZERESOS+NunFQyp0hXq+dl0U1ERFRDvK6biIjI8WnUxoHeOYUsuomIiJyK6bpuFt1EREQOiz3dRERETurWydSIiIjIMWnUN4vuwpJaPS+LbgsJIewdgsvha0pEroLDyx0Hc4v18TUlIlehcb85vLyWe7o5e3kl5HJj94VWq4VarbZzNK5Fq9UC+Oc1JiJyVoE+gI8H4OVu70jqLqXS2HtRUFDAfG1lpbcXK32NiYicVUM/D7zUIw4B3qpaPS+L7kooFAp4eHjgypUrUCqVkMnuPDjAYDBAq9WiqKio0m2dWU3baTAYcOXKFXh4eECh4J8hETm3QB97R0ByuRy+vr64fPkyAMDDwwPSHW6aznxdOSEECgoKcPnyZfj6+vJLciJyegHeKrzYo3bv0Q2w6K6UJEkICQnBmTNncPbs2Uq3F0KgsLAQarX6jsne2VmjnTKZDOHh4S79OhERUe0JDg4GAFPhfSfM15bz9fU1vbZERFR1LLot4ObmhtjYWNNw6DvR6XTYsmUL7rnnHpcehmWNdrq5ubl07wIREdWu0i/KAwMDodPd+Xo95mvLKJVK9nATEdUQi24LyWQyuLtXfrGeXC5HSUkJ3N3dXTqJ15V2EhGR85HL5ZUWinUlj9WVdhIROTJ2MxIRERERERHZCItuIiIiIiIiIhth0U1ERERERERkI7ymG8aZPQEgJyenxsfS6XQoKChATk6OS187xXa6FrbTtbCdzq80H5XmJzJivq66utJOoO60le10LWync7M0X7PoBpCbmwsACAsLs3MkRERE/8jNzYWPD28CXor5moiIHFFl+VoS/BodBoMBFy5cgLe3d43v1ZmTk4OwsDCcP38eGo3GShE6HrbTtbCdroXtdH5CCOTm5iI0NJS3VrwF83XV1ZV2AnWnrWyna2E7nZul+Zo93TDeDqxhw4ZWPaZGo3GpP6iKsJ2uhe10LWync2MPd1nM19VXV9oJ1J22sp2uhe10Xpbka359TkRERERERGQjLLqJiIiIiIiIbIRFt5WpVCq8+eabUKlU9g7FpthO18J2uha2k6hydeXvp660E6g7bWU7XQvbWTdwIjUiIiIiIiIiG2FPNxEREREREZGNsOgmIiIiIiIishEW3UREREREREQ2wqLbilJSUhAZGQl3d3e0b98eu3btsndINbZlyxb0798foaGhkCQJq1evNlsvhMAbb7yBkJAQqNVq9OjRAydPnrRPsNX03nvvoW3btvD29kZgYCAeeOABHD9+3GyboqIiJCcno379+vDy8sKgQYNw6dIlO0VcPQsXLkTz5s1N90fs0KEDfvnlF9N6V2hjed5//31IkoSxY8ealrlKW9966y1IkmT2SEhIMK13lXYCwN9//41///vfqF+/PtRqNe666y7s2bPHtN4V3ouodrlazq4L+Rpgzi7lCm0sj6vmbOZr5msW3Vby7bff4uWXX8abb76Jffv2oUWLFujduzcuX75s79BqJD8/Hy1atEBKSkq562fMmIH58+fj448/xs6dO+Hp6YnevXujqKioliOtvs2bNyM5ORl//PEH1q1bB51Oh169eiE/P9+0zUsvvYQ1a9ZgxYoV2Lx5My5cuICHHnrIjlFXXcOGDfH+++9j79692LNnD7p164aBAwfi8OHDAFyjjbfbvXs3PvnkEzRv3txsuSu1tWnTprh48aLpsW3bNtM6V2nnjRs30KlTJyiVSvzyyy84cuQIZs+eDT8/P9M2rvBeRLXHFXN2XcjXAHM2c7bztpX52shV3ouqTJBVtGvXTiQnJ5ue6/V6ERoaKt577z07RmVdAMQPP/xgem4wGERwcLCYOXOmaVlWVpZQqVTim2++sUOE1nH58mUBQGzevFkIYWyTUqkUK1asMG1z9OhRAUDs2LHDXmFahZ+fn/j8889dso25ubkiNjZWrFu3TnTp0kW8+OKLQgjX+n2++eabokWLFuWuc6V2TpgwQdx9990VrnfV9yKyHVfP2XUlXwvBnF3K2dvo6jmb+drIld+LKsOebivQarXYu3cvevToYVomk8nQo0cP7Nixw46R2daZM2eQmZlp1m4fHx+0b9/eqdudnZ0NAKhXrx4AYO/evdDpdGbtTEhIQHh4uNO2U6/XY/ny5cjPz0eHDh1cso3Jycno16+fWZsA1/t9njx5EqGhoYiOjsawYcNw7tw5AK7Vzv/+979o06YNHnnkEQQGBqJVq1b47LPPTOtd9b2IbKMu5mxX/j/CnG3k7G2sCzmb+dq134sqw6LbCq5evQq9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40acOHGi3HOcPXsWcrkcgYGBpmXnzp2Dp6cn7r//fkRERKBVq1YYM2aM2X6hoaHQarVmw/CIiIjqKuZsIsfHopuIymjevLnpZ7lcjvr16+Ouu+4yLQsKCgIAXL58GYBxcpWNGzearjfz8vJCQkICAODUqVPlnqOwsBAqlcps4piePXsiIiIC0dHRePzxx7F06VLTN/Ol1Go1AJRZTkREVBcxZxM5PhbdRFSGUqk0ey5Jktmy0qRrMBgAAHl5eejfvz/2799v9jh58mSZoWal/P39UVBQAK1Wa1rm7e2Nffv24ZtvvkFISAjeeOMNtGjRAllZWaZtrl+/DgAICAiwSluJiIicGXM2keNj0U1ENZaYmIjDhw8jMjISMTExZg9PT89y92nZsiUA4MiRI2bLFQoFevTogRkzZuDAgQPIyMjAhg0bTOsPHTqEhg0bwt/f32btISIiclXM2US1j0U3EdVYcnIyrl+/jsceewy7d+/GqVOnsHbtWowcORJ6vb7cfQICApCYmIht27aZlv3000+YP38+9u/fj7Nnz+Krr76CwWBAfHy8aZutW7eiV69eNm8TERGRK2LOJqp9LLqJqMZCQ0Px+++/Q6/Xo1evXrjrrrswduxY+Pr6Qiar+G3mySefxNKlS03PfX19sWrVKnTr1g2NGzfGxx9/jG+++QZNmzYFABQVFWH16tV46qmnbN4mIiIiV8ScTVT7JCGEsHcQRFQ3FRYWIj4+Ht9++y06dOhQ6fYLFy7EDz/8gF9//bUWoiMiIqJSzNlE1ceebiKyG7Vaja+++gpXr161aHulUokPP/zQxlERERHR7ZiziaqPPd1ERERERERENsKebiIiIiIiIiIbYdFNREREREREZCMsuomIiIiIiIhshEU3ERERERERkY2w6CYiIiIiIiKyERbdRERERERERDbCopuIiIiIiIjIRlh0ExEREREREdkIi24iIiIiIiIiG/l/BQDzFnTeufYAAAAASUVORK5CYII=" - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "execution_count": 10 - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "1f808f90-0e37-469e-b568-178d84e3e504", - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "82e625e63aa14c4ea99c6a881e166c8f", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Button(description='Add Display', style=ButtonStyle())" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "e612fe6d0fc64f82b8e2b9d6b5f006f0", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Output()" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "33e817abcb4e42949e7521ee326298af", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "HBox(children=(Dropdown(description='Metric:', options=('CPU Util (Min/Max/Mean)', 'CPU Cores (Raw)', 'Mem', '…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "ef836a276959448aa6a29c3a116ca8dd", - "version_major": 2, - "version_minor": 0 - }, - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA+gAAAEsCAYAAABQRZlvAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/P9b71AAAACXBIWXMAAA9hAAAPYQGoP6dpAAB+hUlEQVR4nO3dd3gU1foH8O9szW46pGwiARIIRYogCgJeAYHQRLnoVRQVpEgMRaRKUQEpioqoAVSkKRcRf1KsmAgCSpF+QUAEIVJDaOlty/n9sdmVZRMIJLuz2f1+nidPsjOzM+fdbPLuO+fMGUkIIUBEREREREREslLI3QAiIiIiIiIiYoFORERERERE5BFYoBMRERERERF5ABboRERERERERB6ABToRERERERGRB2CBTkREREREROQBWKATEREREREReQAW6EREREREREQegAU6ERERERERkQdggU5ERERERETkAVigExEREREREXkAFuhEREREREREHoAFOhEREREREZEHYIFORERERERE5AFYoBMRERERERF5ABbo5HUOHDiA5557DrGxsfDz80NAQADuvvtuzJ49G1euXLFv1759e0iSZP/S6XS46667MHfuXFgsFoftGjduXOqxLl26BEmSMGXKlBu2adOmTZAkCf/3f/9X6vphw4ZBkiSHZTNnzsTatWvL3NemTZvsy6ZMmeL0/BsZMGAAunbtan+clpZmfx3KimXAgAH2ba7Vvn17tG/fvtzHLs2VK1egVquxZs0aAEDt2rUhSVKZ+/3000/tbbn2dXAVs9mMiIgIvPvuuwD+ee/ExcVBCOG0/ZYtW+ztW7p0qcvbVx6LFi3CHXfcgby8PLmbQkTkUTzxc4PNhQsX8PLLL6NJkyYICAiAn58f4uPj8eKLL+LYsWMVilsuHTt2RGJiov2x7XON7UupVCI8PBw9e/bE7t273dq2P//8ExqNBnv37nXrcYmuxQKdvMrChQvRokUL7Nq1C2PHjsX69euxZs0a/Oc//8GHH36IgQMHOmwfFxeH7du3Y/v27fjiiy9wxx134KWXXsKECRNkiuAfZRXod999N7Zv34677777tva7b98+LFu2DNOnT3daFxgYiKVLlzp80ACA3NxcfPnllwgKCnJ6zvz58zF//vzbaovNunXroNFoHE4aBAYGYsuWLfjrr7+ctl+8eHGpbXGVLVu24OLFi+jdu7dD+06ePImNGzfK3r7y6NevH/z9/TF79my5m0JE5DE8+XPDzp070aRJEyxatAiPPfYYVq9ejfXr12PMmDHYu3cvWrZsWenHdLV169Zh69ateOWVV5zWzZw5E9u3b8emTZvwyiuvYNu2bWjXrp1bT0TUq1cPffv2xUsvveS2YxI5EUReYtu2bUKpVIquXbuKwsJCp/VFRUVi3bp19sft2rUTjRo1ctimuLhYxMXFCb1eL4qLi8vczubixYsCgHjttddu2Laff/5ZABBffvllqeuHDh0qrv9z9Pf3F/369bvhfm1ee+01p+eX5fHHHxf33Xefw7KTJ08KAGLQoEECgEhJSXFY/8knnwidTieefvrpch/nVnTv3l089thj9se1atUS3bp1EzVq1BATJ0502Pb48eNCkiQxePBgAUD8/PPPld6e6yUlJYl77rnH/tj2nrjvvvvEU0895bBtdna20Ov19vYtWbLE5e0rr7ffflsEBweLvLw8uZtCRCQ7T/7ckJWVJQwGg4iJiRGnT58udZuyPlPcKnfmhJYtW4o+ffo4LCvrM9KyZcsEAPHqq6+6rX1CCLF7924BQGzdutWtxyWyYQ86eY2ZM2dCkiR8/PHH0Gq1Tus1Gg0efvjhG+5DrVajRYsWyM/Px8WLF13V1JuSJAl5eXlYtmyZfciXbbh3aUPcy+vChQtYs2YNnnnmmVLX169fH23atMHixYsdli9evBi9e/dGcHCw03OuH+JuGy7/9ttvY86cOYiNjUVAQABat26NHTt2OD0/OzsbP/30Ex599FGH5QqFAs8++yyWLVvm0KO/ePFixMTEoFOnTk772r17N/r06YPatWtDp9Ohdu3aePLJJ/H333/btxFCoHv37qhevTpOnTplX56fn49GjRqhYcOGDsPAhRBYs2aNU/sA67D/1atXIzMz075s5cqVAIA+ffo4bQ8Ax44dw1NPPYWIiAhotVo0bNgQ8+bNc9imsLAQo0ePRrNmzRAcHIxq1aqhdevWWLdundP+JEnCsGHD8Nlnn6Fhw4bQ6/W466678O233zpt27dvX2RnZ9vbSETkyzz5c8PChQuRnp6O2bNno0aNGqVu89hjjzk8/vrrr9G6dWvo9XoEBgaic+fO2L59u8M2tkvi9u7di8ceewyhoaGoU6cOAGu+mz9/Ppo1awadTofQ0FA89thjOHHihMM+9u3bh4ceesiex6Kjo9GjRw+cOXPmhjHt27cPO3fuLPMzyPXuueceANbPLteaOnUqWrVqhWrVqiEoKAh33303Fi1a5HDJ2dixYxEcHAyz2WxfNnz4cEiShLfeesu+7PLly1AoFPjggw/sy1q0aIGGDRviww8/LFc7iSobC3TyCmazGRs3bkSLFi0QExNToX399ddfUKlUCA0NraTW3brt27dDp9Ohe/fu9qF0FR1GDgApKSkwGo3o0KFDmdsMHDgQa9euxdWrVwEAR48exbZt25yG+d3MvHnzkJqairlz5+K///0v8vLy0L17d2RlZTls980330CSJPTo0cNpHwMGDMC5c+fw448/ArD+npctW4b+/ftDoXD+95WWlob69etj7ty5+PHHH/Hmm2/i/PnzuPfee3Hp0iUA1oL2s88+g16vx+OPPw6j0QgASEpKwsmTJ7Fq1Sr4+/vb97lt2zacP3++1AK9T58+UCqV+Pzzz+3LbEMRSxvifvjwYdx77734/fff8c477+Dbb79Fjx49MGLECEydOtW+XVFREa5cuYIxY8Zg7dq1+Pzzz3H//fejd+/e+PTTT532+9133yE5ORnTpk3DV199hWrVquHf//6304cqg8GABg0a4LvvvnPaBxGRL/H0zw0pKSlQKpXo2bNnubZfsWIFHnnkEQQFBeHzzz/HokWLcPXqVbRv3x6//vqr0/a9e/dG3bp18eWXX9oL0SFDhmDkyJHo1KkT1q5di/nz5+PQoUNo06aNvUjOy8tD586dceHCBYc8X7NmTeTk5Nywjd9++y2USiUeeOCBcsV08uRJANZh59dKS0vDkCFDsGrVKqxevRq9e/fG8OHD8frrr9u36dSpE7Kzs7Fz5077sp9++gk6nQ6pqan2ZRs2bIAQwumkf/v27fHDDz+UOs8MkcvJ2X1PVFnS09MFAKdhUzdiG4JmNBqF0WgU586dEy+//LIAIP7zn/84bVcaOYa42/Z17dDu8g5xf+GFF4ROpxMWi8VhuW2I+1tvvSVycnJEQECASE5OFkIIMXbsWBEbGyssFkup7WzXrp1o166d076aNGkiTCaTffnOnTsFAPH55587PL9Xr16iZ8+eDstq1aolevToYd+/bfj7d999JyRJEidPnhRffvnlTYe4m0wmkZubK/z9/cV7773nsO7XX38V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Rs+ePTF69OhKbYNNYWEhvv/++xv2Ttu88MIL6NmzJ1599dUyz75LkgQhhNMHi08++aTUnozXXnsNv/zyC/773//iiy++wP/+9z+nXvSvvvqqXO3r168fevbsibFjx+KOO+4odRu9Xo8OHTpg3759aNq0Ke655x6nL9uHLEmSoNFoHD5ApaenlzqL+606ceIE7rzzzgrvh4ioqvPkzw0DBw6EwWDAuHHjyjy5v3r1agDWO7DccccdWLFihcNnjLy8PHz11Vf2md1v5KGHHoIQAmfPni01PzVp0sTpOZIk4a677sK7776LkJCQm3YANGjQwGny0hsZN24c6tatizfeeMM+AZ0kSVCpVA6X0BUUFOCzzz5zen7Lli0RFBSEuXPnIj09HZ07dwZg/f3s27cPq1atwp133ono6Gin5544cQIKhQL169cvd3uJKgt70MlrtG7dGgsWLEBSUhJatGiBF154AY0aNYLRaMS+ffvw8ccfo3HjxuWeEdWmb9++mD9/Ph5//HG8/PLLuPfee1FQUIDvv/8eCxcuxPDhwxEXF3fT/bz99tt4/PHH0bFjRwwePBgGgwHHjh3DG2+8AY1Gg1deecVh+yZNmmDTpk345ptvEBUVhcDAwAonivbt20MIgd9++80+RKwsTz/9NJ5++ukKHe9G1q9fj/z8fPTq1eum2zZr1qzUs+PXCgoKwgMPPIC33noLYWFhqF27NjZv3oxFixY5Xb+WmpqKWbNm4ZVXXrGfOZ81axbGjBmD9u3b49///jf279+Pv/76q1wFenR09E3bBwDvvfce7r//fvzrX//CCy+8gNq1ayMnJwfHjx/HN998g40bNwKwflBavXo1kpKS8Nhjj+H06dN4/fXXERUVhWPHjt30OGWxWCzYuXPnLc/IT0TkjTz5c0NwcDDWrVuHhx56CM2bN8ewYcPQunVraDQaHDt2DMuXL8f//vc/9O7dGwqFArNnz0bfvn3x0EMPYciQISgqKsJbb72FzMxMvPHGGzdtc9u2bfH888/jueeew+7du/HAAw/A398f58+fx6+//oomTZrghRdewLfffov58+ejV69eiIuLgxDCfrtRWwFclvbt22Px4sX4888/nWZmL41arcbMmTPx+OOP47333sPkyZPRo0cPzJkzB0899RSef/55XL58GW+//Xapvf5KpRLt2rXDN998g9jYWPvt5Nq2bQutVosNGzZgxIgRpR57x44daNasmax39CEfJsvUdEQutH//ftGvXz9Rs2ZNodFohL+/v2jevLl49dVXRUZGhn27G82yer3s7Gwxbtw4ER8fLzQajdDr9eKee+4RH374odOM6Dfy008/iYSEBBESEiJUKpWIiooSTz/9tDh27FipcbRt21bo9XoBwD5TekVmcTebzaJ27doiKSnJYfm1s7jfyK3M4l7avnDNzLVPP/20w/Oude0s7mUpbRb3M2fOiEcffVSEhoaKwMBA0bVrV/H777+LWrVq2WfEP3funIiIiBAPPvigw2y3FotF9OzZU4SEhIiTJ0+KyZMni1q1apV67PK8d0qbxV0I6+szYMAAcccddwi1Wi3Cw8NFmzZtxPTp0x22e+ONN0Tt2rWFVqsVDRs2FAsXLiz19wxADB061On418Zss2HDBgFA7Nmz54ZtJyLyJZ78uSE9PV2MHz9eNGrUSOj1eqHVakXdunXFkCFDxMGDBx22Xbt2rWjVqpXw8/MT/v7+omPHjmLr1q0O29jyyMWLF0s93uLFi0WrVq2Ev7+/0Ol0ok6dOuLZZ58Vu3fvFkII8ccff4gnn3xS1KlTR+h0OhEcHCxatmwpli5detNYsrKyREBAgJg9e7bD8pvd6aZVq1YiNDRUZGZm2ttYv359odVqRVxcnJg1a5ZYtGiRACBOnjzp8Nz33ntPABCDBw92WN65c2cBQHz99ddOx8vJyRF6vd5pRnsid5GE4A3+iHzJO++8gxkzZuDs2bPQ6XSytKG4uBgRERF4/fXXMXz4cFnacDN33nknunXrhnfeeUfuplSaZ555BidOnMDWrVvlbgoREfmg4cOHY8OGDTh06NAt3x3GXRYtWoQXX3wRp0+fZg86yYIFOpGPKSwsRMOGDTF06FD7pCvk/f766y80bNgQGzduxP333y93c4iIyAdduHAB9erVw6JFi/DYY4/J3RwnJpMJd955J/r162efGZ7I3ThJHJGP8fPzw2effVbq9VrkvU6dOoXk5GQW50REJJvIyEj897//9djbl50+fRpPP/20yybIJSoP9qATEREREREReQD2oBMRERERERF5ABboRERERERERB6A90Evg8Viwblz5xAYGOixs0wSEZHvEEIgJycH0dHRUCh4ft2G+ZqIiDxJRfM1C/QynDt3DjExMXI3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/NoQERF5EuZsIt/DAp2IymXjxo04d+4ctmzZgjlz5mDKlCl46KGHEBoait9++w2JiYlITEzE6dOnAQD5+fno0KEDAgICsGXLFvz6668ICAhA165dUVxcXOoxrl69it9//x333HOPfdn58+fx5JNPYsCAAThy5Ag2bdqE3r1749rBPy1btsTp06fx999/u/ZFICIiqgKYs4mqLhboRFQu1apVw/vvv4/69etjwIABqF+/PvLz8zFx4kTEx8djwoQJ0Gg02Lp1KwDrWXWFQoFPPvkETZo0QcOGDbFkyRKcOnUKmzZtKvUYf//9N4QQiI6Oti87f/48TCYTevfujdq1a6NJkyZISkpCQECAfZs77rgDAG56pp+IiMgXMGcTVV0quRtARFVDo0aNoFD8c04vMjISjRs3tj9WKpWoXr06MjIyAAB79uzB8ePHERgY6LCfwsJC/PXXX6Ueo6CgAADg5+dnX3bXXXehY8eOaNKkCbp06YKEhAQ89thjCA0NtW+j0+kAWHsAiIiIfB1zNlHVxQKdiMpFrVY7PJYkqdRlFosFAGCxWNCiRQv897//ddpXeHh4qccICwsDYB02Z9tGqVQiNTUV27ZtQ0pKCj744ANMmjQJv/32m30G2CtXrtxwv0RERL6EOZuo6uIQdyJyibvvvhvHjh1DREQE6tat6/BV1oyzderUQVBQEA4fPuywXJIktG3bFlOnTsW+ffug0WiwZs0a+/rff/8darUajRo1cmlMRERE3og5m8hzsEAnIpfo27cvwsLC8Mgjj+CXX37ByZMnsXnzZrz44os4c+ZMqc9RKBTo1KkTfv31V/uy3377DTNnzsTu3btx6tQprF69GhcvXkTDhg3t2/zyyy/417/+ZR82R0REROXHnE3kOVigE5FL6PV6bNmyBTVr1kTv3r3RsGFDDBgwAAUFBQgKCirzec8//zxWrlxpH3YXFBSELVu2oHv37qhXrx4mT56Md955B926dbM/5/PPP8fgwYNdHhMREZE3Ys4m8hySuPa+B0REMhNC4L777sPIkSPx5JNP3nT77777DmPHjsWBAwegUnFaDSIiIndhziaqfOxBJyKPIkkSPv74Y5hMpnJtn5eXhyVLljDRExERuRlzNlHlYw86ERERERERkQdgDzoRERERERGRB2CBTkREREREROQBWKATEREREREReQAW6EREREREREQegAU6ERERERERkQdggU5ERERERETkAVigExEREREREXkAFuhEREREREREHoAFOhEREREREZEHYIFORERERERE5AFYoBMRERERERF5ABboRERERERERB6ABToRERERERGRB2CBTkREREREROQB/h+9ARrJ80YjHQAAAABJRU5ErkJggg==", - "text/html": [ - "\n", - "
\n", - "
\n", - " Figure\n", - "
\n", - " \n", - "
\n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "%%display_graph_for_index 0" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "f5368a55-4cd0-437c-a38b-1557a5dbdd88", - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "3d6ffd83e6624207aeff591b601c65ca", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Button(description='Add Display', style=ButtonStyle())" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "f957405cd8d242d285264336f1fe5498", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Output()" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "a8c296952d4d4d879add40a58c229645", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "HBox(children=(Dropdown(description='Metric:', options=('CPU Util (Min/Max/Mean)', 'CPU Cores (Raw)', 'Mem', '…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "3e1f389785e84f9f92edce112f0b6020", - "version_major": 2, - "version_minor": 0 - }, - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA+gAAAEsCAYAAABQRZlvAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/P9b71AAAACXBIWXMAAA9hAAAPYQGoP6dpAAB+hUlEQVR4nO3dd3gU1foH8O9szW46pGwiARIIRYogCgJeAYHQRLnoVRQVpEgMRaRKUQEpioqoAVSkKRcRf1KsmAgCSpF+QUAEIVJDaOlty/n9sdmVZRMIJLuz2f1+nidPsjOzM+fdbPLuO+fMGUkIIUBEREREREREslLI3QAiIiIiIiIiYoFORERERERE5BFYoBMRERERERF5ABboRERERERERB6ABToRERERERGRB2CBTkREREREROQBWKATEREREREReQAW6EREREREREQegAU6ERERERERkQdggU5ERERERETkAVigExEREREREXkAFuhEREREREREHoAFOhEREREREZEHYIFORERERERE5AFYoBMRERERERF5ABbo5HUOHDiA5557DrGxsfDz80NAQADuvvtuzJ49G1euXLFv1759e0iSZP/S6XS46667MHfuXFgsFoftGjduXOqxLl26BEmSMGXKlBu2adOmTZAkCf/3f/9X6vphw4ZBkiSHZTNnzsTatWvL3NemTZvsy6ZMmeL0/BsZMGAAunbtan+clpZmfx3KimXAgAH2ba7Vvn17tG/fvtzHLs2VK1egVquxZs0aAEDt2rUhSVKZ+/3000/tbbn2dXAVs9mMiIgIvPvuuwD+ee/ExcVBCOG0/ZYtW+ztW7p0qcvbVx6LFi3CHXfcgby8PLmbQkTkUTzxc4PNhQsX8PLLL6NJkyYICAiAn58f4uPj8eKLL+LYsWMVilsuHTt2RGJiov2x7XON7UupVCI8PBw9e/bE7t273dq2P//8ExqNBnv37nXrcYmuxQKdvMrChQvRokUL7Nq1C2PHjsX69euxZs0a/Oc//8GHH36IgQMHOmwfFxeH7du3Y/v27fjiiy9wxx134KWXXsKECRNkiuAfZRXod999N7Zv34677777tva7b98+LFu2DNOnT3daFxgYiKVLlzp80ACA3NxcfPnllwgKCnJ6zvz58zF//vzbaovNunXroNFoHE4aBAYGYsuWLfjrr7+ctl+8eHGpbXGVLVu24OLFi+jdu7dD+06ePImNGzfK3r7y6NevH/z9/TF79my5m0JE5DE8+XPDzp070aRJEyxatAiPPfYYVq9ejfXr12PMmDHYu3cvWrZsWenHdLV169Zh69ateOWVV5zWzZw5E9u3b8emTZvwyiuvYNu2bWjXrp1bT0TUq1cPffv2xUsvveS2YxI5EUReYtu2bUKpVIquXbuKwsJCp/VFRUVi3bp19sft2rUTjRo1ctimuLhYxMXFCb1eL4qLi8vczubixYsCgHjttddu2Laff/5ZABBffvllqeuHDh0qrv9z9Pf3F/369bvhfm1ee+01p+eX5fHHHxf33Xefw7KTJ08KAGLQoEECgEhJSXFY/8knnwidTieefvrpch/nVnTv3l089thj9se1atUS3bp1EzVq1BATJ0502Pb48eNCkiQxePBgAUD8/PPPld6e6yUlJYl77rnH/tj2nrjvvvvEU0895bBtdna20Ov19vYtWbLE5e0rr7ffflsEBweLvLw8uZtCRCQ7T/7ckJWVJQwGg4iJiRGnT58udZuyPlPcKnfmhJYtW4o+ffo4LCvrM9KyZcsEAPHqq6+6rX1CCLF7924BQGzdutWtxyWyYQ86eY2ZM2dCkiR8/PHH0Gq1Tus1Gg0efvjhG+5DrVajRYsWyM/Px8WLF13V1JuSJAl5eXlYtmyZfciXbbh3aUPcy+vChQtYs2YNnnnmmVLX169fH23atMHixYsdli9evBi9e/dGcHCw03OuH+JuGy7/9ttvY86cOYiNjUVAQABat26NHTt2OD0/OzsbP/30Ex599FGH5QqFAs8++yyWLVvm0KO/ePFixMTEoFOnTk772r17N/r06YPatWtDp9Ohdu3aePLJJ/H333/btxFCoHv37qhevTpOnTplX56fn49GjRqhYcOGDsPAhRBYs2aNU/sA67D/1atXIzMz075s5cqVAIA+ffo4bQ8Ax44dw1NPPYWIiAhotVo0bNgQ8+bNc9imsLAQo0ePRrNmzRAcHIxq1aqhdevWWLdundP+JEnCsGHD8Nlnn6Fhw4bQ6/W466678O233zpt27dvX2RnZ9vbSETkyzz5c8PChQuRnp6O2bNno0aNGqVu89hjjzk8/vrrr9G6dWvo9XoEBgaic+fO2L59u8M2tkvi9u7di8ceewyhoaGoU6cOAGu+mz9/Ppo1awadTofQ0FA89thjOHHihMM+9u3bh4ceesiex6Kjo9GjRw+cOXPmhjHt27cPO3fuLPMzyPXuueceANbPLteaOnUqWrVqhWrVqiEoKAh33303Fi1a5HDJ2dixYxEcHAyz2WxfNnz4cEiShLfeesu+7PLly1AoFPjggw/sy1q0aIGGDRviww8/LFc7iSobC3TyCmazGRs3bkSLFi0QExNToX399ddfUKlUCA0NraTW3brt27dDp9Ohe/fu9qF0FR1GDgApKSkwGo3o0KFDmdsMHDgQa9euxdWrVwEAR48exbZt25yG+d3MvHnzkJqairlz5+K///0v8vLy0L17d2RlZTls980330CSJPTo0cNpHwMGDMC5c+fw448/ArD+npctW4b+/ftDoXD+95WWlob69etj7ty5+PHHH/Hmm2/i/PnzuPfee3Hp0iUA1oL2s88+g16vx+OPPw6j0QgASEpKwsmTJ7Fq1Sr4+/vb97lt2zacP3++1AK9T58+UCqV+Pzzz+3LbEMRSxvifvjwYdx77734/fff8c477+Dbb79Fjx49MGLECEydOtW+XVFREa5cuYIxY8Zg7dq1+Pzzz3H//fejd+/e+PTTT532+9133yE5ORnTpk3DV199hWrVquHf//6304cqg8GABg0a4LvvvnPaBxGRL/H0zw0pKSlQKpXo2bNnubZfsWIFHnnkEQQFBeHzzz/HokWLcPXqVbRv3x6//vqr0/a9e/dG3bp18eWXX9oL0SFDhmDkyJHo1KkT1q5di/nz5+PQoUNo06aNvUjOy8tD586dceHCBYc8X7NmTeTk5Nywjd9++y2USiUeeOCBcsV08uRJANZh59dKS0vDkCFDsGrVKqxevRq9e/fG8OHD8frrr9u36dSpE7Kzs7Fz5077sp9++gk6nQ6pqan2ZRs2bIAQwumkf/v27fHDDz+UOs8MkcvJ2X1PVFnS09MFAKdhUzdiG4JmNBqF0WgU586dEy+//LIAIP7zn/84bVcaOYa42/Z17dDu8g5xf+GFF4ROpxMWi8VhuW2I+1tvvSVycnJEQECASE5OFkIIMXbsWBEbGyssFkup7WzXrp1o166d076aNGkiTCaTffnOnTsFAPH55587PL9Xr16iZ8+eDstq1aolevToYd+/bfj7d999JyRJEidPnhRffvnlTYe4m0wmkZubK/z9/cV7773nsO7XX38VKpVKjBw5UixevFgAEJ988onTPkaOHCmaNGniFLPtPdGvXz/78PdDhw4JAGLTpk1i165dTkPcu3TpImrUqCGysrIc9jds2DDh5+cnrly5UmYcRqNRDBw4UDRv3txhHQARGRkpsrOz7cvS09OFQqEQs2bNctpX3759RWRkZKnHISLyFZ7+uaFBgwbCYDCUq11ms1lER0eLJk2aCLPZbF+ek5MjIiIiRJs2bezLbJ8Xrh82vn37dgFAvPPOOw7LT58+LXQ6nRg3bpwQ4p/h32vXri1X267VrVs30aBBA6flts81X3zxhTAajSI/P19s3bpV1K9fX9x5553i6tWrN4zdaDSKadOmierVq9s/3+Tl5QmNRiOmTZsmhBDizJkzAoAYP3680Ol09ksaBg8eLKKjo532u3DhQgFAHDly5JbjJKoo9qCTTzt06BDUajXUajWio6PxzjvvoG/fvli4cKHcTXOJc+fOITw8/IYzvgcEBOA///kPFi9eDJPJhE8//RTPPffcLc0SDwA9evSAUqm0P27atCkAOAw3z8vLw48//lhq77TNgAED8PXXX+Py5ctYtGgROnTogNq1a5e6bW5uLsaPH4+6detCpVJBpVIhICAAeXl5OHLkiMO2bdu2xYwZMzB37ly88MILePrpp0sdJbB69eqbtm/37t04ePAgFi1ahDp16pTaO1BYWIgNGzbg3//+N/R6PUwmk/2re/fuKCwsdLgE4Msvv0Tbtm0REBAAlUoFtVqNRYsWOcUBAB06dEBgYKD9cWRkJCIiIhxea5uIiAhkZGTAZDKVGRMREZXOEz83HD16FOfOncMzzzzjMLosICAAjz76KHbs2IH8/HyH51yf17799ltIkoSnn37aIT8ZDAbcdddd9svq6tati9DQUIwfPx4ffvghDh8+XO52njt3DhEREWWuf+KJJ6BWq6HX69G2bVtkZ2fju+++Q0hIiMN2GzduRKdOnRAcHAylUgm1Wo1XX30Vly9fRkZGBgBAr9ejdevW+OmnnwAAqampCAkJwdixY1FcXGwfVfDTTz+VesmcrZ1nz54td3xElYUFOnmFsLAw6PV6+3Co8qpTpw527dqF3bt34/fff0dmZiaWL1/ucK21SqVyuIbpWrYiR61W3/A4KpUKAG64H9s2rlRQUAA/P7+bbjdw4EDs3bsXM2bMwMWLF9G/f/9bPlb16tUdHtuu7ysoKLAv++6772A0Gm94jd9jjz0GPz8/vPvuu/jmm29uONT+qaeeQnJyMgYNGoQff/wRO3fuxK5duxAeHu5wXJu+fftCo9GgqKgIY8eOdVq/c+dOnDp16oYF+gMPPID4+Hh89NFH+Oyzz+y3o7ve5cuXYTKZ8MEHH9g/3Nm+unfvDgD2YfirV6/G448/jjvuuAPLly/H9u3bsWvXLgwYMACFhYVO+77+tQasr3dpMfv5+UEIUep+iIh8had/bqhZsyYuXrxYrltjXr58GQAQFRXltC46OhoWi8V+2ZrN9dteuHABQghERkY65agdO3bY81NwcDA2b96MZs2aYeLEiWjUqBGio6Px2muv2S8ZK8vNPoO8+eab2LVrFzZv3oxJkybhwoUL6NWrF4qKiuzb7Ny5EwkJCQCs1+lv3boVu3btwqRJk+zHsOnUqRN27NiBvLw8/PTTT3jwwQdRvXp1tGjRAj/99BNOnjyJkydPllqg29pZWh4lcjXXVwREbqBUKtGxY0f88MMPOHPmTJkTqlzPz8/PPglJWSIjI7Fr1y4IIZwKL9uZ1cjIyJvu49rtr3f27Nmb7qMyhIWFlevenm3btkX9+vUxbdo0dO7cucLX55Xlq6++woMPPnjD6/b0ej369OmDWbNmISgoyOFWZ9fKysrCt99+i9deew0vv/yyfbnteu7rmc1m9O3bF6GhodBqtRg4cCC2bt0KjUbj0L569eqVeT9bm+eeew6TJ0+GJEno169fqduEhoZCqVTimWeewdChQ0vdJjY2FgCwfPlyxMbG4osvvnB4z137IeV2XblyBVqtFgEBARXeFxFRVeXpnxu6dOmClJQUfPPNN2VOOmpjO0l7/vx5p3Xnzp2DQqFwyrPXtyssLAySJOGXX34pdcK8a5c1adIEK1euhBACBw4cwNKlSzFt2jTodDqH/Hu9sLCwUvOxTVxcnP21feCBB6DT6TB58mR88MEHGDNmDADrRKxqtRrffvutQ7Ff2m1pO3bsiFdeeQVbtmzBhg0b8Nprr9mXp6Sk2HNux44dnZ5ra2dYWFiZ7SVyFfagk9eYMGEChBAYPHgwiouLndYbjUZ88803t7xf20Qj69evd1q3atUqKBQKPPjggzfcR3x8PGrVqoUvv/zSacKRixcv4ueff3Y6g1tWD2hFNGjQAJcvX3aaqK00kydPRs+ePTF69OhKbYNNYWEhvv/++xv2Ttu88MIL6NmzJ1599dUyz75LkgQhhNMHi08++aTUnozXXnsNv/zyC/773//iiy++wP/+9z+nXvSvvvqqXO3r168fevbsibFjx+KOO+4odRu9Xo8OHTpg3759aNq0Ke655x6nL9uHLEmSoNFoHD5ApaenlzqL+606ceIE7rzzzgrvh4ioqvPkzw0DBw6EwWDAuHHjyjy5v3r1agDWO7DccccdWLFihcNnjLy8PHz11Vf2md1v5KGHHoIQAmfPni01PzVp0sTpOZIk4a677sK7776LkJCQm3YANGjQwGny0hsZN24c6tatizfeeMM+AZ0kSVCpVA6X0BUUFOCzzz5zen7Lli0RFBSEuXPnIj09HZ07dwZg/f3s27cPq1atwp133ono6Gin5544cQIKhQL169cvd3uJKgt70MlrtG7dGgsWLEBSUhJatGiBF154AY0aNYLRaMS+ffvw8ccfo3HjxuWeEdWmb9++mD9/Ph5//HG8/PLLuPfee1FQUIDvv/8eCxcuxPDhwxEXF3fT/bz99tt4/PHH0bFjRwwePBgGgwHHjh3DG2+8AY1Gg1deecVh+yZNmmDTpk345ptvEBUVhcDAwAonivbt20MIgd9++80+RKwsTz/9NJ5++ukKHe9G1q9fj/z8fPTq1eum2zZr1qzUs+PXCgoKwgMPPIC33noLYWFhqF27NjZv3oxFixY5Xb+WmpqKWbNm4ZVXXrGfOZ81axbGjBmD9u3b49///jf279+Pv/76q1wFenR09E3bBwDvvfce7r//fvzrX//CCy+8gNq1ayMnJwfHjx/HN998g40bNwKwflBavXo1kpKS8Nhjj+H06dN4/fXXERUVhWPHjt30OGWxWCzYuXPnLc/IT0TkjTz5c0NwcDDWrVuHhx56CM2bN8ewYcPQunVraDQaHDt2DMuXL8f//vc/9O7dGwqFArNnz0bfvn3x0EMPYciQISgqKsJbb72FzMxMvPHGGzdtc9u2bfH888/jueeew+7du/HAAw/A398f58+fx6+//oomTZrghRdewLfffov58+ejV69eiIuLgxDCfrtRWwFclvbt22Px4sX4888/nWZmL41arcbMmTPx+OOP47333sPkyZPRo0cPzJkzB0899RSef/55XL58GW+//Xapvf5KpRLt2rXDN998g9jYWPvt5Nq2bQutVosNGzZgxIgRpR57x44daNasmax39CEfJsvUdEQutH//ftGvXz9Rs2ZNodFohL+/v2jevLl49dVXRUZGhn27G82yer3s7Gwxbtw4ER8fLzQajdDr9eKee+4RH374odOM6Dfy008/iYSEBBESEiJUKpWIiooSTz/9tDh27FipcbRt21bo9XoBwD5TekVmcTebzaJ27doiKSnJYfm1s7jfyK3M4l7avnDNzLVPP/20w/Oude0s7mUpbRb3M2fOiEcffVSEhoaKwMBA0bVrV/H777+LWrVq2WfEP3funIiIiBAPPvigw2y3FotF9OzZU4SEhIiTJ0+KyZMni1q1apV67PK8d0qbxV0I6+szYMAAcccddwi1Wi3Cw8NFmzZtxPTp0x22e+ONN0Tt2rWFVqsVDRs2FAsXLiz19wxADB061On418Zss2HDBgFA7Nmz54ZtJyLyJZ78uSE9PV2MHz9eNGrUSOj1eqHVakXdunXFkCFDxMGDBx22Xbt2rWjVqpXw8/MT/v7+omPHjmLr1q0O29jyyMWLF0s93uLFi0WrVq2Ev7+/0Ol0ok6dOuLZZ58Vu3fvFkII8ccff4gnn3xS1KlTR+h0OhEcHCxatmwpli5detNYsrKyREBAgJg9e7bD8pvd6aZVq1YiNDRUZGZm2ttYv359odVqRVxcnJg1a5ZYtGiRACBOnjzp8Nz33ntPABCDBw92WN65c2cBQHz99ddOx8vJyRF6vd5pRnsid5GE4A3+iHzJO++8gxkzZuDs2bPQ6XSytKG4uBgRERF4/fXXMXz4cFnacDN33nknunXrhnfeeUfuplSaZ555BidOnMDWrVvlbgoREfmg4cOHY8OGDTh06NAt3x3GXRYtWoQXX3wRp0+fZg86yYIFOpGPKSwsRMOGDTF06FD7pCvk/f766y80bNgQGzduxP333y93c4iIyAdduHAB9erVw6JFi/DYY4/J3RwnJpMJd955J/r162efGZ7I3ThJHJGP8fPzw2effVbq9VrkvU6dOoXk5GQW50REJJvIyEj897//9djbl50+fRpPP/20yybIJSoP9qATEREREREReQD2oBMRERERERF5ABboRERERERERB6A90Evg8Viwblz5xAYGOixs0wSEZHvEEIgJycH0dHRUCh4ft2G+ZqIiDxJRfM1C/QynDt3DjExMXI3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/NoQERF5EuZsIt/DAp2IymXjxo04d+4ctmzZgjlz5mDKlCl46KGHEBoait9++w2JiYlITEzE6dOnAQD5+fno0KEDAgICsGXLFvz6668ICAhA165dUVxcXOoxrl69it9//x333HOPfdn58+fx5JNPYsCAAThy5Ag2bdqE3r1749rBPy1btsTp06fx999/u/ZFICIiqgKYs4mqLhboRFQu1apVw/vvv4/69etjwIABqF+/PvLz8zFx4kTEx8djwoQJ0Gg02Lp1KwDrWXWFQoFPPvkETZo0QcOGDbFkyRKcOnUKmzZtKvUYf//9N4QQiI6Oti87f/48TCYTevfujdq1a6NJkyZISkpCQECAfZs77rgDAG56pp+IiMgXMGcTVV0quRtARFVDo0aNoFD8c04vMjISjRs3tj9WKpWoXr06MjIyAAB79uzB8ePHERgY6LCfwsJC/PXXX6Ueo6CgAADg5+dnX3bXXXehY8eOaNKkCbp06YKEhAQ89thjCA0NtW+j0+kAWHsAiIiIfB1zNlHVxQKdiMpFrVY7PJYkqdRlFosFAGCxWNCiRQv897//ddpXeHh4qccICwsDYB02Z9tGqVQiNTUV27ZtQ0pKCj744ANMmjQJv/32m30G2CtXrtxwv0RERL6EOZuo6uIQdyJyibvvvhvHjh1DREQE6tat6/BV1oyzderUQVBQEA4fPuywXJIktG3bFlOnTsW+ffug0WiwZs0a+/rff/8darUajRo1cmlMRERE3og5m8hzsEAnIpfo27cvwsLC8Mgjj+CXX37ByZMnsXnzZrz44os4c+ZMqc9RKBTo1KkTfv31V/uy3377DTNnzsTu3btx6tQprF69GhcvXkTDhg3t2/zyyy/417/+ZR82R0REROXHnE3kOVigE5FL6PV6bNmyBTVr1kTv3r3RsGFDDBgwAAUFBQgKCirzec8//zxWrlxpH3YXFBSELVu2oHv37qhXrx4mT56Md955B926dbM/5/PPP8fgwYNdHhMREZE3Ys4m8hySuPa+B0REMhNC4L777sPIkSPx5JNP3nT77777DmPHjsWBAwegUnFaDSIiIndhziaqfOxBJyKPIkkSPv74Y5hMpnJtn5eXhyVLljDRExERuRlzNlHlYw86ERERERERkQdgDzoRERERERGRB2CBTkREREREROQBWKATEREREREReQAW6EREREREREQegAU6ERERERERkQdggU5ERERERETkAVigExEREREREXkAFuhEREREREREHoAFOhEREREREZEHYIFORERERERE5AFYoBMRERERERF5ABboRERERERERB6ABToRERERERGRB2CBTkREREREROQB/h+9ARrJ80YjHQAAAABJRU5ErkJggg==", - "text/html": [ - "\n", - "
\n", - "
\n", - " Figure\n", - "
\n", - " \n", - "
\n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "%%display_graph_for_all" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "4336cf63-51b4-479b-9b5a-e5527788ffde", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "
\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
indextimestampcode
Loading... (need help?)
\n", - "\n", - "\n", - "
\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "%%display_code_history" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "63be1a14-4659-47d7-9358-c2d435d46bee", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Cell timestamp: 2024-05-11 00:31:34.607913\n", - "--\n", - "# 10⁹ iterations on 4 CPUs.on_click(partial(generate_plot, x=x, y=y))\n", - "compute_pi(10**9, 8)" - ] - } - ], - "source": [ - "%%display_code_for_index 0" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "3ddda407-4989-493d-90ba-dbb89d0841f8", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Exported performance data to myvar variable" - ] - } - ], - "source": [ - "%%perfdata_to_variable myvar" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "d1c55854-ce4c-4f5d-869a-9f920be6645b", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Exported performance data to myfile_perfdata.json and myfile_code.json" - ] - } - ], - "source": [ - "%%perfdata_to_json myfile" - ] - }, - { - "cell_type": "markdown", - "id": "ed4972cf-90c8-4ad8-81d8-b79dccad600b", - "metadata": {}, - "source": [ - "---\n", - "**Plans:**\n", - "- retrieve metrics on multiple nodes (add network, psutil delivers that)\n", - "- parallel serialization (for scorep)\n", - "- show index as thumbnail or so when hovering the graphs" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "29194d3f-ff1f-48a2-9a78-af8192b9dae8", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "JUmPER", - "language": "python", - "name": "jumper" - }, - "language_info": { - "file_extension": ".py", - "mimetype": "text/plain", - "name": "python" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/README.md b/README.md index b6a6859..6c7c05f 100644 --- a/README.md +++ b/README.md @@ -20,18 +20,23 @@ For binding to Score-P, the kernel uses the [Score-P Python bindings](https://gi # Table of Content -- [Installation](#Installation) -- [Usage](#Usage) - * [Monitoring](#Monitoring) - * [Score-P Instrumentation](#Score-P-Instrumentation) - * [Multi-Cell Mode](#Multi-Cell-Mode) - * [Write Mode](#Write-Mode) -- [Presentation of Performance Data](#Presentation-of-Performance-Data) -- [Limitations](#Limitations) -- [Future Work](#Future-Work) -- [Citing](#Citing) -- [Contact](#Contact) -- [Acknowledgments](#Acknowledgments) +- [A Jupyter Kernel for Performance Engineering](#a-jupyter-kernel-for-performance-engineering) +- [Table of Content](#table-of-content) +- [Installation](#installation) +- [Usage](#usage) + - [Monitoring](#monitoring) + - [Score-P Instrumentation](#score-p-instrumentation) + - [Configuring Score-P in Jupyter](#configuring-score-p-in-jupyter) + - [Multi-Cell Mode](#multi-cell-mode) + - [Write Mode](#write-mode) +- [Presentation of Performance Data](#presentation-of-performance-data) +- [Limitations](#limitations) + - [Serialization Type Support](#serialization-type-support) + - [Overhead](#overhead) +- [Future Work](#future-work) +- [Citing](#citing) +- [Contact](#contact) +- [Acknowledgments](#acknowledgments) # Installation @@ -123,11 +128,12 @@ Exports the performance data and the code to json files. ### Configuring Score-P in Jupyter -`%%scorep_env` - -Set up your Score-P environment. For a documentation of Score-P environment variables, see: [Score-P Measurement Configuration](https://perftools.pages.jsc.fz-juelich.de/cicd/scorep/tags/latest/html/scorepmeasurementconfig.html). - -![](doc/scorep_setup.png) +Set up your Score-P environment with `%env` line magic, e.g.: +``` +%env SCOREP_ENABLE_TRACING=1 +%env SCOREP_TOTAL_MEMORY=3g +``` +For a documentation of Score-P environment variables, see: [Score-P Measurement Configuration](https://perftools.pages.jsc.fz-juelich.de/cicd/scorep/tags/latest/html/scorepmeasurementconfig.html). `%%scorep_python_binding_arguments` @@ -198,7 +204,7 @@ Enables the write mode and starts the marking process. Subsequently, "running" c Stops the marking process and writes the marked cells in a Python script. Additionally, a bash script will be created for setting the Score-P environment variables, Pyhton bindings arguments and executing the Python script. **Hints**: -- Recording a cell containing `%%scorep_env` or `%%scorep_python_binding_arguments` will add the environment variables/Score-P Python bindings to the bash script. +- Recording a cell containing `%%scorep_python_binding_arguments` will add the Score-P Python bindings to the bash script. - Code of a cell which is not to be executed with Score-P (not inside the multicell mode and without `%%execute_with_scorep`) will be framed with `with scorep.instrumenter.disable()` in the Python script to prevent instrumentation. @@ -223,7 +229,7 @@ For the execution of a cell, the kernel uses the default IPython kernel. For a c > `dill` cannot yet pickle these standard types: > frame, generator, traceback -Similar yields for cloudpickle. Use the `%%serializer_settings` magic command to switch between both depending on your needs. +Similar yields for cloudpickle. Use the `%%marshalling_settings` magic command to switch between both depending on your needs. ## Overhead @@ -233,9 +239,8 @@ When dealing with big data structures, there might be a big runtime overhead at The kernel is still under development. The following is on the agenda: - - Check alternative Python implementations (Stackless/PyPy) for better serialization support - - Performance data visualizations - - Overhead reduction (we already implemented in-memory communication for persistence handling and plan to have a parallel serializer) + - Provide perfmonitors for multi node setups + - Config for default perfmonitor to define collected metrics PRs are welcome. diff --git a/examples/ExampleMonitoring.ipynb b/examples/ExampleMonitoring.ipynb new file mode 100644 index 0000000..26de264 --- /dev/null +++ b/examples/ExampleMonitoring.ipynb @@ -0,0 +1,418 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "34fc5489-608e-434d-93c2-cefe6b33890f", + "metadata": {}, + "source": [ + "### News about PyPerf Jupyter Kernel (Performance Monitoring and Analysis in Jupyter)\n", + "- added metrics to display in Jupyter (mean/max/min, raw values) for: CPU,GPU,Mem\n", + "- use dropdown menus to select metrics (interactively)\n", + "- use in-memory serialization instead of disk-based for Score-P execution\n", + "- modular system for backbone serializer (dill/cloudpickle)\n", + "\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "539781f0-7e0c-4988-b5c1-8a369a6de653", + "metadata": {}, + "source": [ + "**Toy Example:** Estimating Pi by Throwing Darts" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "a9e8242b-dd85-48dc-b67f-81b0926bf67e", + "metadata": { + "ExecuteTime": { + "end_time": "2024-08-14T16:17:42.040767Z", + "start_time": "2024-08-14T16:17:41.030411Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "env: JUMPER_REPORT_FREQUENCY=2\n" + ] + } + ], + "source": [ + "%env JUMPER_REPORT_FREQUENCY=2" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "2a10feac-0a7b-478a-ab36-71b3a1844f92", + "metadata": { + "ExecuteTime": { + "end_time": "2024-08-14T08:42:57.570647Z", + "start_time": "2024-08-14T08:42:56.565047Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "env: JUMPER_REPORTS_MIN=2\n" + ] + } + ], + "source": [ + "%env JUMPER_REPORTS_MIN=2" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "3bc9bbd5-4220-4361-afa0-e01bb5031996", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "env: SCOREP_ENABLE_TRACING=1\n", + "env: SCOREP_ENABLE_PROFILING=0\n", + "env: SCOREP_TOTAL_MEMORY=3g\n" + ] + } + ], + "source": [ + "%env SCOREP_ENABLE_TRACING=1\n", + "%env SCOREP_ENABLE_PROFILING=0\n", + "%env SCOREP_TOTAL_MEMORY=3g" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "519f9bc9-d715-480d-ac6f-cd2771ded5f3", + "metadata": { + "ExecuteTime": { + "end_time": "2024-08-14T08:42:58.580020Z", + "start_time": "2024-08-14T08:42:57.573894Z" + } + }, + "outputs": [], + "source": [ + "from random import random\n", + "import time\n", + "from multiprocessing import Pool\n", + "import sys" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "bbc9a5d8-bfcf-4810-b232-e49b4be5f1e6", + "metadata": { + "ExecuteTime": { + "end_time": "2024-08-14T08:42:59.590691Z", + "start_time": "2024-08-14T08:42:58.581417Z" + } + }, + "outputs": [], + "source": [ + "def throw_dart(iterations: int) -> int:\n", + " hits = 0\n", + " for i in range(iterations):\n", + " x = random()\n", + " y = random()\n", + " if (x * x) + (y * y) <= 1:\n", + " hits = hits + 1\n", + " return hits\n", + "\n", + "\n", + "def compute_pi(iterations, process_count):\n", + " pool = Pool(processes=process_count)\n", + " trials_per_process = [int(iterations / process_count)] * process_count\n", + "\n", + " start = time.time()\n", + "\n", + " hits = pool.map(throw_dart, trials_per_process)\n", + " pi = (sum(hits) * 4) / iterations\n", + "\n", + " end = time.time()\n", + "\n", + " print(pi)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "86b1962f-2053-4df6-b3fd-6f7e4fed48b3", + "metadata": { + "ExecuteTime": { + "end_time": "2024-08-14T08:46:45.581231Z", + "start_time": "2024-08-14T08:45:44.660151Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3.14178844\n", + "\n", + "----Performance Data----\n", + "Duration: 15.69\n", + "\n", + "CPU Util (Across CPUs) \tAVG: 53.33\t MIN: 0.50\t MAX: 100.00\n", + "Mem Util in GB (Across nodes)\tAVG: 13.49\t MIN: 13.49\t MAX: 13.50\n", + "IO Ops (excl.) Read \tTotal: 3784\n", + " Write \tTotal: 408\n", + "IO Bytes (excl.) Read \tTotal: 0.19\n", + " Write \tTotal: 0.29\n" + ] + } + ], + "source": [ + "# 10⁹ iterations on 2 CPUs\n", + "compute_pi(10**8, 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "47752868-6f55-4e03-be46-e14e4f83e11a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3.14178776\n", + "\n", + "----Performance Data----\n", + "Duration: 7.59\n", + "\n", + "CPU Util (Across CPUs) \tAVG: 99.55\t MIN: 85.70\t MAX: 100.00\n", + "Mem Util in GB (Across nodes)\tAVG: 13.50\t MIN: 13.49\t MAX: 13.51\n", + "IO Ops (excl.) Read \tTotal: 4123\n", + " Write \tTotal: 494\n", + "IO Bytes (excl.) Read \tTotal: 0.19\n", + " Write \tTotal: 0.32\n" + ] + } + ], + "source": [ + "# 10⁹ iterations on 8 CPUs\n", + "compute_pi(10**8, 8)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "461b590d-b319-4172-b95d-91c7989a8a5e", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ce5147eac02741ffbc85924f983349d2", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Button(description='Add Display', style=ButtonStyle())" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b06600309c39418598b96aaad1bdca00", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Output()" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "866be87a83a548e0a61b66c0ecf08e18", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(Dropdown(description='Metric:', options=('CPU Usage (Min/Max/Mean)', 'CPU Usage (Raw)', 'Mem in…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "1b852b9508984362a9c2a4731cae85b5", + "version_major": 2, + "version_minor": 0 + }, + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA+gAAAEsCAYAAABQRZlvAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/TGe4hAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOydd5hU1d34P/dOn9mZbWyFrSy9g4CAIIiCFMUWW97YEmNijMFYkryJKSYmr0aNLUZ/MdGYaGKL2BEFFARE2tLrNrazfWanz9zz+2N2F9ZdYPsueD7Psw/sveee+71nZ+453/NtihBCIJFIJBKJRCKRSCQSiaRfUftbAIlEIpFIJBKJRCKRSCRSQZdIJBKJRCKRSCQSiWRAIBV0iUQikUgkEolEIpFIBgBSQZdIJBKJRCKRSCQSiWQAIBV0iUQikUgkEolEIpFIBgBSQZdIJBKJRCKRSCQSiWQAIBV0iUQikUgkEolEIpFIBgBSQZdIJBKJRCKRSCQSiWQAIBV0iUQikUgkEolEIpFIBgBSQZdIJBKJRCKRSCQSiWQAIBV0iUQikUgkEolEIpFIBgBSQZdIJBKJRCKRSCQSiWQAIBV0iUQikUgkEolEIpFIBgBSQZdIJBKJRCKRSCQSiWQAIBV0ieQkfPnllxiNRoqKirrd14svvoiiKBQWFnZfMEmv8eyzz5Keno7f7+9Q+8LCQhRF4dNPP+1dwSQSiUQiOcvoyXVWb3Duuedy33339bcYkq8hUkEfwOTl5XHbbbeRnZ2N2WzG4XAwa9YsnnjiCbxeb0u7zMxMFEVp+UlMTGT27Nm89dZbrfrLzMxk6dKl7d5r69atKIrCiy++eEqZmhXNrVu3tnt+6dKlZGZmduo5Byo///nPue6668jIyGg5NnfuXBRFYdiwYe1e8/HHH7f8Hd54440el2nKlCncfvvtANx0000oioLD4Wj1eWjm8OHDLbI88sgjPS7LmSjf6bjpppsIBAI899xzvXqfadOmoSgKf/nLX3r1Pv1JQ0MD9913H8OGDcNisZCRkcG3v/1tjh492uE+BuI7UCKRnD0MxHeMXGfNbTXWFouF8ePH8/jjj6NpWp/K95Of/IQ///nPVFRU9Ol9JRKpoA9Q3n//fcaNG8drr73GJZdcwlNPPcUf/vAH0tPTuffee/nRj37Uqv3EiRP55z//yT//+U/uueceysrKuOKKK3j22Wf76QnObHJzc/nkk0/43ve+1+ac2WzmyJEjfPnll23Ovfzyy5jN5jbHv/Wtb+H1eltNQp2lvLycHTt2sGTJkpZjer0ej8fDu+++22FZeouBLl9HMJvN3HjjjTz22GMIIXrlHocPH2bLli1kZmby8ssv98o9+htN07jooot45plnuPzyy3nqqae47rrreP3115k5cyYul+u0fch3oEQi6U3kO6Z/OdU6a8iQIS1j/Yc//AGz2cxdd93F/fff36cyLlu2DIfDwTPPPNOn95VIEJIBR35+voiKihIjR44UZWVlbc4fPnxYPP744y2/Z2RkiCVLlrRqU15eLmw2mxg+fPgp2zWzZcsWAYgXXnjhlLK98MILAhBbtmxp9/ySJUtERkbGKfs4E7jzzjtFenq60DSt1fHzzz9fjBkzRowYMUIsX7681Tmv1yscDoe48sorBSBef/31HpXpb3/7m7BYLMLj8QghhLjxxhuFzWYTCxYsEJdddlmb9sOGDWuR5Y9//GOPynImytdRtm7dKgCxevXq07YtKCgQgFi7dm2H+//lL38pEhMTxZtvvikURREFBQVdF/YrNDY29lhf3WHDhg0CEE8//XSr43//+98FIP773/+e8vqB/A6USCRnPgP5HSPXWZF11ol4vV6RkZEh7Ha7CIVCfSmmuOOOO0RGRkYbOSWS3kRa0AcgDz/8MI2Njfztb38jJSWlzfmcnJw2O7tfJTk5mVGjRlFQUNBbYnaY//znP0yZMgW73Y7D4WDcuHE88cQTLedra2u55557GDduHFFRUTgcDhYtWsTOnTvb9FVUVMSll16KzWYjMTGRu+66i48++qjdOODNmzdz8cUXEx0djdVq5fzzz2fDhg0dknnFihVccMEFKIrS7vnrrruOV199tZW71bvvvovH4+Hqq69u0769GPRmV7jPP/+cadOmYTabyc7O5qWXXmr3nu+//z7z5s3DYrG0On799dfz4YcfUl9f33Jsy5YtHD58mOuvv75NPx0d7xtvvBGz2cz+/ftbHV+4cCGxsbGUlZX1inwA9fX1LF++nLS0NEwmEzk5OTz00ENt3NseeeQRZs6cSXx8PBaLhSlTprQbWqAoCnfccQcrVqxg7NixmEwmxowZw8qVK9u0nTJlCnFxcbz99tvtytZdXnnlFa666iqWLl1KdHQ0r7zySrvtNm/ezOLFi4mNjcVmszF+/PhW35ubbrqJqKgo8vLyWLx4MXa7nW9+85sAuN1u7r777pbxGzFiBI888kgbr4CPP/6Y8847j5iYGKKiohgxYgT/+7//26rNU089xZgxY7BarcTGxnLOOeecVOZmnE4nAElJSa2ON7/PvvoZ+SoD5R1YUVHBzTffzJAhQzCZTKSkpLBs2TKZS0IiOcMZKO+YnuJsXGediNlsZurUqbhcLo4dO9ZyfNeuXdx0000tIQrJycnccsst1NTUtGqjKArvvPNOy7Ft27ahKAqTJ09udZ9FixYxffr0VscuuugiioqKyM3N7dBzSSQ9gVTQByDvvvsu2dnZzJw5s8t9BINBiouLiY+P70HJOs/HH3/MddddR2xsLA899BD/93//x9y5c1u9wPPz81mxYgVLly7lscce495772X37t2cf/75rZRAt9vNBRdcwCeffMKdd97Jz3/+czZu3MhPfvKTNvdds2YNc+bMwel08qtf/Yrf//731NfXc8EFF7Trmn4ipaWlHD16tM2L+0Suv/56ysvLW01Wr7zyCvPnzycxMbHD43PkyBGuuuoqLrroIh599FFiY2O56aab2Lt3b6t2wWCQTz75hMWLF7fp44orrkBRFP773/+2kmXkyJHtPkNHx/uJJ54gISGBG2+8kXA4DMBzzz3HqlWreOqpp0hNTe0V+TweD+effz7/+te/uOGGG3jyySeZNWsWP/vZz/jxj3/cqu0TTzzBpEmTeOCBB/j973+PXq/nG9/4Bu+//36bfj///HNuv/12rr32Wh5++GF8Ph9XXnllq4m8mcmTJ3d4kdEZNm/ezJEjR7juuuswGo1cccUV7bq5f/zxx8yZM4d9+/bxox/9iEcffZR58+bx3nvvtWoXCoVYuHAhiYmJPPLII1x55ZUIIbj00kv505/+xMUXX8xjjz3GiBEjuPfee1uN3969e1m6dCl+v58HHniARx99lEsvvbTVc//1r3/lzjvvZPTo0Tz++OP85je/YeLEiWzevPmUz3nOOedgs9m4//77WbNmDaWlpXz22Wfcd999TJ06lQsvvPCU1w+Ud+CVV17JW2+9xc0338wzzzzDnXfeicvl6lQcvUQiGXgMlHdMT3C2rrO+SnNS1piYmFbPnp+fz80338xTTz3Ftddey3/+8x8WL17csiE9duxYYmJiWLduXct169evR1VVdu7c2bKhrGkaGzduZM6cOa3uO2XKFIBeWRNIJCelny34kq/Q0NAgALFs2bIOX5ORkSEWLFggqqqqRFVVldi5c6e49tprBSB++MMftmrX165XP/rRj4TD4TilS5LP5xPhcLjVsYKCAmEymcQDDzzQcuzRRx8VgFixYkXLMa/XK0aOHNnKzVjTNDFs2DCxcOHCVi5JHo9HZGVliYsuuuiUz/jJJ58IQLz77rttzp3oenXOOeeIb3/720IIIerq6oTRaBT/+Mc/xNq1a9u4uDeP24nuzBkZGQIQ69atazl27NgxYTKZxN13393qvqtXr25zfbMLuRBCXHXVVWL+/PlCCCHC4bBITk4Wv/nNb1pcsE90Ie/oeAshxEcffSQA8bvf/a7FJbA9d/WelO+3v/2tsNls4tChQ63u8dOf/lTodDpx9OjRlmPN7vTNBAIBMXbsWHHBBRe0Og4Io9Eojhw50nJs586dAhBPPfVUm+f57ne/KywWS5vjX6WzLu533HGHSEtLa/lcrlq1SgBix44dLW1CoZDIysoSGRkZoq6urtX1J36eb7zxRgGIn/70p63arFixouVvdiJXXXWVUBSlZQz+9Kc/CUBUVVWdVN5ly5a1cTXsKO+9955ISUkRQMvPwoULhcvlOuV1A+UdWFdXN+DCLyQSSfcZKO+YkyHXWeeLkSNHtoz1gQMHxL333iuANmP71TWAEEL8+9//brO2WrJkiZg2bVrL71dccYW44oorhE6nEx9++KEQQojt27cLQLz99ttt+jQajeL73//+KZ9JIulJpAV9gNG8k2e32zt13apVq0hISCAhIYEJEybw+uuv861vfYuHHnqoN8TsMDExMbjdbj7++OOTtjGZTKhq5KMYDoepqalpcbfdvn17S7uVK1cyePBgLr300pZjZrOZW2+9tVV/ubm5Le7TNTU1VFdXU11djdvtZv78+axbt+6UmUCbLaqxsbGnfLbrr7+e//73vwQCAd544w10Oh2XX375Ka/5KqNHj2b27NktvyckJDBixAjy8/Nbtfvggw8YPXr0STO3Xn/99Xz66adUVFSwZs0aKioqTuo+3tHxBliwYAG33XYbDzzwAFdccQVms7ndDOc9Kd/rr7/O7NmziY2NbfnbVVdXc+GFFxIOh1vtgp/oKl1XV0dDQwOzZ89u8xwAF154IUOHDm35ffz48TgcjjZjDZG/vdfrxePxtCtjVwiFQrz66qtcc801LS59F1xwAYmJia2s6Dt27KCgoIDly5e3shQA7boCfv/732/1+wcffIBOp+POO+9sdfzuu+9GCMGHH34I0NL322+/fdLvQ0xMDCUlJWzZsqVTzwqRz/KkSZN48MEHWbFiBb/+9a9Zv349N9988ymvGyjvQIvFgtFo5NNPP6Wurq5LfUgkkoHHQHnH9BRn4zrrwIEDLWM9cuRI/vjHP3LppZe2yYB/4hrA5/NRXV3NueeeC9DquZrXBW63G4h41C1evJiJEyeyfv16IGJVVxSF8847r408zesRiaSv0Pe3AJLWOBwOgA5lOT6R6dOn87vf/Q5FUbBarYwaNarN4r4jdCQWqDN93H777bz22mssWrSIwYMHs2DBAq6++mouvvjiljaapvHEE0/wzDPPUFBQ0OJODbRyHSsqKmLo0KFtZMzJyWn1++HDh4FIDPXJaGhoOK0CLk6Txfvaa6/lnnvu4cMPP+Tll19m6dKlnZ7w09PT2xyLjY1toxC8//77XHLJJSftpzkG+dVXXyU3N5epU6eSk5PTbqxsR8e7mUceeYS3336b3NxcXnnllXZd+HtSvsOHD7Nr1y4SEhLa7evE+LP33nuP3/3ud+Tm5raqXd7e57ijYw3H//Y98X1oZtWqVVRVVTFt2jSOHDnScnzevHn8+9//5qGHHkJVVfLy8oCIW97p0Ov1DBkypNWxoqIiUlNT23wWR40a1XIe4JprruH555/nO9/5Dj/96U+ZP38+V1xxBVdddVXLQu4nP/kJn3zyCdOmTSMnJ4cFCxZw/fXXM2vWrFPKlZ+fz7x583jppZe48sorgUg23MzMTG666SY+/PBDFi1a1O61A+UdaDKZeOihh7j77rtJSkri3HPPZenSpdxwww0kJyd3ul+JRDIwGCjvmO5wtq+zMjMz+etf/4qmaeTl5fHggw9SVVXVpvJLbW0tv/nNb/jPf/7Tam3QfP9mZs+eTSgUYtOmTaSlpXHs2DFmz57N3r17Wynoo0ePJi4url05e3I9IJGcDqmgDzAcDgepqans2bOnU9cNGjTotHGdZrO53XrUQIul8HRlr5rPn6qfE/tITEwkNzeXjz76iA8//JAPP/yQF154gRtuuIF//OMfAPz+97/n/vvv55ZbbuG3v/0tcXFxqKrK8uXLu1TzsvmaP/7xj0ycOLHdNlFRUSe9vnmyOp3VLCUlhblz5/Loo4+yYcMG3nzzzU7LqtPp2j1+4qRVUFDAgQMHTlkz22QyccUVV/CPf/yD/Px8fv3rX5+0bWfHe8eOHS0T3+7du7nuuutane9p+ZpLdN13333tnh8+fDgQmUwvvfRS5syZwzPPPENKSgoGg4EXXnih3SRmHRnrZurq6rBaradNZtYZmq3k7SURBPjss8+YN29ep/o80SrSWSwWC+vWrWPt2rW8//77rFy5kldffZULLriAVatWodPpGDVqFAcPHuS9995j5cqVvPnmmzzzzDP88pe/5De/+c1J+37xxRfx+Xxt6gE3W2U2bNhwSgV9oLwDly9fziWXXMKKFSv46KOPuP/++/nDH/7AmjVrmDRpUqfkk0gkA4OB9I45WR/w9V5n2Wy2VmM9a9YsJk+ezP/+7//y5JNPthy/+uqr2bhxI/feey8TJ04kKioKTdO4+OKLWz3XOeecg9lsZt26daSnp5OYmMjw4cOZPXs2zzzzDH6/n/Xr15/UC7K+vp5Bgwad9Hkkkp5GKugDkKVLl/L//t//Y9OmTcyYMaPH+s3IyGDfvn3tnjt48GBLm9P10dz+RNfsZg4dOtTG8mc0Grnkkku45JJL0DSN22+/neeee47777+fnJwc3njjDebNm8ff/va3Vtd99YXYLP9XdzJPtEYCLW7MDofjtJNpe4wcORKgQ5lZr7/+er7zne8QExPTboK0nuD9998nOjq6Xberr8ry97//HVVVufbaa0/arqPjDZGEMTfffDOjR49m5syZPPzww1x++eVMnTq11+QbOnQojY2Np/3bvfnmm5jNZj766CNMJlPL8RdeeOGU13WEgoKCFotzT+B2u3n77be55ppruOqqq9qcv/POO3n55ZeZN29ey+d3z549Xfr8ZmRk8Mknn+ByuVpZ0Q8cONByvhlVVZk/fz7z58/nscce4/e//z0///nPWbt2bcu9bTYb11xzDddccw2BQIArrriCBx98kJ/97GcnXWhWVlYihGhlpYFIUiWIuPufioH0Dhw6dCh33303d999N4cPH2bixIk8+uij/Otf/+oxuSQSSd8ykN4x7fXR3F6usyKMHz+e//mf/+G5557jnnvuIT09nbq6OlavXs1vfvMbfvnLX7a0bbbun4jRaGTatGmsX7+e9PT0lnGdPXs2fr+fl19+mcrKyjYJ4iCS0C4QCPTomkAiOR0yBn0Act9992Gz2fjOd75DZWVlm/N5eXmtymd0lMWLF1NSUsKKFStaHff7/Tz//PMkJiaeNqPmlClTSExM5Pnnn2/lUgyRkhmlpaWtLGNfzZCtqirjx49vuS9ELJtftWK+/vrrlJaWtjq2cOFCSktLW5XK8Pl8/PWvf20j49ChQ3nkkUdobGxs8wxVVVWnfMbBgweTlpbG1q1bT9kO4KqrruJXv/oVzzzzDEaj8bTtu8IHH3zAggUL0OtPvZ82b948fvvb3/L000+f0gW3o+MNERfno0eP8o9//IPHHnuMzMxMbrzxxlZ/+56W7+qrr2bTpk189NFHbc7V19e3KHc6nQ5FUVopgYWFhW0+311h+/bt3cru+1Xeeust3G43P/jBD7jqqqva/CxdupQ333wTv9/P5MmTycrK4vHHH29Vmg5OH3YBke95OBzm6aefbnX8T3/6E4qitHw/a2tr21zbbAlp/vt+9ftrNBoZPXo0QogWZbs9hg8fjhCC1157rdXxf//73wCntT4PhHegx+PB5/O1ajd06FDsdnubd59EIjmzGAjvmJMh11ntc9999xEMBnnsscdangnazouPP/54u9fPnj2bzZs3s3bt2hYFfdCgQYwaNaolj0B7GyLbtm0D6NE1gURyOqQFfQAydOhQXnnlFa655hpGjRrFDTfcwNixYwkEAmzcuJHXX3+dm266qdP9fve73+Xvf/873/jGN7jllluYNGkSNTU1vPrqq+zZs4eXXnrptEqm0WjkkUce4cYbb2Tq1Klcc801xMfHs2PHDv7+978zfvx4vvvd77a0/853vkNtbS0XXHABQ4YMoaioiKeeeoqJEye27EYuXbqUBx54gJtvvpmZM2eye/duXn75ZbKzs1vd+7bbbuPpp5/muuuu40c/+hEpKSm8/PLLLVa85t1eVVV5/vnnWbRoEWPGjOHmm29m8ODBlJaWsnbtWhwOB+++++4pn3PZsmW89dZbp407io6OPqW7dnfxer2sXbuWZ5999rRtVVXlF7/4xWnbdXS816xZwzPPPMOvfvWrlgXFCy+8wNy5c7n//vt5+OGHe0W+e++9l3feeYelS5dy0003MWXKFNxuN7t37+aNN96gsLCQQYMGsWTJEh57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", + "text/html": [ + "\n", + "
\n", + "
\n", + " Figure\n", + "
\n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%%display_graph_for_all" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4336cf63-51b4-479b-9b5a-e5527788ffde", + "metadata": { + "ExecuteTime": { + "end_time": "2024-08-14T08:48:12.747027Z", + "start_time": "2024-08-14T08:48:12.735846Z" + } + }, + "outputs": [], + "source": [ + "%%display_code_history" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "63be1a14-4659-47d7-9358-c2d435d46bee", + "metadata": { + "ExecuteTime": { + "end_time": "2024-08-14T08:45:20.906658Z", + "start_time": "2024-08-14T08:45:20.904172Z" + } + }, + "outputs": [], + "source": [ + "%%display_code_for_index 0" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8dde35f2-a3ec-4278-9188-d7be10cf0cc0", + "metadata": {}, + "outputs": [], + "source": [ + "%%display_code_for_index 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d20c7377-6c42-410d-b8f7-6bd83192dc05", + "metadata": {}, + "outputs": [], + "source": [ + "%%display_graph_for_index 0" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3b5516b-be9c-49e0-8394-3c9fff3943de", + "metadata": { + "ExecuteTime": { + "end_time": "2024-08-14T08:46:50.316954Z", + "start_time": "2024-08-14T08:46:50.041909Z" + } + }, + "outputs": [], + "source": [ + "%%display_graph_for_last" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3ddda407-4989-493d-90ba-dbb89d0841f8", + "metadata": {}, + "outputs": [], + "source": [ + "%%perfdata_to_variable myvar" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10fbf6a8-760b-4d72-b520-03b0346930b8", + "metadata": {}, + "outputs": [], + "source": [ + "myvar" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d1c55854-ce4c-4f5d-869a-9f920be6645b", + "metadata": {}, + "outputs": [], + "source": [ + "%%perfdata_to_json myfile" + ] + }, + { + "cell_type": "markdown", + "id": "ed4972cf-90c8-4ad8-81d8-b79dccad600b", + "metadata": {}, + "source": [ + "---\n", + "**Plans:**\n", + "- retrieve metrics on multiple nodes (add network, psutil delivers that)\n", + "- parallel serialization (for scorep)\n", + "- show index as thumbnail or so when hovering the graphs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29194d3f-ff1f-48a2-9a78-af8192b9dae8", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "JUmPER", + "language": "python", + "name": "jumper" + }, + "language_info": { + "file_extension": ".py", + "mimetype": "text/plain", + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/ExampleNotebook.ipynb b/examples/ExampleNotebook.ipynb deleted file mode 100644 index d068b6b..0000000 --- a/examples/ExampleNotebook.ipynb +++ /dev/null @@ -1,506 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## The Score-P Python Kernel\n", - "This is the Score-P Python Kernel that allows you to execute Jupyter Notebooks with Score-P for performance analysis. It supports the usual Jupyter interactivity between cells though with some limitations (see **General Limitations**).\n", - "\n", - "The kernel requires [Score-P](https://www.vi-hps.org/projects/score-p/) and [Score-P Python bindings](https://github.com/score-p/scorep_binding_python) to be installed." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Setup\n", - "You can set up your Score-P environment by executing a cell that starts with the `%%scorep_env magic command`.\n", - "\n", - "You can set the Score-P Python binding arguments by executing a cell that starts with `%%scorep_python_binding_arguments`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "any text" - } - }, - "outputs": [], - "source": [ - "%%scorep_env\n", - "SCOREP_ENABLE_TRACING=1\n", - "SCOREP_ENABLE_PROFILING=0\n", - "SCOREP_TOTAL_MEMORY=3g" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "any text" - } - }, - "outputs": [], - "source": [ - "%%scorep_python_binding_arguments\n", - "--noinstrumenter" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Cells instrumentation\n", - "\n", - "Cells that should be executed with Score-P have to be marked with `%%execute_with_scorep` in the first line. Cells without that command are executed as ordinary Python processes." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "any text" - } - }, - "outputs": [], - "source": [ - "%%execute_with_scorep\n", - "import scorep\n", - "class A:\n", - " desc = \"This class and method should be...\"\n", - " def print_desc(self, x):\n", - " print(self.desc + str(x))\n", - "\n", - "a = A()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "any text" - } - }, - "outputs": [], - "source": [ - "a.print_desc(\"known here\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "any text" - } - }, - "outputs": [], - "source": [ - "a.desc = \"new desc\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "any text" - } - }, - "outputs": [], - "source": [ - "print(a.desc)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "any text" - } - }, - "outputs": [], - "source": [ - "%%execute_with_scorep\n", - "import scorep\n", - "with scorep.instrumenter.enable():\n", - " a.desc = \"new desc2\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "any text" - } - }, - "outputs": [], - "source": [ - "print(a.desc)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "any text" - } - }, - "outputs": [], - "source": [ - "%%execute_with_scorep\n", - "import scorep\n", - "import time\n", - "\n", - "def sleep_and_double(x):\n", - " time.sleep(x)\n", - " return 2*x\n", - "\n", - "with scorep.instrumenter.enable():\n", - " x = 5\n", - " x = sleep_and_double(x)\n", - " x = sleep_and_double(x)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "any text" - } - }, - "outputs": [], - "source": [ - "print(x)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Multicell mode\n", - "You can also treat multiple cells as one single cell by using the multicell mode.\n", - "\n", - "For that, you can mark the cells in the order you wish to execute them. Start the marking process by a cell that starts with the `%%enable_multicellmode` command.\n", - "\n", - "Now mark your cells by running them. Note that the cells will not be executed at this point but will be marked for later execution.\n", - "You can stop the marking and execute all the marked cells by running a cell that starts with `%%finalize_multicellmode` command.\n", - "This will execute all the marked cells orderly with Score-P. Note that the `%%execute_with_scorep` command has no effect in the multi cell mode.\n", - "\n", - "There is no \"unmark\" command available but you can abort the multicellmode by the `%%abort_multicellmode` command. Start your marking process again if you have marked your cells in the wrong order.\n", - "\n", - "The `%%enable_multicellmode`, `%%finalize_multicellmode` and `%%abort_multicellmode` commands should be run in an exclusive cell. Additional code in the cell will be ignored." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "any text" - } - }, - "outputs": [], - "source": [ - "%%enable_multicellmode" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "any text" - } - }, - "outputs": [], - "source": [ - "with scorep.instrumenter.enable():\n", - " class B:\n", - " desc = \"This is a class defined in multi cell mode\"\n", - " def print_desc(self, x):\n", - " print(self.desc + str(x))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "any text" - } - }, - "outputs": [], - "source": [ - "import scorep\n", - "with scorep.instrumenter.enable():\n", - " b = B()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "any text" - } - }, - "outputs": [], - "source": [ - "with scorep.instrumenter.enable():\n", - " b.print_desc(\"...and this object is initialized and used in it.\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "any text" - } - }, - "outputs": [], - "source": [ - "b.desc = \"modified desc\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "any text" - } - }, - "outputs": [], - "source": [ - "print(b.desc)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "any text" - } - }, - "outputs": [], - "source": [ - "%%finalize_multicellmode" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Write mode\n", - "\n", - "With write mode you can convert notebook cells into Python script which is then to be executed by Score-P bindings using auxillary bash script. \n", - "\n", - "Similarly to multicell mode, you can run a cell with `%%start_writefile` magic command to enable write mode. Then, running the cells will record their contents instead of executing them. Environment variables and Score-P Python bindings arguments will be written to bash script. Finish the write mode with `%%end_writefile` cell.\n", - "\n", - "You can specify Python script name by providing it as an argument for `%%start_writefile`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "plaintext" - } - }, - "outputs": [], - "source": [ - "%%start_writefile myscript.py" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "plaintext" - } - }, - "outputs": [], - "source": [ - "%%scorep_env\n", - "SCOREP_ENABLE_TRACING=1\n", - "SCOREP_ENABLE_PROFILING=0\n", - "SCOREP_TOTAL_MEMORY=3g" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "plaintext" - } - }, - "outputs": [], - "source": [ - "%%scorep_python_binding_arguments\n", - "--noinstrumenter" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "plaintext" - } - }, - "outputs": [], - "source": [ - "print(\"Cell without instrumentation.\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "plaintext" - } - }, - "outputs": [], - "source": [ - "%%execute_with_scorep\n", - "\n", - "import numpy as np\n", - "import scorep\n", - "\n", - "a = np.array([1, 2, 3])\n", - "b = np.array([4, 5, 6])\n", - "c = a.dot(b)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "plaintext" - } - }, - "outputs": [], - "source": [ - "%%enable_multicellmode\n", - "\n", - "with scorep.instrumenter.enable():\n", - " d = a.outer(b)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "plaintext" - } - }, - "outputs": [], - "source": [ - "with scorep.instrumenter.enable():\n", - " e = b.outer(a)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "plaintext" - } - }, - "outputs": [], - "source": [ - "%%abort_multicellmode" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "plaintext" - } - }, - "outputs": [], - "source": [ - "%%finalize_multicellmode" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "plaintext" - } - }, - "outputs": [], - "source": [ - "%%end_writefile" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can now run `myscript_run.sh` to execute Python script with Score-P bindings." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Presentation of Performance Data\n", - "\n", - "To inspect the collected performance data you can use tools such as [Vampir](https://vampir.eu/) (Trace) or [Cube](https://www.scalasca.org/software/cube-4.x/) (Profile).\n", - "\n", - "### Future Work\n", - "\n", - "The kernel is still under development. If you have any questions or wishes, please report to elias.werner@tu-dresden.de\n", - " \n", - "PRs are welcome.\n", - "\n", - "### General Limitations \n", - "\n", - "For the execution of a cell, the kernel starts a new Python process either with Score-P or standalone. The kernel handles persistency between these processes on its own. Therefore it uses pickle/shelve and additional techniques. However this comes with the following drawbacks:\n", - "\n", - "- when dealing with big data structures, there might be a big runtime overhead at the beginning and the end of a cell. This is due to additional data saving and loading processes for persistency in the background. However this does not affect the actual user code and the Score-P measurements.\n", - "- Pickle/Shelve can not handle each kind ob Python object (e.g. file handles, network connections,...). Thus, they can not be shared between cells and your notebook might not work.\n", - "- Pickle/Shelve does not store class information but gives a reference to the class when storing a class instance. Thus, overwriting classes differs from the ordinary Python way. E.g. if you define a class and an object of this class in one cell and overwrite the class in a different cell, the defined object will also be changed. So please avoid class overwriting." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "scorep-python", - "language": "python", - "name": "scorep-python" - }, - "language_info": { - "file_extension": ".py", - "mimetype": "text/plain", - "name": "Any text" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/examples/demonstrator.ipynb b/examples/demonstrator.ipynb deleted file mode 100644 index dc8e010..0000000 --- a/examples/demonstrator.ipynb +++ /dev/null @@ -1,340 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# This is a demonstrator of the Score-P Python Kernel\n", - "This is the Score-P Python Kernel that allows you to execute Jupyter Notebooks with Score-P.\n", - "\n", - "The kernel supports the usual jupyter interactivity between cells but with some limitations (see \"General Limitations\").\n", - "\n", - "## Setup\n", - "You can set up your Score-P environment by executing a cell that starts with the %%scorep_env magic command.\n", - "\n", - "You can set the Score-P Python binding arguments by executing a cell that starts with %%scorep_python_binding_arguments.\n", - "\n", - "## Usage\n", - "Cells that should be executed with Score-P have to be marked with %%execute_with_scorep in the first line. Cells without that command are executed as ordinary Python processes.\n", - "\n", - "### Multi Cell Mode\n", - "You can also treat multiple cells as one single cell by using the multi cell mode.\n", - "\n", - "Therefore you can mark the cells in the order you wish to execute them. Start the marking process by a cell that starts with the %%enable_multicellmode command.\n", - "Now mark your cells by running them. Note that the cells will not be executed at this point but will be marked for later execution.\n", - "You can stop the marking and execute all the marked cells by running a cell that starts with %%finalize_multicellmode command.\n", - "This will execute all the marked cells orderly with Score-P. Note that the %%execute_with_scorep command has no effect in the multi cell mode.\n", - "\n", - "There is no \"unmark\" command available but you can abort the multicellmode by the %%abort_multicellmode command. Start your marking process again if you have marked your cells in the wrong order.\n", - "\n", - "The %%enable_multicellmode, %%finalize_multicellmode and %%abort_multicellmode commands should be run in an exclusive cell. Additional code in the cell will be ignored.\n", - "\n", - "### Presentation of Performance Data\n", - "\n", - "To inspect the collected performance data, use tools as Vampir (Trace) or Cube (Profile)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "---\n", - "# Set up SCORE-P Environment" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Score-P environment set successfully: {'SCOREP_ENABLE_TRACING': '1', 'SCOREP_ENABLE_PROFILING': '0', 'SCOREP_TOTAL_MEMORY': '3g'}" - ] - } - ], - "source": [ - "%%scorep_env\n", - "SCOREP_ENABLE_TRACING=1\n", - "SCOREP_ENABLE_PROFILING=0\n", - "SCOREP_TOTAL_MEMORY=3g" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Score-P Python binding arguments set successfully: ['--noinstrumenter', '--noinstrumenter']" - ] - } - ], - "source": [ - "%%scorep_python_binding_arguments\n", - "--noinstrumenter" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "import scorep\n", - "import pandas as pd\n", - "import numpy as np\n", - "from sklearn.preprocessing import StandardScaler\n", - "import sys" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "---\n", - "# Example 1: Data Conjunction" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "nrows, ncols = 1000000, 20\n", - "df1, df2, df3, df4 = [pd.DataFrame(np.random.randn(nrows, ncols)) for _ in range(4)]" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u0000Instrumentation results can be found in /home/visitor/Demonstrators/score-p_kernel/supplementary/example/scorep-20230630_1737_31341656626898" - ] - } - ], - "source": [ - "%%execute_with_scorep\n", - "with scorep.instrumenter.enable():\n", - " # data conjunction\n", - " df5 = df1 + df2 +df3 + df4" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u0000Instrumentation results can be found in /home/visitor/Demonstrators/score-p_kernel/supplementary/example/scorep-20230630_1737_31353813505508" - ] - } - ], - "source": [ - "%%execute_with_scorep\n", - "with scorep.instrumenter.enable():\n", - " # data conjunction\n", - " df5 = pd.eval(\"df1 + df2 +df3 + df4\", engine='numexpr')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "---\n", - "# Example 2: Deep Learning" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "filename = \"fairytales_demo.txt\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# imports\n", - "import scorep\n", - "import logging\n", - "\n", - "logging.basicConfig(\n", - " format=\"%(asctime)s - %(levelname)s - %(name)s - %(message)s\",\n", - " datefmt=\"%d/%m/%Y %H:%M:%S\",\n", - " level=logging.INFO)\n", - "\n", - "from utils import set_seed\n", - "set_seed(42)\n", - "\n", - "import numpy as numpy\n", - "import torch\n", - "import torch.nn as nn\n", - "from torch.nn import functional as F\n", - "\n", - "import math\n", - "from torch.utils.data import Dataset\n", - "\n", - "from model import GPT, GPTconfig\n", - "from trainer import Trainer, TrainerConfig" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# defining the data set\n", - "class CharDataset(Dataset):\n", - " def __init__(self, data, block_size):\n", - " chars = sorted(list(set(data)))\n", - " data_size, vocab_size = len(data), len(chars)\n", - " print(\"data has %d characters, %d unique.\" % (data_size, vocab_size))\n", - "\n", - " self.stoi = {ch:i for i, ch in enumerate(chars)}\n", - " self.itos = {i:ch for i, ch in enumerate(chars)}\n", - " self.block_size = block_size\n", - " self.vocab_size = vocab_size\n", - " self.data = data\n", - "\n", - " def __len__(self):\n", - " return len(self.data) - self.block_size\n", - "\n", - " def __getitem__(self, idx):\n", - " chunk = self.data[idx : idx+self.block_size+1]\n", - " dix = [self.stoi[s] for s in chunk]\n", - "\n", - " x = torch.tensor(dix[:-1], dtype = torch.long)\n", - " y = torch.tensor(dix[1:], dtype = torch.long)\n", - " return x, y" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%execute_with_scorep\n", - "\n", - "with scorep.instrumenter.enable():\n", - " block_size = 32\n", - "\n", - " text = open(\"./{}\".format(filename), \"r\").read()\n", - " train_dataset = CharDataset(text, block_size)\n", - "\n", - " \n", - " mconf = GPTconfig(train_dataset.vocab_size, train_dataset.block_size,\n", - " n_layer=4, n_head=4, n_embd=256)\n", - " model = GPT(mconf)\n", - "\n", - " tconf = TrainerConfig(max_epochs=1, batch_size=1024, learning_rate=0.01,\n", - " lr_decay=True, warmup_tokens=512*20, final_tokens=2*len(train_dataset)*block_size,\n", - " num_workers=1)\n", - " trainer = Trainer(model, train_dataset, None, tconf)\n", - "\n", - " torch.cuda.empty_cache()\n", - " trainer.train()\n", - "\n", - " torch.save(model.state_dict(), \"./saved_models/trained_gpt_model\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "tags": [] - }, - "source": [ - "![image info](trace_profile.png)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Try your model" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "context = \"The sun shone in the sky.\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%execute_with_scorep\n", - "from utils import sample\n", - "\n", - "with scorep.instrumenter.enable():\n", - " x = torch.tensor([train_dataset.stoi[s] for s in context], dtype=torch.long)[None,...].to(trainer.device)\n", - " y = sample(model, x, 2000, temperature=1.0, sample=True, top_k=10)[0]\n", - "\n", - " completion = ''.join([train_dataset.itos[int(i)] for i in y])\n", - " print(completion)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "---\n", - "# Playground\n", - "Feel free to add your code and to analyze it\n", - "\n", - "Begin a cell with:\n", - "%%execute_with_scorep" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "scorep-python", - "language": "python", - "name": "scorep-python" - }, - "language_info": { - "file_extension": ".py", - "mimetype": "text/plain", - "name": "Any text" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/examples/fairytales_demo.txt b/examples/fairytales_demo.txt deleted file mode 100644 index 220f605..0000000 --- a/examples/fairytales_demo.txt +++ /dev/null @@ -1,136 +0,0 @@ -The Three Heads of the Well -LONG before Arthur and the Knights of the Round Table, there reigned in the eastern part of England a king who kept his court at Colchester. -In the midst of all his glory, his queen died, leaving behind her an only daughter, about fifteen years of age who for her beauty and kindness was the wonder of all that knew her. But the king hearing of a lady who had likewise an only daughter, had a mind to marry her for the sake of her riches, though she was old, ugly, hook-nosed, and hump-backed. Her daughter was a yellow dowdy, full of envy and ill-nature; and, in short, was much of the same mould as her mother. But in a few weeks the king, attended by the nobility and gentry, brought his deformed bride to the palace, where the marriage rites were performed. She had not been long in the Court before she set the king against his own beautiful daughter by false reports. The young princess having lost her father's love, grew weary of the Court, and one day, meeting with her father in the garden, she begged him, with tears in her eyes, to let her go and seek her fortune; to which the king consented, and ordered her mother-in-law to give her what she pleased. She went to the queen, who gave her a canvas bag of brown bread and hard cheese, with a bottle of beer. Though this was but a pitiful dowry for a king's daughter, she took it, with thanks, and proceeded on her journey, passing through groves, woods, and valleys, till at length she saw an old man sitting on a stone at the mouth of a cave, who said: "Good morrow, fair maiden, whither away so fast?" -"Aged father," says she, "I am going to seek my fortune." -"What have you got in your bag and bottle?" -"In my bag I have got bread and cheese, and in my bottle good small beer. Would you like to have some?" -"Yes " said he, "with all my heart." -With that the lady pulled out the provisions, and bade him eat and welcome. He did so, and gave her many thanks, and said: "There is a thick thorny hedge before you, which you cannot get through, but take this wand in your hand, strike it three times, and say, 'Pray, hedge, let me come through,' and it will open immediately; then, a little further, you will find a well; sit down on the brink of it, and there will come up three golden heads, which will speak; and whatever they require, that do." Promising she would, she took her leave of him. Coming to the hedge and using the old man's wand, it divided, and let her through; then, coming to the well, she had no sooner sat down than a golden head came up singing: -"Wash me and comb me, And lay me down softly. And lay me on a bank to dry, That I may look pretty, When somebody passes by." -"Yes," said she, and taking it in her lap combed it with a silver comb, and then placed it upon a primrose bank. Then up came a second and a third head, -saying the same as the former. So she did the same for them, and then, pulling out her provisions, sat down to eat her dinner. -Then said the heads one to another: "What shall we weird for this damsel who has used us so kindly?" -The first said: "I weird her to be so beautiful that she shall charm the most powerful prince in the world" -The second said: "I weird her such a sweet voice as shall far exceed the nightingale." -The third said: "My gift shall be none of the least, as she is a king's daughter, I'll weird her so fortunate that she shall become queen to the greatest prince that reigns." -She then let them down into the well again, and so went on her journey. She had not travelled long before she saw a king hunting in the park with his nobles. She would have avoided him, but the king, having caught a sight of her, approached, and what with her beauty and sweet voice, fell desperately in love with her, and soon induced her to marry him. -This king finding that she was the king of Colchester's daughter, ordered some chariots to be got ready, that he might pay the king, his father-in-law, a visit. The chariot in which the king and queen rode was adorned with rich gems of gold. The king, her father, was at first astonished that his daughter had been so fortunate, till the young king let him know of all that had happened. Great was the joy at Court amongst all, with the exception of the queen and her club-footed daughter, who were ready to burst with envy. The rejoicings, with feasting and dancing continued many days. Then at length they returned home with the dowry her father gave her. -The hump-backed princess, perceiving that her sister had been so lucky in seeking her fortune, wanted to do the same; so she told her mother, and all preparations were made, and she was furnished with rich dresses, and with sugar, almonds, and sweetmeats, in great quantities, and a large bottle of Malaga sack. With these she went the same road as her sister; and coming near the cave, the old man said: "Young woman, whither so fast?" -"What's that to you?" said she. -"Then," said he, "what have you in your bag and bottle?" -She answered: "Good things, which you shall not be troubled with." -"Won't you give me some?" said he. -"No, not a bit, nor a drop, unless it would choke you." -The old man frowned, saying: "Evil fortune attend ye!" -Going on, she came to the hedge, through which she espied a gap, and thought to pass through it; but the hedge closed, and the thorns ran into her flesh, so that it was with great difficulty that she got through. Being now all over blood, she searched for water to wash herself, and, looking round she saw the well. She sat down on the brink of it, and one of the heads came up saying: "Wash me, comb me, and lay me down softly," as before, but she banged it with her bottle, saying, "Take that for your washing." So the second and third heads came up, and met with no better treatment than the first. Whereupon the heads consulted among themselves what evils to plague her with for such usage. -The first said "Let her be struck with leprosy in her face." -The second: "Let her voice be as harsh as a corncrake's." -The third said: "Let her have for husband but a poor country cobbler." -Well, on she went till she came to a town, and it being market-day, the people looked at her, and, seeing such an ugly face, and hearing such a squeaky voice, all fled but a poor country cobbler. Now he not long before had mended the shoes of an old hermit, who, having no money, gave him a box of ointment for the cure of the leprosy, and a bottle of spirits for a harsh voice. So the cobbler, having a mind to do an act of charity, was induced to go up to her and ask her who she was. -"I am," said she, "the king of Colchester's daughter-in-law." -"Well," said the cobbler, "if I restore you to your natural complexion, and make a sound cure both in face and voice, will you in reward take me for a husband?" -"Yes, friend," replied she, "with all my heart!" -With this the cobbler applied the remedies, and they made her well in a few weeks; after which they were married, and so set forward for the Court at Colchester. When the queen found that her daughter had married nothing but a poor cobbler she hanged herself in wrath. The death of the queenso pleased the king, who was glad to get rid of her so soon, that he gave the cobbler a hundred pounds, to quit the Court with his lady, and take to a remote part of the kingdom, where he lived many years mending shoes, his wife spinning the thread for him. - - -<|endoftext|> - - -Master of all Masters -A GIRL once went to the fair to hire herself for servant. At last a funny-looking old gentleman engaged her, and took her home to his house. When she got there, he told her that he had -He said to her: "What will you call me?" -"Master or mister, or whatever you please sir," says she. -He said: "You must call me 'master of all masters.' And what would you call this?" pointing to his bed. -"Bed or couch, or whatever you please, sir." -"No, that's my 'barnacle.' And what do you call these?" said he pointing to his pantaloons. -"Breeches or trousers, or whatever you please, sir." -"You must call them 'squibs and crackers.' And what would you call her?" pointing to the cat. -"Cat or kit, or whatever you please, sir." -"You must call her 'white-faced simminy.' And this now," showing the fire, "what would you call this?" -"Fire or flame, or whatever you please, sir." -"You must call it 'hot cockalorum,' and what this?" he went on, pointing to the water. -"Water or wet, or whatever you please, sir." -"No, 'pondalorum' is its name. And what do you call all this?" asked he as he pointed to the house. -"House or cottage, or whatever you please, sir." -"You must call it 'high topper mountain.'" -That very night the servant woke her master up in a fright and said: "Master of all masters, get out of your barnacle and put on your squibs and crackers. For white-faced simminy has got a spark of hot cockalorum on its tail, and unless you get some pondalorum high topper mountain will be all on hot cockalorum" . . -. . . . . . That's all. - - -<|endoftext|> - - -The Well of the World's End -ONCE upon a time, and a very good time it was, though it wasn't in my time, nor in your time, nor any one else's time, there was a girl whose mother had died, and her father married again. And her stepmother hated her because she was more beautiful than herself, and she was very cruel to her. She used to make her do all the servant's work, and never let her have any peace. At last, one day, the stepmother thought to get rid of her altogether; so she handed her a sieve and said to her: "Go, fill it at the Well of the World's End and bring it home to me full, or woe betide you." For she thought she would never be able to find the Well of the World's End, and, if she did, how could she bring home a sieve full of water? -Well, the girl started off, and asked every one she met to tell her where was the Well of the World's End. But nobody knew, and she didn't know what to do, when a queer little old woman, all bent double, told her where it was, and how she could get to it. So she did what the old woman told her, and at last arrived at the Well of the World's End. But when she dipped the sieve in the cold, cold water, it all ran out again. She tried and she tried again, but every time it was the same; and at last she sate down and cried as if her heart would break. -Suddenly she heard a croaking voice, and she looked up and saw a great frog with goggle eyes looking at her and speaking to her. -"What's the matter, dearie?" it said. -"Oh, dear, oh dear," she said, "my stepmother has sent me all this long way to fill this sieve with water from the Well of the World's End, and I can't fill it no how at all." -"Well," said the frog, "if you promise me to do whatever I bid you for a whole night long, I'll tell you how to fill it." -So the girl agreed, and the frog said: -"Stop it with moss and daub it with clay, And then it will carry the water away;" -and then it gave a hop, skip, and jump, and went flop into the Well of the World's End. -So the girl looked about for some moss, and lined the bottom of the sieve with it, and over that she put some clay, and then she dipped it once again into the Well of the World's End; and this time, the water didn't run out, and she turned to go away. -Just then the frog popped up its head out of the Well of the World's End, and said: "Remember your promise." -"All right," said the girl; for thought she, "what harm can a frog do me?" -So she went back to her stepmother, and brought the sieve full of water from the Well of the World's End. The stepmother was angry as angry, but she said nothing at all. -That very evening they heard something tap tapping at the door low down, and a voice cried out: -"Open the door, my hinny, my heart, Open the door, my own darling; Mind you the words that you and I spoke, Down in the meadow, at the World's End Well." -"Whatever can that be?" cried out the stepmother, and the girl had to tell her all about it, and what she had promised the frog. -"Girls must keep their promises," said the stepmother. "Go and open the door this instant." For she was glad the girl would have to obey a nasty frog. -So the girl went and opened the door, and there was the frog from the Well of the World's End. And it hopped, and it hopped, and it jumped, till it reached the girl, and then it said: -"Lift me to your knee, my hinny, my heart; Lift me to your knee, my own darling; Remember the words you and I spoke, Down in the meadow by the World's End Well." -But the girl didn't like to, till her stepmother said: "Lift it up this instant, you hussy! Girls must keep their promises!" -THE WELL OF THE WORLD'S END. -"Give me some supper, my hinny, my heart, Give me some supper, my darling; Remember the words you and I spake, In the meadow, by the Well of the World's End." -Well, she didn't mind doing that, so she got it a bowl of milk and bread, and fed it well. And when the frog had finished, it said: -"Go with me to bed, my hinny, my heart, Go with me to bed, my own darling; Mind you the words you spake to me, Down by the cold well, so weary." -But that the girl wouldn't do, till her stepmother said: "Do what you promised, girl; girls must keep their promises. Do what you're bid, or out you go, you and your froggie." -So the girl took the frog with her to bed, and kept it as far away from her as she could. Well, just as the day was beginning to break what should the frog say but: -"Chop off my head, my hinny, my heart, Chop off my head, my own darling; Remember the promise you made to me, Down by the cold well so weary." -At first the girl wouldn't, for she thought of what the frog had done for her at the Well of the World's End. But when the frog said the words over again she went and took an axe and chopped off its head and lo! and behold, there stood before her a handsome young prince, who told her that he had been enchanted by a wicked magician, and he could never be unspelled till some girl would do his bidding for a whole night, and chop off his head at the end of it. -The stepmother was surprised indeed when she found the young prince instead of the nasty frog, and she wasn't best pleased, you may be sure, when the prince told her that he was going to marry her stepdaughter because she had unspelled him. But married they were, and went away to live in the castle of the king, his father, and all the stepmother had to console her was, that it was all through her that her stepdaughter was married to a prince. - - -<|endoftext|> - - -Fairy Ointment -DAME GOODY was a nurse that looked after sick people, and minded babies. One night she was woke up at midnight, and when she went downstairs, she saw a strange squinny-eyed, little ugly old fellow, who asked her to come to his wife who was too ill to mind her baby. Dame Goody didn't like the look of the old fellow, but business is business; so she popped on her things, and went down to him. And when she got down to him, he whisked her up on to a large coal-black horse with fiery eyes, that stood at the door; and soon they were going at a rare pace, Dame Goody holding on to the old fellow like grim death. -They rode, and they rode, till at last they stopped before a cottage door. So they got down and went in and found the good woman abed with the children playing about; and the babe, a fine bouncing boy, beside her. -Dame Goody took the babe, which was as fine a baby boy as you'd wish to see. The mother, when she handed the baby to Dame Goody to mind, gave her a box of ointment, and told her to stroke the baby's eyes with it as soon as it opened them. After a while it began to open its eyes. Dame Goody saw that it had squinny eyes just like its father. So she took the box of ointment and stroked its two eyelids with it But she couldn't help wondering what it was for, as she had never seen such a thing done before. So she looked to see if the others were looking, and, when they were not noticing, she stroked her own right eyelid with the ointment. -No sooner had she done so, than everything seemed changed about her. The cottage became elegantly furnished. The mother in the bed was a beautiful lady, dressed up in white silk. The little baby was still more beautiful than before, and its clothes were made of a sort of silvery gauze. Its little brothers and sisters around the bed were flat-nosed imps with pointed ears, who made faces at one another, and scratched their polls. Sometimes they would pull the sick lady's ears with their long and hairy paws. In fact, they were up to all kinds of mischief; and Dame Goody knew that she had got into a house of pixies. But she said nothing to nobody, and as soon as the lady was well enough to mind the baby, she asked the old fellow to take her back home. So he came round to the door with the coal-black horse with eyes of fire, and off they went as fast as before, or perhaps a little faster, till they came to Dame Goody's cottage, where the squinny-eyed old fellow lifted her down and left her, thanking her civilly enough, and paying her more than she had ever been paid before for such service. -Now next day happened to be market-day, and as Dame Goody had been away from home, she wanted many things in the house, and trudged off to get them at the market. As she was buying the things she wanted, who should she see but the squinny-eyed old fellow who had taken her on the coal-black horse. And what do you think he was doing? Why he went about from stall to stall taking up things from each, here some fruit, and there some eggs, and so on; and no one seemed to take any notice. -Now Dame Goody did not think it her business to interfere, but she thought she ought not to let so good a customer pass without speaking. So she ups to him and bobs a curtsey and said: "Gooden, sir, I hopes as how your good lady and the little one are as well as—" -But she couldn't finish what she was a-saying, for the funny old fellow started back in surprise, and he says to her, says he: "What! do you see me to-day?" -"See you," says she, "why, of course I do, as plain as the sun in the skies, and what's more," says she, "I see you are busy too, into the bargain.""Ah, you see too much," said he; "now, pray, with which eye do you see all this?""With the right eye to be sure," said she, as proud as can be to find him out. -"The ointment! The ointment!" cried the old pixy thief. "Take that for meddlng with what don't concern you: you shall see me no more." And with that he struck her on her right eye, and she couldn't see him any more; and, what was worse, she was blind on the right side from that hour till the day of her death. - - -<|endoftext|> - - -The Ass, the Table, and the Stick -A LAD named Jack was once so unhappy at home through his father's ill-treatment, that he made up his mind to run away and seek his fortune in the wide world. -He ran, and he ran, until he could run no longer, and then he ran right up against a little old woman who was gathering sticks. He was too much out of breath to beg pardon, but the woman was good-natured, and she said he seemed to be a likely lad, so she would take him to be her servant, and would pay him well. He agreed, for he was very hungry, and she brought him to her house in the wood, where he served her for twelvemonths and a day. When the year had passed, she called him to her, and said she had good wages for him. So she presented him with an ass out of the stable, and he had but to pull Neddy's ears to make him begin at once to ee—aw! And when he brayed there dropped from his mouth silver sixpences, and halfcrowns, and golden guineas. -The lad was well pleased with the wage he had received, and away he rode till he reached an inn. There he ordered the best of everything, and when the innkeeper refused to serve him without being paid beforehand, the boy went off to the stable, pulled the ass's ears and obtained his pocket full of money. The host had watched all this through a crack in the door, and when night came on he put an ass of his own for the precious Neddy of the poor youth. So Jack, without knowing that any change had been made, rode away next morning to his father's house. -Now, I must tell you that near his home dwelt a poor widow with an only daughter. The lad and the maiden were fast friends and true-loves; but when Jack asked his father's leave to marry the girl, "Never till you have the money to keep her," was the reply. "I have that, father," said the lad, and going to the ass he pulled its long ears; well, he pulled, and he pulled, till one of them came off in his hands; but Neddy, though he hee-hawed and he hee-hawed let fall no halfcrowns or guineas. The father picked up a hayfork and beat his son out of the house. I promise you he ran. Ah! he ran and ran till he came bang against a door, and burst it open, and there he was in a joiner's shop. "You're a likely lad," said the joiner; "serve me for a twelvemonths and a day and I will pay you well." So he agreed, and served the carpenter for a year and a day. "Now," said the master, "I will give you your wage;" and he presented him with a table, telling him he had but to say, "Table, be covered," and at once it would be spread with lots to eat and drink. -Jack hitched the table on his back, and away he went with it till he came to the inn. "Well, host," shouted he, "my dinner to-day, and that of the best." -"Very sorry, but there is nothing in the house but ham and eggs." -"Ham and eggs for me!" exclaimed Jack. "I can do better than that. Come, my table, be covered!" -At once the table was spread with turkey and sausages, roast mutton, potatoes, and greens. The innkeeper opened his eyes, but he said nothing, not he. -That night he fetched down from his attic a table very like that of Jack's, and exchanged the two. Jack, none the wiser, next morning hitched the worthless table on to his back and carried it home." Now, father, may I marry my lass?" he asked. -"Not unless you can keep her," replied the father. -"Look here!" exclaimed Jack. "Father, I have a table which does all my bidding." -"Let me see it," said the old man. -The lad set it in the middle of the room, and bade it be covered; but all in vain, the table remained bare. In a rage, the father caught the warming-pan down from the wall and warmed his son's back with it so that the boy fled howling from the house, and ran and ran till he came to a river and tumbled in. A man picked him out and bade him help him in making a bridge over the river; and how do you think he was doing it. Why, by casting a tree across; so Jack climbed up to the top of the tree and threw his weight on it, so that when the man had rooted the tree up, Jack and the tree-head dropped on the farther bank. -"Thank you," said the man; "and now for what you have done I will pay you;" so saying, he tore a branch from the tree, and fettled it up into a club with his knife. "There," exclaimed he; "take this stick, and when you say to it, 'Up stick and bang him,' it will knock any one down who angers you." -The lad was overjoyed to get this stick—so away he went with it to the inn, and as soon as the innkeeper, appeared, "Up stick and bang him!" was his cry. At the word the cudgel flew from his hand and battered the old fellow on the back, rapped his head, bruised his arms, tickled his ribs, till he fell groaning on the floor; still the stick belaboured the prostrate man, nor would Jack call it off till he had got back the stolen ass and table. Then he galloped home on the ass, with the table on his shoulders, and the stick in his hand. When he arrived there he found his father was dead, so he brought his ass into the stable and pulled its ears till he had filled the manger with money. -It was soon known through the town that Jack had returned rolling in wealth, and accordingly all the girls in the place set their caps at him. "Now," said Jack, "I shall marry the richest lass in the place; so to-morrow do you all come in front of my house with your money in your aprons." -Next morning the street was full of girls with aprons held out, and gold and silver in them; but Jack's own sweetheart was among them, and she had neither gold nor silver, nought but two copper pennies that was all she had. -"Stand aside, lass," said Jack to her, speaking roughly. "Thou hast no silver nor gold—stand off from the rest." She obeyed, and the tears ran down her cheeks, and filled her apron with diamonds. -"Up stick and bang them!" exclaimed Jack; whereupon the cudgel leaped up, and running along the line of girls, knocked them all on the heads and left them senseless on the pavement. Jack took all their money and poured it into his true-love's lap. "Now, lass," he exclaimed, "thou art the richest, and I shall marry thee." - - -<|endoftext|> - diff --git a/examples/gpt-demo/01-GPT-Training.ipynb b/examples/gpt-demo/01-GPT-Training.ipynb index 625b5a4..597be33 100644 --- a/examples/gpt-demo/01-GPT-Training.ipynb +++ b/examples/gpt-demo/01-GPT-Training.ipynb @@ -17,7 +17,11 @@ { "cell_type": "code", "execution_count": 1, - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "any text" + } + }, "outputs": [ { "name": "stdout", @@ -28,16 +32,19 @@ } ], "source": [ - "%%scorep_env\n", - "SCOREP_ENABLE_TRACING=1\n", - "SCOREP_ENABLE_PROFILING=0\n", - "SCOREP_TOTAL_MEMORY=3g" + "%env SCOREP_ENABLE_TRACING=1\n", + "%env SCOREP_ENABLE_PROFILING=0\n", + "%env SCOREP_TOTAL_MEMORY=3g" ] }, { "cell_type": "code", "execution_count": 2, - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "any text" + } + }, "outputs": [ { "name": "stdout", @@ -55,7 +62,11 @@ { "cell_type": "code", "execution_count": 3, - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "any text" + } + }, "outputs": [], "source": [ "filename = \"fairytales.txt\"" @@ -64,7 +75,11 @@ { "cell_type": "code", "execution_count": 4, - "metadata": {}, + "metadata": { + "vscode": { + "languageId": "any text" + } + }, "outputs": [ { "name": "stdout", @@ -169,14 +184,14 @@ "lastKernelId": null }, "kernelspec": { - "display_name": "scorep-python3", - "language": "python3", - "name": "scorep-python3" + "display_name": "JUmPER", + "language": "python", + "name": "jumper" }, "language_info": { "file_extension": ".py", "mimetype": "text/plain", - "name": "Any text" + "name": "python" } }, "nbformat": 4, diff --git a/src/jumper/kernel.py b/src/jumper/kernel.py index 3f0b2bd..46b72d9 100644 --- a/src/jumper/kernel.py +++ b/src/jumper/kernel.py @@ -5,6 +5,7 @@ import subprocess import sys import time +import shutil from enum import Enum from textwrap import dedent @@ -73,7 +74,6 @@ def __init__(self, **kwargs): self.blacklist_prefixes = ["%lsmagic"] self.scorep_binding_args = [] - self.scorep_env = {} os.environ["SCOREP_KERNEL_PERSISTENCE_DIR"] = "./" self.pershelper = PersHelper("dill", "memory") @@ -96,6 +96,8 @@ def __init__(self, **kwargs): self.perfdata_handler = PerformanceDataHandler() self.nodelist = self.perfdata_handler.get_nodelist() + self.scorep_available_ = shutil.which("scorep") + def cell_output(self, string, stream="stdout"): """ Display string as cell output. @@ -111,6 +113,13 @@ def standard_reply(self): "payload": [], "user_expressions": {}, } + + def scorep_not_available(self): + if not self.scorep_available_: + self.cell_output("Score-P not available, cell ignored.", "stderr") + return self.standard_reply() + else: + return None def marshaller_settings(self, code): """ @@ -121,9 +130,9 @@ def marshaller_settings(self, code): self.pershelper.postprocess() marshaller_match = re.search( - r"MARSHALLER=(\w+)", code.split("\n", 1)[1] + r"MARSHALLER=([\w-]+)", code.split("\n", 1)[1] ) - mode_match = re.search(r"MODE=(\w+)", code.split("\n", 1)[1]) + mode_match = re.search(r"MODE=([\w-]+)", code.split("\n", 1)[1]) marshaller = ( marshaller_match.group(1) if marshaller_match else None ) @@ -132,11 +141,10 @@ def marshaller_settings(self, code): if marshaller: if not self.pershelper.set_marshaller(marshaller): self.cell_output( - f"Marshaller '{marshaller}' is not recognized, " + f"Marshaller '{marshaller}' is not available or compatible, " f"kernel will use '{self.pershelper.marshaller}'.", "stderr", ) - return self.standard_reply() if mode: if not self.pershelper.set_mode(mode): self.cell_output( @@ -193,40 +201,18 @@ def set_perfmonitor(self, code): ) return self.standard_reply() - def set_scorep_env(self, code): - """ - Read and record Score-P environment variables from the cell. - """ - if self.mode == KernelMode.DEFAULT: - for scorep_param in code.split("\n")[1:]: - if not scorep_param == "": - key, val = scorep_param.split("=") - self.scorep_env[key] = val - self.cell_output( - "Score-P environment set successfully: " + str(self.scorep_env) - ) - elif self.mode == KernelMode.WRITEFILE: - self.writefile_scorep_env += code.split("\n")[1:] - self.cell_output("Environment variables recorded.") - else: - self.cell_output( - f"KernelWarning: Currently in {self.mode}, command ignored.", - "stderr", - ) - return self.standard_reply() - def set_scorep_pythonargs(self, code): """ Read and record Score-P Python binding arguments from the cell. """ if self.mode == KernelMode.DEFAULT: - self.scorep_binding_args += code.split("\n")[1:] + self.scorep_binding_args = code.split("\n")[1].replace(' ', '\n').split("\n") self.cell_output( "Score-P Python binding arguments set successfully: " + str(self.scorep_binding_args) ) elif self.mode == KernelMode.WRITEFILE: - self.writefile_scorep_binding_args += code.split("\n")[1:] + self.writefile_scorep_binding_args = code.split("\n")[1].replace(' ', '\n').split("\n") self.cell_output("Score-P bindings arguments recorded.") else: self.cell_output( @@ -306,6 +292,9 @@ def start_writefile(self, code): # TODO: Edge cases processing, similar to multicellmode if self.mode == KernelMode.DEFAULT: self.mode = KernelMode.WRITEFILE + # init writefile_scorep_env and python binding args + self.writefile_scorep_env = [] + self.writefile_scorep_binding_args = [] writefile_cmd = code.split("\n")[0].split(" ") if len(writefile_cmd) > 1: if writefile_cmd[1].endswith(".py"): @@ -322,30 +311,31 @@ def start_writefile(self, code): os.path.realpath("") + "/" + self.writefile_base_name + ".py" ) - with os.fdopen(os.open(self.writefile_bash_name, os.O_WRONLY | os.O_CREAT), 'w') as bash_script: + with os.fdopen(os.open(self.writefile_bash_name, os.O_WRONLY | os.O_CREAT | os.O_TRUNC), 'w') as bash_script: bash_script.write( dedent( f""" # This bash script is generated automatically to run # Jupyter Notebook -> Python script conversion - # by Jumper kernel + # by JUmPER kernel # {self.writefile_python_name} # !/bin/bash """ ) ) - with os.fdopen(os.open(self.writefile_python_name, os.O_WRONLY | os.O_CREAT), 'w') as python_script: + with os.fdopen(os.open(self.writefile_python_name, os.O_WRONLY | os.O_CREAT | os.O_TRUNC), 'w') as python_script: python_script.write( dedent( f""" # This is the automatic conversion of - # Jupyter Notebook -> Python script by Jumper kernel. + # Jupyter Notebook -> Python script by JUmPER kernel. # Code corresponding to the cells not marked for # Score-P instrumentation is framed by # "with scorep.instrumenter.disable() # The script can be run with proper settings using # bash script {self.writefile_bash_name} import scorep + import os """ ) ) @@ -374,7 +364,9 @@ def append_writefile(self, code, explicit_scorep): Append cell to writefile. """ if self.mode == KernelMode.WRITEFILE: - if explicit_scorep or self.writefile_multicell: + if not code: + pass + elif explicit_scorep or self.writefile_multicell: with os.fdopen(os.open(self.writefile_python_name, os.O_WRONLY | os.O_APPEND), 'a') as python_script: python_script.write(code + "\n") self.cell_output( @@ -391,6 +383,11 @@ def append_writefile(self, code, explicit_scorep): self.cell_output( "Python commands without instrumentation recorded." ) + else: + self.cell_output( + f"KernelWarning: Currently in {self.mode}, command ignored.", + "stderr", + ) return self.standard_reply() def end_writefile(self): @@ -402,7 +399,7 @@ def end_writefile(self): self.mode = KernelMode.DEFAULT with os.fdopen(os.open(self.writefile_bash_name, os.O_WRONLY | os.O_APPEND), 'a') as bash_script: bash_script.write( - f"{' '.join(self.writefile_scorep_env)} " + f"{''.join(self.writefile_scorep_env)}\n" f"{PYTHON_EXECUTABLE} -m scorep " f"{' '.join(self.writefile_scorep_binding_args)} " f"{self.writefile_python_name}" @@ -415,6 +412,31 @@ def end_writefile(self): ) return self.standard_reply() + def abort_writefile(self): + """ + Cancel writefile mode. + """ + if self.mode == KernelMode.WRITEFILE: + self.mode = KernelMode.DEFAULT + + if os.path.exists(self.writefile_bash_name): + os.remove(self.writefile_bash_name) + if os.path.exists(self.writefile_python_name): + os.remove(self.writefile_python_name) + + self.writefile_base_name = "jupyter_to_script" + self.writefile_bash_name = "" + self.writefile_python_name = "" + self.writefile_scorep_binding_args = [] + self.writefile_multicell = False + self.cell_output("Writefile mode aborted.") + else: + self.cell_output( + f"KernelWarning: Currently in {self.mode}, command ignored.", + "stderr", + ) + return self.standard_reply() + def ghost_cell_error(self, reply_status, error_message): self.shell.execution_count += 1 reply_status["execution_count"] = self.shell.execution_count - 1 @@ -612,7 +634,6 @@ async def scorep_execute( code, code_for_history, silent, - store_history=True, user_expressions=None, allow_stdin=False, *, @@ -652,6 +673,7 @@ async def scorep_execute( allow_stdin=allow_stdin, cell_id=cell_id, ) + if reply_status_dump["status"] != "ok": self.ghost_cell_error( reply_status_dump, @@ -665,13 +687,12 @@ async def scorep_execute( + self.scorep_binding_args + [scorep_script_name] ) - proc_env = self.scorep_env.copy() - proc_env.update({'PATH': os.environ.get('PATH', ''), - 'LD_LIBRARY_PATH': - os.environ.get('LD_LIBRARY_PATH', ''), - 'PYTHONPATH': - os.environ.get('PYTHONPATH', ''), - 'PYTHONUNBUFFERED': 'x'}) + scorep_env = {key: os.environ[key] for key in os.environ if key.startswith('SCOREP_')} + proc_env = {'PATH': os.environ.get('PATH', ''), + 'LD_LIBRARY_PATH': os.environ.get('LD_LIBRARY_PATH', ''), + 'PYTHONPATH': os.environ.get('PYTHONPATH', ''), + 'PYTHONUNBUFFERED': 'x'} + proc_env.update(scorep_env) # scorep path, subprocess observation # determine datetime for figuring out scorep path after execution @@ -823,8 +844,8 @@ async def scorep_execute( # Determine directory to which trace files were saved by Score-P scorep_folder = "" - if "SCOREP_EXPERIMENT_DIRECTORY" in self.scorep_env: - scorep_folder = self.scorep_env["SCOREP_EXPERIMENT_DIRECTORY"] + if "SCOREP_EXPERIMENT_DIRECTORY" in os.environ: + scorep_folder = os.environ["SCOREP_EXPERIMENT_DIRECTORY"] self.cell_output( f"Instrumentation results can be found in {scorep_folder}" ) @@ -867,16 +888,9 @@ async def scorep_execute( code_for_history, time_indices) return self.standard_reply() - async def do_execute( - self, - code, - silent, - store_history=False, - user_expressions=None, - allow_stdin=False, - *, - cell_id=None, - ): + async def do_execute(self, code, silent, store_history=False, + user_expressions=None, allow_stdin=False, *, + cell_id=None, **kwargs): """ Override of do_execute() method of IPythonKernel. If no custom magic commands specified, execute cell with super().do_execute(), @@ -1073,10 +1087,9 @@ async def do_execute( return self.standard_reply() elif code.startswith("%%set_perfmonitor"): return self.set_perfmonitor(code) - elif code.startswith("%%scorep_env"): - return self.set_scorep_env(code) elif code.startswith("%%scorep_python_binding_arguments"): - return self.set_scorep_pythonargs(code) + return (self.scorep_not_available() or + self.set_scorep_pythonargs(code)) elif code.startswith("%%serializer_settings"): self.cell_output( "Deprecated. Use: %%marshalling_settings\n[MARSHALLER=]\n[" @@ -1085,74 +1098,83 @@ async def do_execute( ) return self.standard_reply() elif code.startswith("%%marshalling_settings"): - return self.marshaller_settings(code) + return (self.scorep_not_available() or + self.marshaller_settings(code)) elif code.startswith("%%enable_multicellmode"): - return self.enable_multicellmode() + return self.scorep_not_available() or self.enable_multicellmode() elif code.startswith("%%abort_multicellmode"): - return self.abort_multicellmode() + return self.scorep_not_available() or self.abort_multicellmode() elif code.startswith("%%finalize_multicellmode"): # Cannot be put into a separate function due to tight coupling # between do_execute and scorep_execute - if self.mode == KernelMode.MULTICELL: - self.mode = KernelMode.DEFAULT - try: - # second multicell_code should be cleaned for code history - reply_status = await self.scorep_execute( - self.multicell_code, - self.multicell_code_history, + if not self.scorep_available_: + self.cell_output("Score-P not available, cell ignored.", + "stderr") + return self.standard_reply() + else: + if self.mode == KernelMode.MULTICELL: + self.mode = KernelMode.DEFAULT + try: + reply_status = await self.scorep_execute( + self.multicell_code, + silent, + store_history, + user_expressions, + allow_stdin, + cell_id=cell_id, + ) + except Exception: + self.cell_output( + "KernelError: Multicell execution failed.", + "stderr" + ) + return self.standard_reply() + self.multicell_code = "" + self.multicell_cellcount = 0 + return reply_status + elif self.mode == KernelMode.WRITEFILE: + self.writefile_multicell = False + return self.standard_reply() + else: + self.cell_output( + f"KernelWarning: Currently in {self.mode}, ignore command", + "stderr", + ) + return self.standard_reply() + elif code.startswith("%%start_writefile"): + return self.scorep_not_available() or self.start_writefile(code) + elif code.startswith("%%abort_writefile"): + return self.scorep_not_available() or self.abort_writefile() + elif code.startswith("%%end_writefile"): + return self.scorep_not_available() or self.end_writefile() + elif code.startswith("%%execute_with_scorep"): + if not self.scorep_available_: + self.cell_output("Score-P not available, cell ignored.", "stderr") + return self.standard_reply() + else: + if self.mode == KernelMode.DEFAULT: + return await self.scorep_execute( + code.split("\n", 1)[1], silent, store_history, user_expressions, allow_stdin, cell_id=cell_id, ) - except Exception: - self.cell_output( - "KernelError: Multicell execution failed.", "stderr" + elif self.mode == KernelMode.MULTICELL: + return self.append_multicellmode( + magics_cleanup(code.split("\n", 1)[1])[1] + ) + elif self.mode == KernelMode.WRITEFILE: + scorep_env, nomagic_code = magics_cleanup(code.split("\n", 1)[1]) + self.writefile_scorep_env.extend(scorep_env) + return self.append_writefile( + nomagic_code, + explicit_scorep=True, ) - return self.standard_reply() - self.multicell_code = "import time\n" - self.multicell_code_history = "" - self.multicell_cellcount = -1 - return reply_status - elif self.mode == KernelMode.WRITEFILE: - self.writefile_multicell = False - return self.standard_reply() - else: - self.cell_output( - f"KernelWarning: Currently in {self.mode}, ignore command", - "stderr", - ) - return self.standard_reply() - elif code.startswith("%%start_writefile"): - return self.start_writefile(code) - elif code.startswith("%%end_writefile"): - return self.end_writefile() - elif code.startswith("%%execute_with_scorep"): - if self.mode == KernelMode.DEFAULT: - # second code argument is for history purposes, we want to keep - # everything - return await self.scorep_execute( - code.split("\n", 1)[1], - code, - silent, - store_history, - user_expressions, - allow_stdin, - cell_id=cell_id, - ) - elif self.mode == KernelMode.MULTICELL: - return self.append_multicellmode( - magics_cleanup(code.split("\n", 1)[1]) - ) - elif self.mode == KernelMode.WRITEFILE: - return self.append_writefile( - magics_cleanup(code.split("\n", 1)[1]), - explicit_scorep=True, - ) else: if self.mode == KernelMode.DEFAULT: - self.pershelper.parse(magics_cleanup(code), "jupyter") + self.pershelper.parse(magics_cleanup(code)[1], "jupyter") self.perfdata_handler.start_perfmonitor(os.getpid()) parent_ret = await super().do_execute( code, @@ -1171,10 +1193,13 @@ async def do_execute( code) return parent_ret elif self.mode == KernelMode.MULTICELL: - return self.append_multicellmode(magics_cleanup(code)) + return self.append_multicellmode(magics_cleanup(code)[1]) elif self.mode == KernelMode.WRITEFILE: + scorep_env, nomagic_code = magics_cleanup(code) + self.writefile_scorep_env.extend(scorep_env) return self.append_writefile( - magics_cleanup(code), explicit_scorep=False + nomagic_code, + explicit_scorep=False, ) def do_shutdown(self, restart): diff --git a/src/jumper/userpersistence.py b/src/jumper/userpersistence.py index a356f12..ee9cd4c 100644 --- a/src/jumper/userpersistence.py +++ b/src/jumper/userpersistence.py @@ -4,8 +4,7 @@ import astunparse from pathlib import Path import uuid - -import dill +import importlib scorep_script_name = "scorep_script.py" @@ -83,12 +82,13 @@ def postprocess(self): os.remove(scorep_script_name) def set_marshaller(self, marshaller): - # TODO: valid marshallers should not be configured in code but via an - # environment variable - valid_marshallers = {"dill", "cloudpickle", "parallel_marshall"} - return marshaller in valid_marshallers and ( - setattr(self, "marshaller", marshaller) or True - ) + try: + marshaller_module = importlib.import_module(marshaller) + except ImportError: + return False + if not hasattr(marshaller_module, 'dump') or not hasattr(marshaller_module, 'load'): + return False + return (setattr(self, "marshaller", marshaller) or True) def set_mode(self, mode): valid_modes = {"disk", "memory"} @@ -99,6 +99,7 @@ def jupyter_dump(self): Generate code for kernel ghost cell to dump notebook persistence for subprocess. """ + jupyter_dump_ = ( "import sys\n" "import os\n" @@ -109,7 +110,7 @@ def jupyter_dump(self): f"'{self.paths['jupyter']['sys_path']}',{self.marshaller})\n" f"dump_variables({str(self.jupyter_variables)},globals()," f"'{self.paths['jupyter']['var']}'," - f"{self.marshaller})" + f"{self.marshaller})\n" ) return jupyter_dump_ @@ -209,14 +210,14 @@ def dump_runtime( filtered_os_environ_ = { k: v for k, v in os_environ_.items() - if not k.startswith("SCOREP_PYTHON_BINDINGS_") + if not k.startswith("SCOREP_") } with os.fdopen(os.open(os_environ_dump_, os.O_WRONLY | os.O_CREAT), 'wb') as file: - dill.dump(filtered_os_environ_, file) + marshaller.dump(filtered_os_environ_, file) with os.fdopen(os.open(sys_path_dump_, os.O_WRONLY | os.O_CREAT), 'wb') as file: - dill.dump(sys_path_, file) + marshaller.dump(sys_path_, file) def dump_variables(variables_names, globals_, var_dump_, marshaller): @@ -235,7 +236,6 @@ def dump_variables(variables_names, globals_, var_dump_, marshaller): with os.fdopen(os.open(var_dump_, os.O_WRONLY | os.O_CREAT), 'wb') as file: marshaller.dump(user_variables, file) - def load_runtime( os_environ_, sys_path_, os_environ_dump_, sys_path_dump_, marshaller ): @@ -243,10 +243,10 @@ def load_runtime( loaded_sys_path_ = [] with os.fdopen(os.open(os_environ_dump_, os.O_RDONLY), 'rb') as file: - loaded_os_environ_ = dill.load(file) + loaded_os_environ_ = marshaller.load(file) with os.fdopen(os.open(sys_path_dump_, os.O_RDONLY), 'rb') as file: - loaded_sys_path_ = dill.load(file) + loaded_sys_path_ = marshaller.load(file) # os_environ_.clear() os_environ_.update(loaded_os_environ_) @@ -327,6 +327,25 @@ def magics_cleanup(code): Remove IPython magics from the code. Return only "persistent" code, which is executed with whitelisted magics. """ + lines = code.splitlines(True) + scorep_env = [] + for i, line in enumerate(lines): + if line.startswith("%env"): + env_var = line.strip().split(' ', 1)[1] + if '=' in env_var: + # Assign environment variable value + if env_var.startswith('SCOREP'): + # For writefile mode, extract SCOREP env vars separately + scorep_env.append('export ' + env_var + '\n') + else: + key, val = env_var.split('=', 1) + lines[i] = f'os.environ["{key}"]="{val}"\n' + else: + # Print environment variable value + key = env_var + lines[i] = f'print("env: {key}=os.environ[\'{key}\']")\n' + code = ''.join(lines) + whitelist_prefixes_cell = ["%%prun", "%%capture"] whitelist_prefixes_line = ["%prun", "%time"] @@ -343,4 +362,4 @@ def magics_cleanup(code): tuple(whitelist_prefixes_line) ): # Line magic & executed cell, remove first word nomagic_code = code.split(" ", 1)[1] - return nomagic_code + return scorep_env, nomagic_code \ No newline at end of file diff --git a/tests/kernel/notebook.ipynb b/tests/kernel/notebook.ipynb index d0cee68..a335321 100644 --- a/tests/kernel/notebook.ipynb +++ b/tests/kernel/notebook.ipynb @@ -16,16 +16,18 @@ "name": "stdout", "output_type": "stream", "text": [ - "Score-P environment set successfully: {'SCOREP_ENABLE_TRACING': '1', 'SCOREP_ENABLE_PROFILING': '0', 'SCOREP_TOTAL_MEMORY': '3g', 'SCOREP_EXPERIMENT_DIRECTORY': 'test_kernel_tmp/scorep-traces'}" + "env: SCOREP_ENABLE_TRACING=1\n", + "env: SCOREP_ENABLE_PROFILING=0\n", + "env: SCOREP_TOTAL_MEMORY=3g\n", + "env: SCOREP_EXPERIMENT_DIRECTORY=test_kernel_tmp/scorep-traces\n" ] } ], "source": [ - "%%scorep_env\n", - "SCOREP_ENABLE_TRACING=1\n", - "SCOREP_ENABLE_PROFILING=0\n", - "SCOREP_TOTAL_MEMORY=3g\n", - "SCOREP_EXPERIMENT_DIRECTORY=test_kernel_tmp/scorep-traces" + "%env SCOREP_ENABLE_TRACING=1\n", + "%env SCOREP_ENABLE_PROFILING=0\n", + "%env SCOREP_TOTAL_MEMORY=3g\n", + "%env SCOREP_EXPERIMENT_DIRECTORY=test_kernel_tmp/scorep-traces" ] }, { @@ -122,14 +124,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "Serializer set to 'dill', mode set to 'memory'." + "Kernel uses 'dill' marshaller in 'memory' mode." ] } ], "source": [ - "%%serializer_settings\n", - "dill\n", - "memory" + "%%marshalling_settings\n", + "MARSHALLER=dill\n", + "MODE=memory" ] }, { @@ -162,14 +164,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "Serializer set to 'dill', mode set to 'disk'." + "Kernel uses 'dill' marshaller in 'disk' mode." ] } ], "source": [ - "%%serializer_settings\n", - "dill\n", - "disk" + "%%marshalling_settings\n", + "MARSHALLER=dill\n", + "MODE=disk" ] }, { @@ -202,14 +204,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "Serializer set to 'cloudpickle', mode set to 'memory'." + "Kernel uses 'cloudpickle' marshaller in 'memory' mode." ] } ], "source": [ - "%%serializer_settings\n", - "cloudpickle\n", - "memory" + "%%marshalling_settings\n", + "MARSHALLER=cloudpickle\n", + "MODE=memory" ] }, { @@ -242,14 +244,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "Serializer set to 'cloudpickle', mode set to 'disk'." + "Kernel uses 'cloudpickle' marshaller in 'disk' mode." ] } ], "source": [ - "%%serializer_settings\n", - "cloudpickle\n", - "disk" + "%%marshalling_settings\n", + "MARSHALLER=cloudpickle\n", + "MODE=disk" ] }, { @@ -420,9 +422,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 20, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "'/new/subprocess/path' found in sys.path\n" + ] + } + ], "source": [ "if '/new/subprocess/path' in sys.path:\n", " print(\"'/new/subprocess/path' found in sys.path\")" @@ -437,45 +447,85 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 21, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Multicell mode enabled. The following cells will be marked for instrumented execution." + ] + } + ], "source": [ "%%enable_multicellmode" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 22, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Cell marked for multicell mode. It will be executed at position 1" + ] + } + ], "source": [ "c = np.sum(c_mtx)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 23, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Multicell mode aborted." + ] + } + ], "source": [ "%%abort_multicellmode" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 24, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Multicell mode enabled. The following cells will be marked for instrumented execution." + ] + } + ], "source": [ "%%enable_multicellmode" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 25, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Cell marked for multicell mode. It will be executed at position 1" + ] + } + ], "source": [ "with scorep.instrumenter.enable():\n", " c = np.sum(c_mtx)\n", @@ -484,9 +534,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 26, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Cell marked for multicell mode. It will be executed at position 2" + ] + } + ], "source": [ "print('c =', c)\n", "print('Sum(c_vec) =', c_vec.sum())" @@ -494,9 +552,32 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 27, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u0000Executing cell 1\n", + "with scorep.instrumenter.enable():\n", + " c = np.sum(c_mtx)\n", + "c_vec = np.arange(b, c)\n", + "----------------------------------\n", + "\n", + "\n", + "Executing cell 2\n", + "print('c =', c)\n", + "print('Sum(c_vec) =', c_vec.sum())\n", + "----------------------------------\n", + "c = 350\n", + "Sum(c_vec) = 61030\n", + "\n", + "\n", + "Instrumentation results can be found in test_kernel_tmp/scorep-traces" + ] + } + ], "source": [ "%%finalize_multicellmode" ] @@ -510,31 +591,56 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 28, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Started converting to Python script. See files:\n", + "/home/carthage/py_work/hotfix/test_kernel_tmp/my_jupyter_to_script_run.sh\n", + "/home/carthage/py_work/hotfix/test_kernel_tmp/my_jupyter_to_script.py\n" + ] + } + ], "source": [ "%%start_writefile test_kernel_tmp/my_jupyter_to_script" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 29, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python commands without instrumentation recorded." + ] + } + ], "source": [ - "%%scorep_env\n", - "SCOREP_ENABLE_TRACING=1\n", - "SCOREP_ENABLE_PROFILING=0\n", - "SCOREP_TOTAL_MEMORY=3g\n", - "SCOREP_EXPERIMENT_DIRECTORY=test_kernel_tmp/scorep-traces" + "%env SCOREP_ENABLE_TRACING=1\n", + "%env SCOREP_ENABLE_PROFILING=0\n", + "%env SCOREP_TOTAL_MEMORY=3g\n", + "%env SCOREP_EXPERIMENT_DIRECTORY=test_kernel_tmp/scorep-traces" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 30, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Score-P bindings arguments recorded." + ] + } + ], "source": [ "%%scorep_python_binding_arguments\n", "--noinstrumenter" @@ -542,9 +648,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 30, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python commands without instrumentation recorded." + ] + } + ], "source": [ "import numpy as np\n", "a = 5\n", @@ -556,9 +670,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 31, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python commands with instrumentation recorded." + ] + } + ], "source": [ "%%execute_with_scorep\n", "import scorep\n", @@ -569,7 +691,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 32, "metadata": {}, "outputs": [], "source": [ @@ -578,18 +700,34 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 33, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python commands with instrumentation recorded." + ] + } + ], "source": [ "c = np.sum(c_mtx)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 34, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python commands with instrumentation recorded." + ] + } + ], "source": [ "with scorep.instrumenter.enable():\n", " c = np.sum(c_mtx)\n", @@ -598,9 +736,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 35, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python commands with instrumentation recorded." + ] + } + ], "source": [ "print('c =', c)\n", "print('Sum(c_vec) =', c_vec.sum())" @@ -608,7 +754,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 36, "metadata": {}, "outputs": [], "source": [ @@ -617,9 +763,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 37, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Finished converting to Python script." + ] + } + ], "source": [ "%%end_writefile" ] @@ -628,19 +782,37 @@ "cell_type": "code", "execution_count": null, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a + b = 15\n", + "a - b = -5\n", + "c = 350\n", + "Sum(c_vec) = 61030\n" + ] + } + ], "source": [ "%%bash\n", - "chmod u+x test_kernel_tmp/my_jupyter_to_script_run.sh\n", + "chmod u+x ./test_kernel_tmp/my_jupyter_to_script_run.sh\n", "./test_kernel_tmp/my_jupyter_to_script_run.sh" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { "kernelspec": { - "display_name": "scorep-python", + "display_name": "JUmPER", "language": "python", - "name": "scorep-python" + "name": "jumper" }, "language_info": { "file_extension": ".py", diff --git a/tests/kernel/persistence.yaml b/tests/kernel/persistence.yaml index f720699..3d2e459 100644 --- a/tests/kernel/persistence.yaml +++ b/tests/kernel/persistence.yaml @@ -1,8 +1,7 @@ - - |- - import os - os.environ['JUPYTER_VAR'] = 'JUPYTER' - - - "" + %env JUPYTER_VAR=JUPYTER + - - "env: JUPYTER_VAR=JUPYTER\n" - - |- import numpy as np @@ -64,8 +63,8 @@ 4 36 Name: a*b, dtype: int64 - - - "print('SUBPROCESS_VAR =', os.environ['SUBPROCESS_VAR'])" - - - "SUBPROCESS_VAR = SUBPROCESS\n" + - "%env SUBPROCESS_VAR" + - - "'SUBPROCESS'" - - |- if '/new/subprocess/path' in sys.path: diff --git a/tests/kernel/scorep_env.yaml b/tests/kernel/scorep_env.yaml index 859dc1d..f7af4d3 100644 --- a/tests/kernel/scorep_env.yaml +++ b/tests/kernel/scorep_env.yaml @@ -1,9 +1,11 @@ - - |- - %%scorep_env - SCOREP_ENABLE_TRACING=1 - SCOREP_ENABLE_PROFILING=0 - SCOREP_TOTAL_MEMORY=3g - SCOREP_EXPERIMENT_DIRECTORY=test_kernel_tmp/scorep-traces - - - "Score-P environment set successfully: {'SCOREP_ENABLE_TRACING': '1', 'SCOREP_ENABLE_PROFILING': '0', - 'SCOREP_TOTAL_MEMORY': '3g', 'SCOREP_EXPERIMENT_DIRECTORY': 'test_kernel_tmp/scorep-traces'}" \ No newline at end of file + %env SCOREP_ENABLE_TRACING=1 + %env SCOREP_ENABLE_PROFILING=0 + %env SCOREP_TOTAL_MEMORY=3g + %env SCOREP_EXPERIMENT_DIRECTORY=test_kernel_tmp/scorep-traces + - - | + env: SCOREP_ENABLE_TRACING=1 + env: SCOREP_ENABLE_PROFILING=0 + env: SCOREP_TOTAL_MEMORY=3g + env: SCOREP_EXPERIMENT_DIRECTORY=test_kernel_tmp/scorep-traces diff --git a/tests/kernel/writemode.yaml b/tests/kernel/writemode.yaml index 471283c..d1a805f 100644 --- a/tests/kernel/writemode.yaml +++ b/tests/kernel/writemode.yaml @@ -1,17 +1,32 @@ - - - "%%start_writefile test_kernel_tmp/my_jupyter_to_script" + - "%%start_writefile" - - | Started converting to Python script. See files: /home/runner/work/scorep_jupyter_kernel_python/scorep_jupyter_kernel_python/test_kernel_tmp/my_jupyter_to_script_run.sh /home/runner/work/scorep_jupyter_kernel_python/scorep_jupyter_kernel_python/test_kernel_tmp/my_jupyter_to_script.py - - |- - %%scorep_env - SCOREP_ENABLE_TRACING=1 - SCOREP_ENABLE_PROFILING=0 - SCOREP_TOTAL_MEMORY=3g - SCOREP_EXPERIMENT_DIRECTORY=test_kernel_tmp/scorep-traces - - - "Environment variables recorded." + %env SCOREP_ENABLE_TRACING=1 + %env SCOREP_ENABLE_PROFILING=0 + %env SCOREP_TOTAL_MEMORY=3g + %env SCOREP_EXPERIMENT_DIRECTORY=test_kernel_tmp/scorep-traces + - - "Python commands without instrumentation recorded." +- + - "%%abort_writefile" + - - "Writefile mode aborted." +- + - "%%start_writefile test_kernel_tmp/my_jupyter_to_script" + - - | + Started converting to Python script. See files: + /home/carthage/Documents/scorep_jupyter_kernel_python/test_kernel_tmp/my_jupyter_to_script_run.sh + /home/carthage/Documents/scorep_jupyter_kernel_python/test_kernel_tmp/my_jupyter_to_script.py +- + - |- + %env SCOREP_ENABLE_TRACING=1 + %env SCOREP_ENABLE_PROFILING=0 + %env SCOREP_TOTAL_MEMORY=3g + %env SCOREP_EXPERIMENT_DIRECTORY=test_kernel_tmp/scorep-traces + - - "Python commands without instrumentation recorded." - - |- %%scorep_python_binding_arguments diff --git a/tests/test_kernel.py b/tests/test_kernel.py index 430002b..f2b2fd8 100644 --- a/tests/test_kernel.py +++ b/tests/test_kernel.py @@ -28,9 +28,14 @@ def check_stream_output(self, code, expected_output, stream="stdout"): self.flush_channels() reply, output_msgs = self.execute_helper(code=code) for msg, expected_msg in zip(output_msgs, expected_output): - self.assertEqual(msg["header"]["msg_type"], "stream") - self.assertEqual(msg["content"]["name"], stream) - self.assertEqual(msg["content"]["text"], expected_msg) + #self.assertEqual(msg["header"]["msg_type"], "stream") + # some messages can be of type 'execute_result' type instead of stdout + # self.assertEqual(msg["content"]["name"], stream) + if msg["header"]["msg_type"] == "stream": + self.assertEqual(msg["content"]["name"], stream) + self.assertEqual(msg["content"]["text"], expected_msg) + elif msg["header"]["msg_type"] == "execute_result": + self.assertEqual(msg["content"]["data"]["text/plain"], expected_msg) def check_from_file(self, filename): with open(filename, "r") as file: From 19527cab7fea437cb0ef5b4c817eef1516e6e212 Mon Sep 17 00:00:00 2001 From: Elias Werner Date: Wed, 20 Nov 2024 16:57:44 +0100 Subject: [PATCH 06/13] add ipympl to dependencies --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index bee4a19..6511b6d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,13 +21,14 @@ classifiers=[ dependencies = [ "ipykernel", + "ipywidgets", + "ipympl", "jupyter-client", "astunparse", "dill", "itables", "matplotlib", "pandas", - "ipywidgets", "pynvml" # we need that only for Nvidia GPU systems ] From 928e76e0e5295d3de566ac3d90f0f8c3ad500554 Mon Sep 17 00:00:00 2001 From: Elias Werner Date: Wed, 20 Nov 2024 17:35:03 +0100 Subject: [PATCH 07/13] check also for scorep python bindings in scorep availability --- src/jumper/kernel.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/jumper/kernel.py b/src/jumper/kernel.py index abfd1e0..d7ef4b8 100644 --- a/src/jumper/kernel.py +++ b/src/jumper/kernel.py @@ -97,6 +97,11 @@ def __init__(self, **kwargs): self.nodelist = self.perfdata_handler.get_nodelist() self.scorep_available_ = shutil.which("scorep") + self.scorep_python_available_ = True + try: + import scorep + except ModuleNotFoundError: + self.scorep_python_available_ = False def cell_output(self, string, stream="stdout"): """ @@ -116,7 +121,13 @@ def standard_reply(self): def scorep_not_available(self): if not self.scorep_available_: - self.cell_output("Score-P not available, cell ignored.", "stderr") + self.cell_output("Score-P not available, cell ignored.", + "stderr") + return self.standard_reply() + if not self.scorep_python_available_: + self.cell_output("Score-P Python not available, cell ignored. " + "Consider installing it via `pip install scorep`", + "stderr") return self.standard_reply() else: return None @@ -1188,12 +1199,8 @@ async def do_execute(self, code, silent, store_history=False, elif code.startswith("%%end_writefile"): return self.scorep_not_available() or self.end_writefile() elif code.startswith("%%execute_with_scorep"): - if not self.scorep_available_: - self.cell_output( - "Score-P not available, cell ignored.", "stderr" - ) - return self.standard_reply() - else: + scorep_missing = self.scorep_not_available() + if scorep_missing is None: if self.mode == KernelMode.DEFAULT: return await self.scorep_execute( code.split("\n", 1)[1], @@ -1216,6 +1223,8 @@ async def do_execute(self, code, silent, store_history=False, nomagic_code, explicit_scorep=True, ) + else: + return scorep_missing else: if self.mode == KernelMode.DEFAULT: self.pershelper.parse(magics_cleanup(code)[1], "jupyter") From 87607e51a97423347080f8b5e4d89600f8106161 Mon Sep 17 00:00:00 2001 From: Elias Werner Date: Wed, 20 Nov 2024 18:04:05 +0100 Subject: [PATCH 08/13] fix linter, improve scorep python check --- src/jumper/kernel.py | 115 +++++++++++++++++++++--------------- src/jumper/visualization.py | 41 +++++++++---- 2 files changed, 97 insertions(+), 59 deletions(-) diff --git a/src/jumper/kernel.py b/src/jumper/kernel.py index d7ef4b8..898b804 100644 --- a/src/jumper/kernel.py +++ b/src/jumper/kernel.py @@ -15,7 +15,7 @@ from itables import show from jumper.userpersistence import PersHelper, scorep_script_name from jumper.userpersistence import magics_cleanup - +import importlib from jumper.perfdatahandler import PerformanceDataHandler import jumper.visualization as perfvis @@ -99,7 +99,7 @@ def __init__(self, **kwargs): self.scorep_available_ = shutil.which("scorep") self.scorep_python_available_ = True try: - import scorep + importlib.import_module("scorep") except ModuleNotFoundError: self.scorep_python_available_ = False @@ -121,13 +121,14 @@ def standard_reply(self): def scorep_not_available(self): if not self.scorep_available_: - self.cell_output("Score-P not available, cell ignored.", - "stderr") + self.cell_output("Score-P not available, cell ignored.", "stderr") return self.standard_reply() if not self.scorep_python_available_: - self.cell_output("Score-P Python not available, cell ignored. " - "Consider installing it via `pip install scorep`", - "stderr") + self.cell_output( + "Score-P Python not available, cell ignored. " + "Consider installing it via `pip install scorep`", + "stderr", + ) return self.standard_reply() else: return None @@ -268,7 +269,7 @@ def append_multicellmode(self, code): f"print('Executing cell {self.multicell_cellcount}')\n" + f"print('''{code}''')\n" + f"print('-' * {max_line_len})\n" - + f"print('MCM_TS'+str(time.time()))\n" + + "print('MCM_TS'+str(time.time()))\n" + f"{code}\n" + "print('''\n''')\n" ) @@ -814,17 +815,21 @@ async def scorep_execute( # retrieve the index this cell will have in the global history sub_idx = len(self.perfdata_handler.get_code_history()) # append to have end of last code fragment - multicellmode_timestamps.append("MCM_TS"+str(time.time())) + multicellmode_timestamps.append("MCM_TS" + str(time.time())) time_indices = [[]] nb_ms = 0.0 for idx, ts_string in enumerate(multicellmode_timestamps[:-1]): - secs = (float(multicellmode_timestamps[idx+1][6:]) - - float(ts_string[6:])) - nb_ms += (secs / - int(os.environ.get("JUMPER_REPORT_FREQUENCY", 2))) + secs = float(multicellmode_timestamps[idx + 1][6:]) - float( + ts_string[6:] + ) + nb_ms += secs / int( + os.environ.get("JUMPER_REPORT_FREQUENCY", 2) + ) if nb_ms >= 1.0: # only consider if we have measurements - time_indices[0].append((str(sub_idx)+"_"+str(idx), nb_ms)) + time_indices[0].append( + (str(sub_idx) + "_" + str(idx), nb_ms) + ) nb_ms %= 1.0 # add time for last to last measurement if nb_ms >= 0.0: @@ -932,13 +937,22 @@ async def scorep_execute( self.pershelper.postprocess() if performance_data_nodes: self.report_perfdata(performance_data_nodes, duration) - self.perfdata_handler.append_code(datetime.datetime.now(), - code_for_history, time_indices) + self.perfdata_handler.append_code( + datetime.datetime.now(), code_for_history, time_indices + ) return self.standard_reply() - async def do_execute(self, code, silent, store_history=False, - user_expressions=None, allow_stdin=False, *, - cell_id=None, **kwargs): + async def do_execute( + self, + code, + silent, + store_history=False, + user_expressions=None, + allow_stdin=False, + *, + cell_id=None, + **kwargs, + ): """ Override of do_execute() method of IPythonKernel. If no custom magic commands specified, execute cell with super().do_execute(), @@ -972,20 +986,20 @@ async def do_execute(self, code, silent, store_history=False, """ if code.startswith("%%display_graph_for_last"): if not len(self.perfdata_handler.get_perfdata_history()): - self.cell_output( - "No performance data available." - ) + self.cell_output("No performance data available.") time_indices = self.perfdata_handler.get_time_indices()[-1] if time_indices: sub_idxs = [x[0] for x in time_indices[0]] - self.cell_output(f"Cell seemed to be tracked in multi cell" - " mode. Got performance data for the" - f" following sub cells: {sub_idxs}") + self.cell_output( + f"Cell seemed to be tracked in multi cell" + " mode. Got performance data for the" + f" following sub cells: {sub_idxs}" + ) perfvis.draw_performance_graph( self.nodelist, self.perfdata_handler.get_perfdata_history()[-1], self.gpu_avail, - time_indices + time_indices, ) return self.standard_reply() elif code.startswith("%%display_graph_for_index"): @@ -1005,14 +1019,16 @@ async def do_execute(self, code, silent, store_history=False, time_indices = self.perfdata_handler.get_time_indices()[index] if time_indices: sub_idxs = [x[0] for x in time_indices[0]] - self.cell_output(f"Cell seemed to be tracked in multi cell" - " mode. Got performance data for the" - f" following sub cells: {sub_idxs}") + self.cell_output( + f"Cell seemed to be tracked in multi cell" + " mode. Got performance data for the" + f" following sub cells: {sub_idxs}" + ) perfvis.draw_performance_graph( self.nodelist, self.perfdata_handler.get_perfdata_history()[index], self.gpu_avail, - time_indices + time_indices, ) return self.standard_reply() elif code.startswith("%%display_graph_for_all"): @@ -1057,8 +1073,9 @@ async def do_execute(self, code, silent, store_history=False, pd.DataFrame( self.perfdata_handler.get_code_history(), columns=["timestamp", "code"], - ).reset_index(), layout={"topStart": "search", "topEnd": None}, - columnDefs=[{"className": 'dt-left', "targets": 2}], + ).reset_index(), + layout={"topStart": "search", "topEnd": None}, + columnDefs=[{"className": "dt-left", "targets": 2}], ) return self.standard_reply() elif code.startswith("%%perfdata_to_variable"): @@ -1078,15 +1095,18 @@ async def do_execute(self, code, silent, store_history=False, # measurements, e.g. (2_0, 5), (2_1, 3), (2_2, 7) mcm_time_indices = self.perfdata_handler.get_time_indices() mcm_time_indices = list( - filter(lambda item: item is not None, mcm_time_indices)) + filter(lambda item: item is not None, mcm_time_indices) + ) - code = (f"{varname}=" - f"{self.perfdata_handler.get_perfdata_history()}") + code = ( + f"{varname}=" + f"{self.perfdata_handler.get_perfdata_history()}" + ) if mcm_time_indices: code += f"\n{varname}.append({mcm_time_indices})" - await super().do_execute(code,silent=True) + await super().do_execute(code, silent=True) self.cell_output( "Exported performance data to " + str(varname) @@ -1097,10 +1117,10 @@ async def do_execute(self, code, silent, store_history=False, self.cell_output( "Detected that cells were executed in multi cell mode." + f"Last entry in {varname} is a list that contains " - f"the sub indices per cell that were executed in " - f"in multi cell mode and a counter for the number of" - f" performance measurements within this sub cell, " - f"e.g. f{mcm_time_indices[-1]}", + f"the sub indices per cell that were executed in " + f"in multi cell mode and a counter for the number of" + f" performance measurements within this sub cell, " + f"e.g. f{mcm_time_indices[-1]}", "stdout", ) return self.standard_reply() @@ -1137,8 +1157,9 @@ async def do_execute(self, code, silent, store_history=False, elif code.startswith("%%set_perfmonitor"): return self.set_perfmonitor(code) elif code.startswith("%%scorep_python_binding_arguments"): - return (self.scorep_not_available() or - self.set_scorep_pythonargs(code)) + return self.scorep_not_available() or self.set_scorep_pythonargs( + code + ) elif code.startswith("%%serializer_settings"): self.cell_output( "Deprecated. Use: %%marshalling_settings" @@ -1147,8 +1168,9 @@ async def do_execute(self, code, silent, store_history=False, ) return self.standard_reply() elif code.startswith("%%marshalling_settings"): - return (self.scorep_not_available() or - self.marshaller_settings(code)) + return self.scorep_not_available() or self.marshaller_settings( + code + ) elif code.startswith("%%enable_multicellmode"): return self.scorep_not_available() or self.enable_multicellmode() elif code.startswith("%%abort_multicellmode"): @@ -1242,8 +1264,9 @@ async def do_execute(self, code, silent, store_history=False, ) if performance_data_nodes: self.report_perfdata(performance_data_nodes, duration) - self.perfdata_handler.append_code(datetime.datetime.now(), - code) + self.perfdata_handler.append_code( + datetime.datetime.now(), code + ) return parent_ret elif self.mode == KernelMode.MULTICELL: return self.append_multicellmode(magics_cleanup(code)[1]) diff --git a/src/jumper/visualization.py b/src/jumper/visualization.py index dcff8b0..c0ef635 100644 --- a/src/jumper/visualization.py +++ b/src/jumper/visualization.py @@ -150,8 +150,8 @@ def plot_graph(ax, metric, perfdata, time_indices=None, color=None): # colorization of the plot in case of multiple cells if time_indices: - # in multi node case, we have to iterate over the indices (time_indices) - # and not only 0 here + # in multi node case, we have to iterate over the indices ( + # time_indices) and not only 0 here current_index = 0 target_index = -1 transition_offset = (x_scale[1] - x_scale[0]) / 2 @@ -165,21 +165,36 @@ def plot_graph(ax, metric, perfdata, time_indices=None, color=None): # don't use offset for last cell if sub_idx == last_idx: transition_offset = 0 - ax.axvspan(x_scale[current_index] + start_offset, - x_scale[target_index] + - transition_offset, - facecolor=color[cell_idx], alpha=0.3) + ax.axvspan( + x_scale[current_index] + start_offset, + x_scale[target_index] + transition_offset, + facecolor=color[cell_idx], + alpha=0.3, + ) - text_x_pos = x_scale[current_index] + start_offset + ( - (x_scale[target_index] + transition_offset - - x_scale[current_index] + start_offset) / 2) + text_x_pos = ( + x_scale[current_index] + + start_offset + + ( + ( + x_scale[target_index] + + transition_offset + - x_scale[current_index] + + start_offset + ) + / 2 + ) + ) text_y_pos = ax.get_ylim()[0] + (ax.get_ylim()[1] * 0.05) # add cell index to plot - ax.text(text_x_pos, text_y_pos, "#" + str(sub_idx), style='italic', - bbox={ - 'facecolor': 'lightgrey', 'alpha': 0.5, 'pad': 2} - ) + ax.text( + text_x_pos, + text_y_pos, + "#" + str(sub_idx), + style="italic", + bbox={"facecolor": "lightgrey", "alpha": 0.5, "pad": 2}, + ) current_index = target_index start_offset = transition_offset From 78ad091dc389530c8cbfc79308b6f297db73605f Mon Sep 17 00:00:00 2001 From: Elias Werner Date: Wed, 20 Nov 2024 19:15:12 +0100 Subject: [PATCH 09/13] not sure if tests fixed --- tests/kernel/multicell.yaml | 10 +++++----- tests/test_kernel.py | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/kernel/multicell.yaml b/tests/kernel/multicell.yaml index a0e4720..bda0392 100644 --- a/tests/kernel/multicell.yaml +++ b/tests/kernel/multicell.yaml @@ -3,7 +3,7 @@ - - "Multicell mode enabled. The following cells will be marked for instrumented execution." - - "c = np.sum(c_mtx)" - - - "Cell marked for multicell mode. It will be executed at position 1" + - - "Cell marked for multicell mode. It will be executed at position 0" - - "%%abort_multicellmode" - - "Multicell mode aborted." @@ -15,23 +15,23 @@ with scorep.instrumenter.enable(): c = np.sum(c_mtx) c_vec = np.arange(b, c) - - - "Cell marked for multicell mode. It will be executed at position 1" + - - "Cell marked for multicell mode. It will be executed at position 0" - - |- print('c =', c) print('Sum(c_vec) =', c_vec.sum()) - - - "Cell marked for multicell mode. It will be executed at position 2" + - - "Cell marked for multicell mode. It will be executed at position 1" - - "%%finalize_multicellmode" - - "\0" - - "Executing cell 1\n" + - "Executing cell 0\n" - "with scorep.instrumenter.enable():\n" - " c = np.sum(c_mtx)\n" - "c_vec = np.arange(b, c)\n" - "----------------------------------\n" - "\n" - "\n" - - "Executing cell 2\n" + - "Executing cell 1\n" - "print('c =', c)\n" - "print('Sum(c_vec) =', c_vec.sum())\n" - "----------------------------------\n" diff --git a/tests/test_kernel.py b/tests/test_kernel.py index 7bd0584..abec581 100644 --- a/tests/test_kernel.py +++ b/tests/test_kernel.py @@ -34,6 +34,7 @@ def check_stream_output(self, code, expected_output, stream="stdout"): # some messages can be of type 'execute_result' # type instead of stdout # self.assertEqual(msg["content"]["name"], stream) + if msg["header"]["msg_type"] == "stream": self.assertEqual(msg["content"]["name"], stream) self.assertEqual(msg["content"]["text"], expected_msg) @@ -42,6 +43,7 @@ def check_stream_output(self, code, expected_output, stream="stdout"): msg["content"]["data"]["text/plain"], expected_msg ) + def check_from_file(self, filename): with open(filename, "r") as file: From 174732b58b3607c1b090b06a96c77fe3de5af7e0 Mon Sep 17 00:00:00 2001 From: Elias Werner Date: Thu, 21 Nov 2024 05:49:03 +0100 Subject: [PATCH 10/13] fix multicellmode for very short cells --- src/jumper/kernel.py | 76 +++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/src/jumper/kernel.py b/src/jumper/kernel.py index 898b804..fe3bfb6 100644 --- a/src/jumper/kernel.py +++ b/src/jumper/kernel.py @@ -674,7 +674,6 @@ def report_perfdata(self, performance_data_nodes, duration): async def scorep_execute( self, code, - code_for_history, silent, user_expressions=None, allow_stdin=False, @@ -702,7 +701,6 @@ async def scorep_execute( os.open(scorep_script_name, os.O_WRONLY | os.O_CREAT), "w" ) as file: file.write(self.pershelper.subprocess_wrapper(code)) - # For disk mode use implicit synchronization between kernel and # subprocess: await jupyter_dump, subprocess.wait(), # await jupyter_update Ghost cell - dump current Jupyter session for @@ -752,6 +750,7 @@ async def scorep_execute( proc = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=proc_env ) + self.perfdata_handler.start_perfmonitor(proc.pid) # For memory mode jupyter_dump and jupyter_update must be awaited # concurrently to the running subprocess @@ -810,6 +809,7 @@ async def scorep_execute( # explicit timestamps, but aligns the colorization of the plot based # on the number of perf measurements we have, which is individual per # node + time_indices = None if len(multicellmode_timestamps): # retrieve the index this cell will have in the global history @@ -832,9 +832,15 @@ async def scorep_execute( ) nb_ms %= 1.0 # add time for last to last measurement + if nb_ms >= 0.0: - sub_idx, val = time_indices[0][-1] - time_indices[0][-1] = (sub_idx, val + nb_ms) + if len(time_indices[0]): + sub_idx, val = time_indices[0][-1] + time_indices[0][-1] = (sub_idx, val + nb_ms) + else: + time_indices[0].append( + (str(sub_idx) + "_" + str(0), nb_ms) + ) nb_ms = 0.0 for idx, val in enumerate(time_indices[0]): @@ -903,7 +909,7 @@ async def scorep_execute( f"Instrumentation results can be found in {scorep_folder}" ) else: - # Find last creasted directory with scorep* name + # Find last created directory with scorep* name # TODO: Directory isn't created local when running scorep-collector max_iterations = 5 while max_iterations > 0: @@ -938,7 +944,7 @@ async def scorep_execute( if performance_data_nodes: self.report_perfdata(performance_data_nodes, duration) self.perfdata_handler.append_code( - datetime.datetime.now(), code_for_history, time_indices + datetime.datetime.now(), code, time_indices ) return self.standard_reply() @@ -1178,42 +1184,35 @@ async def do_execute( elif code.startswith("%%finalize_multicellmode"): # Cannot be put into a separate function due to tight coupling # between do_execute and scorep_execute - if not self.scorep_available_: - self.cell_output( - "Score-P not available, cell ignored.", "stderr" - ) - return self.standard_reply() - else: - if self.mode == KernelMode.MULTICELL: - self.mode = KernelMode.DEFAULT - try: - reply_status = await self.scorep_execute( - self.multicell_code, - silent, - store_history, - user_expressions, - allow_stdin, - cell_id=cell_id, - ) - except Exception: - self.cell_output( - "KernelError: Multicell execution failed.", - "stderr", - ) - return self.standard_reply() - self.multicell_code = "" - self.multicell_cellcount = 0 - return reply_status - elif self.mode == KernelMode.WRITEFILE: - self.writefile_multicell = False - return self.standard_reply() - else: + if self.mode == KernelMode.MULTICELL: + self.mode = KernelMode.DEFAULT + try: + reply_status = await self.scorep_execute( + self.multicell_code, + silent, + user_expressions, + allow_stdin, + cell_id=cell_id, + ) + except Exception as e: self.cell_output( - f"KernelWarning: Currently in {self.mode}," - f" ignore command", + "KernelError: Multicell execution failed.", "stderr", ) return self.standard_reply() + self.multicell_code = "" + self.multicell_cellcount = -1 + return reply_status + elif self.mode == KernelMode.WRITEFILE: + self.writefile_multicell = False + return self.standard_reply() + else: + self.cell_output( + f"KernelWarning: Currently in {self.mode}," + f" ignore command", + "stderr", + ) + return self.standard_reply() elif code.startswith("%%start_writefile"): return self.scorep_not_available() or self.start_writefile(code) elif code.startswith("%%abort_writefile"): @@ -1227,7 +1226,6 @@ async def do_execute( return await self.scorep_execute( code.split("\n", 1)[1], silent, - store_history, user_expressions, allow_stdin, cell_id=cell_id, From cbb27698c41f108d26eab10c7c9a607bfaf7da6d Mon Sep 17 00:00:00 2001 From: Elias Werner Date: Thu, 21 Nov 2024 06:04:47 +0100 Subject: [PATCH 11/13] make flake8 happy --- src/jumper/kernel.py | 2 +- src/jumper/userpersistence.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/jumper/kernel.py b/src/jumper/kernel.py index fe3bfb6..fc16b8e 100644 --- a/src/jumper/kernel.py +++ b/src/jumper/kernel.py @@ -1194,7 +1194,7 @@ async def do_execute( allow_stdin, cell_id=cell_id, ) - except Exception as e: + except Exception: self.cell_output( "KernelError: Multicell execution failed.", "stderr", diff --git a/src/jumper/userpersistence.py b/src/jumper/userpersistence.py index 07022c1..c8f1695 100644 --- a/src/jumper/userpersistence.py +++ b/src/jumper/userpersistence.py @@ -240,6 +240,7 @@ def dump_variables(variables_names, globals_, var_dump_, marshaller): with os.fdopen(os.open(var_dump_, os.O_WRONLY | os.O_CREAT), "wb") as file: marshaller.dump(user_variables, file) + def load_runtime( os_environ_, sys_path_, os_environ_dump_, sys_path_dump_, marshaller ): From 612b4660525d788b5011bae6ac3c2d237b47568b Mon Sep 17 00:00:00 2001 From: Elias Werner Date: Thu, 21 Nov 2024 17:29:03 +0100 Subject: [PATCH 12/13] fix visualization --- src/jumper/visualization.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/jumper/visualization.py b/src/jumper/visualization.py index c0ef635..01bdc6d 100644 --- a/src/jumper/visualization.py +++ b/src/jumper/visualization.py @@ -153,7 +153,7 @@ def plot_graph(ax, metric, perfdata, time_indices=None, color=None): # in multi node case, we have to iterate over the indices ( # time_indices) and not only 0 here current_index = 0 - target_index = -1 + target_index = 0 transition_offset = (x_scale[1] - x_scale[0]) / 2 start_offset = 0 last_idx = time_indices[0][-1][0] @@ -167,7 +167,7 @@ def plot_graph(ax, metric, perfdata, time_indices=None, color=None): transition_offset = 0 ax.axvspan( x_scale[current_index] + start_offset, - x_scale[target_index] + transition_offset, + x_scale[min(target_index, len(x_scale)-1)] + transition_offset, facecolor=color[cell_idx], alpha=0.3, ) @@ -177,7 +177,7 @@ def plot_graph(ax, metric, perfdata, time_indices=None, color=None): + start_offset + ( ( - x_scale[target_index] + x_scale[min(target_index, len(x_scale)-1)] + transition_offset - x_scale[current_index] + start_offset From 17e11ef4c9eadf426a61a0382c3e4fb6a79bca85 Mon Sep 17 00:00:00 2001 From: Elias Werner Date: Thu, 28 Nov 2024 16:55:20 +0100 Subject: [PATCH 13/13] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6511b6d..cbd21e2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta:__legacy__" [project] name='jumper-kernel' -version='1.0.0' +version='1.1.0' authors=[ {name='Elias Werner',email='elias.werner@tu-dresden.de'}, ]