From dfbabe7ae0d5049421a46b0b15e5a166bc526c60 Mon Sep 17 00:00:00 2001 From: Maximilian Schmeller Date: Mon, 12 Sep 2022 19:24:26 +0200 Subject: [PATCH] Make notebook script-executable * Add settings to disable certain features * Add IPython magic to conditionally execute cells * Add support for config through environment vars * Installation instructions in README.md --- README.md | 21 ++- misc/utils.py | 21 +++ requirements.txt | 2 - trace-analysis.ipynb | 370 ++++++++++++++++++++++++++----------------- 4 files changed, 266 insertions(+), 148 deletions(-) diff --git a/README.md b/README.md index eca5261..1bec1f4 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,22 @@ # MA Autoware Trace Analysis -Automatically extract data dependencies and latencies from Autoware source code and trace data +Automatically extract data dependencies and end-to-end (E2E) latencies from ROS2 trace data. + +## Prerequisites +* Python 3.10 +* [JupyterLab](https://jupyter.org/install#jupyterlab) or [Jupyter Notebook](https://jupyter.org/install#jupyter-notebook) +* [ROS2 Tracing](https://github.com/ros2/ros2_tracing) +* [Tracetools Analysis](https://gitlab.com/ros-tracing/tracetools_analysis) +* `python3-babeltrace` and `python3-lttng`, e.g. via `sudo apt install`, **for Python 3.10** (this requires either Ubuntu 22.04 or custom installation) + +## Installation + +```shell +# Make sure you are running Python 3.10 and the corresponding pip: +python3.10 -V +pip3.10 -V + +pip3.10 install -r requirements.txt +``` + +## Usage diff --git a/misc/utils.py b/misc/utils.py index cd74968..3d43b01 100644 --- a/misc/utils.py +++ b/misc/utils.py @@ -8,6 +8,19 @@ import pickle import time from typing import List +from IPython.core.magic import (register_cell_magic, needs_local_scope) +from IPython import get_ipython + + +@register_cell_magic +@needs_local_scope +def skip_if_false(line, cell, local_ns=None): + condition_var = eval(line, None, local_ns) + if condition_var: + result = get_ipython().run_cell(cell) + return None + return f"Skipped (evaluated {line} to False)" + def left_abbreviate(string, limit=120): return string if len(string) <= limit else f"...{string[:limit - 3]}" @@ -44,6 +57,14 @@ def stable_hash(obj): return hashlib.md5(json.dumps(obj).encode("utf-8")).hexdigest()[:10] +def parse_as(type, string): + if any(issubclass(type, type2) for type2 in (str, bool, float, int)): + return type(string) + if issubclass(type, list) or issubclass(type, dict) or issubclass(type, set): + val = json.loads(string) + return type(val) + raise ValueError(f"Unknown type {type.__name__}") + def cached(name, function, file_deps: List[str]): if not os.path.isdir("cache"): os.makedirs("cache", exist_ok=True) diff --git a/requirements.txt b/requirements.txt index 90e2676..7343431 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,5 +4,3 @@ matplotlib pyvis graphviz ruamel.yaml -blist @ git+https://github.com/mojomex/blist.git@47724cbc4137ddfb685f9711e950fb82587bf971 - diff --git a/trace-analysis.ipynb b/trace-analysis.ipynb index bbf94a4..e3ff6ec 100644 --- a/trace-analysis.ipynb +++ b/trace-analysis.ipynb @@ -21,22 +21,13 @@ "import pandas as pd\n", "from matplotlib import pyplot as plt\n", "\n", - "from clang_interop.cl_types import ClContext\n", - "from clang_interop.process_clang_output import process_clang_output\n", - "\n", - "sys.path.append(\"../autoware/build/tracetools_read/\")\n", - "sys.path.append(\"../autoware/build/tracetools_analysis/\")\n", - "from tracetools_read.trace import *\n", - "from tracetools_analysis.loading import load_file\n", - "from tracetools_analysis.processor.ros2 import Ros2Handler\n", - "from tracetools_analysis.utils.ros2 import Ros2DataModelUtil\n", - "\n", - "from tracing_interop.tr_types import TrTimer, TrTopic, TrPublisher, TrPublishInstance, TrCallbackInstance, \\\n", - "TrCallbackSymbol, TrCallbackObject, TrSubscriptionObject, TrContext\n", - "from misc.utils import ProgressPrinter, cached\n", + "from misc.utils import ProgressPrinter, cached, parse_as\n", "\n", "%load_ext pyinstrument\n", - "%matplotlib inline" + "%matplotlib inline\n", + "\n", + "A=True\n", + "B=None" ] }, { @@ -49,10 +40,133 @@ }, "outputs": [], "source": [ - "TR_PATH = os.path.expanduser(\"data/awsim-trace/ust\")\n", - "CL_PATH = os.path.expanduser(\"~/Projects/llvm-project/clang-tools-extra/ros2-internal-dependency-checker/output\")" + "##################################################\n", + "# User Settings\n", + "##################################################\n", + "# Change these to influence the execution of the\n", + "# notebook.\n", + "# You can override these from the command line\n", + "# by defining environment variables with the\n", + "# name of the constants below, prefixed with\n", + "# \"ANA_NB_\".\n", + "# For example, the environment variable\n", + "# \"ANA_NB_TR_PATH\" will override the \"TR_PATH\"\n", + "# setting below.\n", + "##################################################\n", + "\n", + "# The path to the build folder of a ROS2 workspace that contains the\n", + "# tracetools_read and tracetools_analysis folders.\n", + "TRACING_WS_BUILD_PATH = \"../autoware/build\"\n", + "\n", + "# Path to trace directory (e.g. ~/.ros/my-trace/ust) or to a converted trace file.\n", + "# Using the path \"/ust\" at the end is optional but greatly reduces processing time\n", + "# if kernel traces are also present.\n", + "TR_PATH = \"data/awsim-trace/ust\"\n", + "\n", + "# Path to the folder all artifacts from this notebook are saved to.\n", + "# This entails plots as well as data tables.\n", + "OUT_PATH = \"out/\"\n", + "\n", + "# Whether to annotate topics/publications with bandwidth/message size\n", + "BW_ENABLED = True\n", + "# Path to a results folder as output by ma-hw-perf-tools/messages/record.bash\n", + "# Used to annotate message sizes in E2E latency calculations\n", + "BW_PATH = \"../ma-hw-perf-tools/data/results\"\n", + "\n", + "# Whether to use dependencies extracted by the Clang-tools to supplement\n", + "# automatic node-internal data flow annotations.\n", + "# If in doubt, set to False.\n", + "CL_ENABLED = False\n", + "# Path to the output directory of the ROS2 dependency checker.\n", + "# Will only be used if CL_ENABLED is True.\n", + "CL_PATH = \"~/Projects/llvm-project/clang-tools-extra/ros2-internal-dependency-checker/output\"\n", + "\n", + "# Whether to compute data flow graphs.\n", + "# If you are only interested in E2E latencies, set this to False\n", + "DFG_ENABLED = False\n", + "# Whether to plot data flow graphs (ignored if DFG_ENABLED is False)\n", + "DFG_PLOT = True\n", + "\n", + "# The maximum node namespace hierarchy level to be plotted.\n", + "# Top-level (1): e.g. /sensing, /control, etc.\n", + "# Level 3: e.g. /sensing/lidar/pointcloud_processor\n", + "DFG_MAX_HIER_LEVEL = 100\n", + "\n", + "# RegEx pattern for nodes that shall be marked as system inputs\n", + "# These will be plotted with a start arrow as known from automata diagrams\n", + "DFG_INPUT_NODE_PATTERNS = [r\"^/sensing\"]\n", + "# RegEx pattern for nodes that shall be marked as system outputs\n", + "# These will be plotted with a double border\n", + "DFG_OUTPUT_NODE_PATTERNS = [r\"^/awapi\", r\"^/control/external_cmd_converter\"]\n", + "# RegEx for nodes which shall not be plotted in the DFG\n", + "DFG_EXCL_NODE_PATTERNS = [r\"^/rviz2\", r\"transform_listener_impl\"]\n", + "\n", + "# Whether to compute E2E latencies.\n", + "E2E_ENABLED = True\n", + "# Whether to plot end-to-end latency information (ignored if E2E_ENABLED is False)\n", + "E2E_PLOT = True\n", + "# The index of the output message that shall be used in plots that visualize a specific\n", + "# message dependency tree. This index has to be 0 <= n < #output messages\n", + "E2E_PLOT_TIMESTAMP = 3900\n", + "# E2E latency threshold. Every E2E latency higher than this is discarded.\n", + "# Set this as low as comfortably possible to speed up calculations.\n", + "# WARNING: If you set this too low (i.e. to E2E latencies that plausibly can happen)\n", + "# your results will be wrong)\n", + "E2E_TIME_LIMIT_S = 2\n", + "\n", + "# All topics containing any of these RegEx patterns are considered output topics in E2E latency calculations\n", + "# E.g. r\"^/control/\" will cover all control topics\n", + "E2E_OUTPUT_TOPIC_PATTERNS = [r\"^/control/trajectory_follower/control_cmd\"]\n", + "# All topics containing any of these RegEx patterns are considered input topics in E2E latency calculations\n", + "# E.g. r\"^/sensing/\" will cover all sensing topics\n", + "E2E_INPUT_TOPIC_PATTERNS = [\"/vehicle/status/\", \"/sensing/imu\"]\n", + "\n", + "\n", + "# This code overrides the above constants with environment variables, do not edit.\n", + "for env_key, env_value in os.environ.items():\n", + " if env_key.startswith(\"ANA_NB_\"):\n", + " key = env_key.removeprefix(\"ANA_NB_\")\n", + " if key not in globals().keys():\n", + " continue\n", + " value = parse_as(type(globals()[key]), env_value)\n", + " globals()[key] = value\n", + "\n", + "# Convert input paths to absolute paths\n", + "def _expand_path(path):\n", + " return os.path.realpath(os.path.expandvars(os.path.expanduser(path)))\n", + "\n", + "TRACING_WS_BUILD_PATH = _expand_path(TRACING_WS_BUILD_PATH)\n", + "TR_PATH = _expand_path(TR_PATH)\n", + "OUT_PATH = _expand_path(OUT_PATH)\n", + "BW_PATH = _expand_path(BW_PATH)\n", + "CL_PATH = _expand_path(CL_PATH)" ] }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "from clang_interop.cl_types import ClContext\n", + "from clang_interop.process_clang_output import process_clang_output\n", + "\n", + "sys.path.append(os.path.join(TRACING_WS_BUILD_PATH, \"tracetools_read/\"))\n", + "sys.path.append(os.path.join(TRACING_WS_BUILD_PATH, \"tracetools_analysis/\"))\n", + "from tracetools_read.trace import *\n", + "from tracetools_analysis.loading import load_file\n", + "from tracetools_analysis.processor.ros2 import Ros2Handler\n", + "from tracetools_analysis.utils.ros2 import Ros2DataModelUtil\n", + "\n", + "from tracing_interop.tr_types import TrTimer, TrTopic, TrPublisher, TrPublishInstance, TrCallbackInstance, \\\n", + "TrCallbackSymbol, TrCallbackObject, TrSubscriptionObject, TrContext" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, { "cell_type": "markdown", "source": [ @@ -86,7 +200,7 @@ "for name in _tr_globals:\n", " globals()[name] = getattr(_tracing_context, name)\n", "\n", - "print(\"Done.\")\n" + "print(\"Done.\")" ], "metadata": { "collapsed": false, @@ -112,30 +226,13 @@ "execution_count": null, "outputs": [], "source": [ + "%%skip_if_false DFG_ENABLED\n", "from latency_graph import latency_graph as lg\n", "\n", - "lat_graph = lg.LatencyGraph(_tracing_context)\n", + "def _make_latency_graph():\n", + " return lg.LatencyGraph(_tracing_context)\n", "\n", - "import pickle\n", - "\n", - "with open(\"lat_graph.pkl\", \"wb\") as f:\n", - " pickle.dump(lat_graph, f)\n", - "#with open(\"lat_graph.pkl\", \"rb\") as f:\n", - "# lat_graph = pickle.load(f)" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": null, - "outputs": [], - "source": [ - "len(lat_graph.edges)" + "lat_graph = cached(\"lat_graph\", _make_latency_graph, [TR_PATH])" ], "metadata": { "collapsed": false, @@ -149,6 +246,9 @@ "execution_count": null, "outputs": [], "source": [ + "%%skip_if_false DFG_ENABLED\n", + "%%skip_if_false DFG_PLOT\n", + "\n", "from matching.subscriptions import sanitize\n", "from typing import Iterable, Sized\n", "from tracing_interop.tr_types import TrNode, TrCallbackObject, TrCallbackSymbol, TrSubscriptionObject\n", @@ -246,6 +346,9 @@ "execution_count": null, "outputs": [], "source": [ + "%%skip_if_false DFG_ENABLED\n", + "%%skip_if_false DFG_PLOT\n", + "\n", "import re\n", "import math\n", "\n", @@ -253,19 +356,12 @@ "# Compute in/out topics for hierarchy level X\n", "##################################################\n", "\n", - "HIER_LEVEL = 100\n", - "\n", - "input_node_patterns = [r\"^/sensing\"]\n", - "output_node_patterns = [r\"^/awapi\", r\"^/control/external_cmd_converter\"]\n", - "\n", - "node_excluded_patterns = [r\"^/rviz2\", r\"transform_listener_impl\"]\n", - "\n", "def get_nodes_on_level(lat_graph: lg.LatencyGraph):\n", " def _traverse_node(node: lg.LGHierarchyLevel, cur_lvl=0):\n", - " if cur_lvl == HIER_LEVEL:\n", + " if cur_lvl == DFG_MAX_HIER_LEVEL:\n", " return [node]\n", "\n", - " if not node.children and cur_lvl < HIER_LEVEL:\n", + " if not node.children and cur_lvl < DFG_MAX_HIER_LEVEL:\n", " return [node]\n", "\n", " collected_nodes = []\n", @@ -276,10 +372,10 @@ " return _traverse_node(lat_graph.top_node)\n", "\n", "lvl_nodes = get_nodes_on_level(lat_graph)\n", - "lvl_nodes = [n for n in lvl_nodes if not any(re.search(p, n.full_name) for p in node_excluded_patterns)]\n", + "lvl_nodes = [n for n in lvl_nodes if not any(re.search(p, n.full_name) for p in DFG_EXCL_NODE_PATTERNS)]\n", "\n", - "input_nodes = [n.full_name for n in lvl_nodes if any(re.search(p, n.full_name) for p in input_node_patterns)]\n", - "output_nodes = [n.full_name for n in lvl_nodes if any(re.search(p, n.full_name) for p in output_node_patterns)]\n", + "input_nodes = [n.full_name for n in lvl_nodes if any(re.search(p, n.full_name) for p in DFG_INPUT_NODE_PATTERNS)]\n", + "output_nodes = [n.full_name for n in lvl_nodes if any(re.search(p, n.full_name) for p in DFG_OUTPUT_NODE_PATTERNS)]\n", "\n", "print(', '.join(map(lambda n: n, input_nodes)))\n", "print(', '.join(map(lambda n: n, output_nodes)))\n", @@ -382,14 +478,12 @@ "execution_count": null, "outputs": [], "source": [ + "%%skip_if_false E2E_ENABLED\n", + "\n", "from latency_graph.message_tree import DepTree\n", "from tqdm.notebook import tqdm\n", "from bisect import bisect\n", "\n", - "# All topics containing any of these strings are considered end topics in E2E latency calculations\n", - "# E.g. /control/ will cover all control topics\n", - "end_topic_filters = [\"/control/trajectory_follower/control_cmd\"]\n", - "\n", "\n", "def inst_get_dep_msg(inst: TrCallbackInstance):\n", " if inst.callback_object not in _tracing_context.callback_objects.by_callback_object:\n", @@ -545,49 +639,34 @@ "execution_count": null, "outputs": [], "source": [ + "%%skip_if_false E2E_ENABLED\n", "\n", - "#for path in e2e_topic_paths:\n", - "# end_topics = path[-1]\n", - "end_topics = [t for t in _tracing_context.topics if any(f in t.name for f in end_topic_filters)]\n", - "for end_topic in end_topics:\n", - " end_topic: TrTopic\n", + "end_topics = [t for t in _tracing_context.topics if any(re.search(f, t.name) for f in E2E_OUTPUT_TOPIC_PATTERNS)]\n", "\n", - " pubs = end_topic.publishers\n", - " for pub in pubs:\n", - " depths = []\n", - " sizes = []\n", - " e2e_lats = []\n", - " trees = []\n", - " msgs = pub.instances\n", - " for msg in tqdm(msgs, desc=f\"Building message chains for topic {end_topic.name}\"):\n", - " msg: TrPublishInstance\n", - " tree = get_dep_tree(msg)\n", - " depths.append(tree.depth())\n", - " sizes.append(tree.size())\n", - " e2e_lats.append(tree.e2e_lat())\n", - " trees.append(tree)\n", - " if depths:\n", - " print(f\"Depth: min={min(depths)} avg={sum(depths) / len(depths)} max={max(depths)}\")\n", - " print(f\"Size: min={min(sizes)} avg={sum(sizes) / len(sizes)} max={max(sizes)}\")\n", - " print(f\"E2E Lat: min={min(e2e_lats)*1000:.3f}ms avg={sum(e2e_lats) / len(sizes)*1000:.3f}ms max={max(e2e_lats)*1000:.3f}ms\")\n", + "def build_dep_trees():\n", + " for end_topic in end_topics:\n", + " end_topic: TrTopic\n", "\n", - "with open(\"trees.pkl\", \"wb\") as f:\n", - " pickle.dump(trees, f)\n" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": null, - "outputs": [], - "source": [ - "with open(\"trees.pkl\", \"rb\") as f:\n", - " trees = pickle.load(f)" + " pubs = end_topic.publishers\n", + " for pub in pubs:\n", + " depths = []\n", + " sizes = []\n", + " e2e_lats = []\n", + " trees = []\n", + " msgs = pub.instances\n", + " for msg in tqdm(msgs, desc=f\"Building message chains for topic {end_topic.name}\"):\n", + " msg: TrPublishInstance\n", + " tree = get_dep_tree(msg)\n", + " depths.append(tree.depth())\n", + " sizes.append(tree.size())\n", + " e2e_lats.append(tree.e2e_lat())\n", + " trees.append(tree)\n", + " if depths:\n", + " print(f\"Depth: min={min(depths)} avg={sum(depths) / len(depths)} max={max(depths)}\")\n", + " print(f\"Size: min={min(sizes)} avg={sum(sizes) / len(sizes)} max={max(sizes)}\")\n", + " print(f\"E2E Lat: min={min(e2e_lats)*1000:.3f}ms avg={sum(e2e_lats) / len(sizes)*1000:.3f}ms max={max(e2e_lats)*1000:.3f}ms\")\n", + "\n", + "trees = cached(\"trees\", build_dep_trees, [TR_PATH])" ], "metadata": { "collapsed": false, @@ -601,6 +680,9 @@ "execution_count": null, "outputs": [], "source": [ + "%%skip_if_false E2E_ENABLED\n", + "%%skip_if_false BW_ENABLED\n", + "\n", "import glob\n", "\n", "\n", @@ -625,7 +707,6 @@ " return f\"{bytes:.0f} B\"\n", "\n", "\n", - "BW_PATH = \"../ma-hw-perf-tools/data/results\"\n", "bw_files = glob.glob(os.path.join(BW_PATH, \"*.log\"))\n", "msg_sizes = {}\n", "for bw_file in bw_files:\n", @@ -660,12 +741,10 @@ "execution_count": null, "outputs": [], "source": [ - "from typing import List\n", - "from latency_graph.message_tree import DepTree\n", + "%%skip_if_false E2E_ENABLED\n", "\n", - "# All topics containing any of these strings are considered start topics in E2E latency calculations\n", - "# E.g. /sensing/ will cover all sensing topics\n", - "start_topic_filters = [\"/vehicle/status/\", \"/sensing/imu\"]\n", + "\n", + "from latency_graph.message_tree import DepTree\n", "\n", "def leaf_topics(tree: DepTree, lvl=0):\n", " ret_list = []\n", @@ -679,21 +758,6 @@ " ret_list += leaf_topics(dep, lvl+1)\n", " return ret_list\n", "\n", - "#all_topics = set()\n", - "\n", - "#for tree in trees:\n", - "# for d, t in leaf_topics(tree):\n", - "# if t in [\"/parameter_events\", \"/clock\"]:\n", - "# continue\n", - "# all_topics.add(t)\n", - "\n", - "#def critical_path(self: DepTree, start_topic_filters: List[str]):\n", - "# if not self.deps:\n", - "# return [self.head]\n", - "#\n", - "# return [self.head, *max(map(DepTree.critical_path, self.deps), key=lambda ls: ls[-1].timestamp)]\n", - "\n", - "E2E_TIME_LIMIT_S = 2\n", "\n", "def all_e2es(tree: DepTree, t_start=None):\n", " if t_start is None:\n", @@ -707,7 +771,8 @@ " ret_list += all_e2es(dep, t_start)\n", " return ret_list\n", "\n", - "def relevant_e2es(tree: DepTree, start_topic_filters, t_start=None, path=None):\n", + "\n", + "def relevant_e2es(tree: DepTree, input_topic_patterns, t_start=None, path=None):\n", " if t_start is None:\n", " t_start = tree.head.timestamp\n", "\n", @@ -723,19 +788,19 @@ " if not tree.deps:\n", " match tree.head:\n", " case TrPublishInstance(publisher=pub):\n", - " if pub and any(f in pub.topic_name for f in start_topic_filters):\n", + " if pub and any(re.search(f, pub.topic_name) for f in input_topic_patterns):\n", " return [(latency,new_path)]\n", "\n", " ret_list = []\n", " for dep in tree.deps:\n", - " ret_list += relevant_e2es(dep, start_topic_filters, t_start, new_path)\n", + " ret_list += relevant_e2es(dep, input_topic_patterns, t_start, new_path)\n", " return ret_list\n", "\n", "\n", "e2ess = []\n", "e2e_pathss = []\n", "for tree in trees:\n", - " e2es, e2e_paths = zip(*relevant_e2es(tree, start_topic_filters))\n", + " e2es, e2e_paths = zip(*relevant_e2es(tree, E2E_INPUT_TOPIC_PATTERNS))\n", " e2ess.append(e2es)\n", " e2e_pathss.append(e2e_paths)" ], @@ -751,26 +816,26 @@ "execution_count": null, "outputs": [], "source": [ - "from matplotlib.animation import FuncAnimation\n", - "from IPython import display\n", + "#from matplotlib.animation import FuncAnimation\n", + "#from IPython import display\n", "\n", - "fig, ax = plt.subplots(figsize=(16, 9))\n", - "ax: plt.Axes\n", - "ax.set_xlim(0, 4)\n", + "#fig, ax = plt.subplots(figsize=(16, 9))\n", + "#ax: plt.Axes\n", + "#ax.set_xlim(0, 4)\n", "\n", - "ax.hist([], bins=200, range=(0, 4), histtype='stepfilled')\n", - "ax.set_title(\"Time: 0.000000s\")\n", + "#ax.hist([], bins=200, range=(0, 4), histtype='stepfilled')\n", + "#ax.set_title(\"Time: 0.000000s\")\n", "\n", - "def anim(frame):\n", - " print(frame, end='\\r')\n", - " ax.clear()\n", - " ax.hist(e2es[frame], bins=200, range=(0, 4), histtype='stepfilled')\n", - " ax.set_title(f\"Time: {(trees[frame].head.timestamp - trees[0].head.timestamp):.6}s\")\n", + "#def anim(frame):\n", + "# print(frame, end='\\r')\n", + "# ax.clear()\n", + "# ax.hist(e2es[frame], bins=200, range=(0, 4), histtype='stepfilled')\n", + "# ax.set_title(f\"Time: {(trees[frame].head.timestamp - trees[0].head.timestamp):.6}s\")\n", "\n", "\n", - "anim_created = FuncAnimation(fig, anim, min(len(trees), 10000), interval=16, repeat_delay=200)\n", + "#anim_created = FuncAnimation(fig, anim, min(len(trees), 10000), interval=16, repeat_delay=200)\n", "\n", - "video = anim_created.save(\"anim.mp4\", dpi=120)\n", + "#video = anim_created.save(\"anim.mp4\", dpi=120)\n", "\n", "#for tree in trees:\n", "# path = tree.critical_path(start_topic_filters)\n", @@ -802,6 +867,10 @@ "execution_count": null, "outputs": [], "source": [ + "%%skip_if_false E2E_ENABLED\n", + "%%skip_if_false E2E_PLOT\n", + "\n", + "\n", "fig, ax = plt.subplots(figsize=(18, 9), num=\"e2e_plot\")\n", "DS=1\n", "times = [tree.head.timestamp - trees[0].head.timestamp for tree in trees[::DS]]\n", @@ -847,6 +916,8 @@ "execution_count": null, "outputs": [], "source": [ + "%%skip_if_false E2E_ENABLED\n", + "\n", "def critical_path(self):\n", " if not self.deps:\n", " return [self.head]\n", @@ -891,7 +962,7 @@ "\n", " match inst:\n", " case TrPublishInstance(publisher=pub):\n", - " return pub and any(f in pub.topic_name for f in start_topic_filters)\n", + " return pub and any(f in pub.topic_name for f in E2E_INPUT_TOPIC_PATTERNS)\n", " return False\n", "\n", "\n", @@ -909,6 +980,9 @@ "execution_count": null, "outputs": [], "source": [ + "%%skip_if_false E2E_ENABLED\n", + "%%skip_if_false E2E_PLOT\n", + "\n", "from cycler import cycler\n", "\n", "def dict_safe_append(dictionary, key, value):\n", @@ -961,8 +1035,6 @@ " last_pub_time = None\n", " last_cb_time = t\n", "\n", - " #dict_safe_append(time_breakdown, \"tmr_cb_calc_time\", tmr_cb_calc_time)\n", - " #dict_safe_append(time_breakdown, \"sub_cb_calc_time\", sub_cb_calc_time)\n", " dict_safe_append(time_breakdown, \"Timer CB\", tmr_cb_relevant_time)\n", " dict_safe_append(time_breakdown, \"Subscription CB\", sub_cb_relevant_time)\n", " dict_safe_append(time_breakdown, \"DDS\", dds_time)\n", @@ -1002,6 +1074,9 @@ "execution_count": null, "outputs": [], "source": [ + "%%skip_if_false E2E_ENABLED\n", + "%%skip_if_false E2E_PLOT\n", + "\n", "from scipy import stats\n", "\n", "fig, ax = plt.subplots(figsize=(60, 15), num=\"crit_pdf\")\n", @@ -1038,16 +1113,18 @@ "execution_count": null, "outputs": [], "source": [ + "%%skip_if_false E2E_ENABLED\n", + "%%skip_if_false E2E_PLOT\n", + "\n", "from tracing_interop.tr_types import TrSubscription\n", "from matching.subscriptions import sanitize\n", "import matplotlib.patches as mpatch\n", "from matplotlib.text import Text\n", "import math\n", "\n", - "i = 3900\n", - "tree = trees[i]\n", - "e2es = e2ess[i]\n", - "e2e_paths = e2e_pathss[i]\n", + "tree = trees[E2E_PLOT_TIMESTAMP]\n", + "e2es = e2ess[E2E_PLOT_TIMESTAMP]\n", + "e2e_paths = e2e_pathss[E2E_PLOT_TIMESTAMP]\n", "margin_y = .2\n", "margin_x=0\n", "arr_width= 1 - margin_y\n", @@ -1199,7 +1276,7 @@ "plot_margin_x = .01 * tree_e2e\n", "ax.set_xlim(-tree_e2e - plot_margin_x, plot_margin_x)\n", "ax.set_yticks(np.array(range(len(y_labels))) + .5, y_labels)\n", - "ax.set_title(f\"Timestep {i}: {(tree.head.timestamp - trees[0].head.timestamp):10.6f}s\")\n", + "ax.set_title(f\"Timestep {E2E_PLOT_TIMESTAMP}: {(tree.head.timestamp - trees[0].head.timestamp):10.6f}s\")\n", "ax.set_xlabel(\"Time relative to output message [s]\")\n", "ax.set_ylabel(\"Start topic\")\n", "\n", @@ -1234,6 +1311,8 @@ "execution_count": null, "outputs": [], "source": [ + "%%skip_if_false E2E_ENABLED\n", + "\n", "from termcolor import colored\n", "from tqdm import tqdm\n", "from matching.subscriptions import sanitize\n", @@ -1295,13 +1374,14 @@ " perc = np.percentile(durations, [70, 90, 95, 100])\n", " colors = [\"green\", \"yellow\", \"red\", \"magenta\"]\n", " for part, duration in zip(key, durations):\n", - " i = 0\n", + " E2E_PLOT_TIMESTAMP = 0\n", " for j, p in enumerate(perc):\n", " if duration < p:\n", " break\n", - " i = j\n", - " dur_str = colored(f\"{duration * 1000 :>.3f}ms\", colors[i])\n", - " print(f\" -> {dur_str} {part}\")\n" + " E2E_PLOT_TIMESTAMP = j\n", + " dur_str = colored(f\"{duration * 1000 :>.3f}ms\", colors[E2E_PLOT_TIMESTAMP])\n", + " print(f\" -> {dur_str} {part}\")\n", + "\n" ], "metadata": { "collapsed": false,