1 # Copyright (c) 2019 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
6 # http://www.apache.org/licenses/LICENSE-2.0
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
14 """Algorithms to generate plots.
21 import plotly.offline as ploff
22 import plotly.graph_objs as plgo
24 from plotly.exceptions import PlotlyError
25 from collections import OrderedDict
26 from copy import deepcopy
28 from utils import mean, stdev
31 COLORS = ["SkyBlue", "Olive", "Purple", "Coral", "Indigo", "Pink",
32 "Chocolate", "Brown", "Magenta", "Cyan", "Orange", "Black",
33 "Violet", "Blue", "Yellow", "BurlyWood", "CadetBlue", "Crimson",
34 "DarkBlue", "DarkCyan", "DarkGreen", "Green", "GoldenRod",
35 "LightGreen", "LightSeaGreen", "LightSkyBlue", "Maroon",
36 "MediumSeaGreen", "SeaGreen", "LightSlateGrey"]
38 REGEX_NIC = re.compile(r'\d*ge\dp\d\D*\d*-')
41 def generate_plots(spec, data):
42 """Generate all plots specified in the specification file.
44 :param spec: Specification read from the specification file.
45 :param data: Data to process.
46 :type spec: Specification
50 logging.info("Generating the plots ...")
51 for index, plot in enumerate(spec.plots):
53 logging.info(" Plot nr {0}: {1}".format(index + 1,
54 plot.get("title", "")))
55 plot["limits"] = spec.configuration["limits"]
56 eval(plot["algorithm"])(plot, data)
57 logging.info(" Done.")
58 except NameError as err:
59 logging.error("Probably algorithm '{alg}' is not defined: {err}".
60 format(alg=plot["algorithm"], err=repr(err)))
64 def plot_service_density_reconf_box_name(plot, input_data):
65 """Generate the plot(s) with algorithm: plot_service_density_reconf_box_name
66 specified in the specification file.
68 :param plot: Plot to generate.
69 :param input_data: Data to process.
70 :type plot: pandas.Series
71 :type input_data: InputData
75 plot_title = plot.get("title", "")
76 logging.info(" Creating the data set for the {0} '{1}'.".
77 format(plot.get("type", ""), plot_title))
78 data = input_data.filter_tests_by_name(
79 plot, params=["result", "parent", "tags", "type"])
81 logging.error("No data.")
84 # Prepare the data for the plot
85 y_vals = OrderedDict()
90 if y_vals.get(test["parent"], None) is None:
91 y_vals[test["parent"]] = list()
92 loss[test["parent"]] = list()
94 y_vals[test["parent"]].append(test["result"]["time"])
95 loss[test["parent"]].append(test["result"]["loss"])
96 except (KeyError, TypeError):
97 y_vals[test["parent"]].append(None)
99 # Add None to the lists with missing data
101 nr_of_samples = list()
102 for val in y_vals.values():
103 if len(val) > max_len:
105 nr_of_samples.append(len(val))
106 for key, val in y_vals.items():
107 if len(val) < max_len:
108 val.extend([None for _ in range(max_len - len(val))])
112 df = pd.DataFrame(y_vals)
114 for i, col in enumerate(df.columns):
115 tst_name = re.sub(REGEX_NIC, "",
116 col.lower().replace('-ndrpdr', '').
117 replace('2n1l-', ''))
118 tst_name = "-".join(tst_name.split("-")[3:-2])
119 name = "{nr}. ({samples:02d} run{plural}, packets lost average: " \
120 "{loss:.1f}) {name}".format(
122 samples=nr_of_samples[i],
123 plural='s' if nr_of_samples[i] > 1 else '',
125 loss=mean(loss[col]))
127 traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
128 y=[y if y else None for y in df[col]],
131 boxpoints="outliers",
135 layout = deepcopy(plot["layout"])
136 layout["title"] = "<b>Time Lost:</b> {0}".format(layout["title"])
137 layout["yaxis"]["title"] = "<b>Implied Time Lost [s]</b>"
138 layout["legend"]["font"]["size"] = 14
139 layout["yaxis"].pop("range")
140 plpl = plgo.Figure(data=traces, layout=layout)
143 file_type = plot.get("output-file-type", ".html")
144 logging.info(" Writing file '{0}{1}'.".
145 format(plot["output-file"], file_type))
146 ploff.plot(plpl, show_link=False, auto_open=False,
147 filename='{0}{1}'.format(plot["output-file"], file_type))
148 except PlotlyError as err:
149 logging.error(" Finished with error: {}".
150 format(repr(err).replace("\n", " ")))
154 def plot_performance_box_name(plot, input_data):
155 """Generate the plot(s) with algorithm: plot_performance_box_name
156 specified in the specification file.
158 :param plot: Plot to generate.
159 :param input_data: Data to process.
160 :type plot: pandas.Series
161 :type input_data: InputData
165 plot_title = plot.get("title", "")
166 logging.info(" Creating the data set for the {0} '{1}'.".
167 format(plot.get("type", ""), plot_title))
168 data = input_data.filter_tests_by_name(
169 plot, params=["throughput", "parent", "tags", "type"])
171 logging.error("No data.")
174 # Prepare the data for the plot
175 y_vals = OrderedDict()
179 if y_vals.get(test["parent"], None) is None:
180 y_vals[test["parent"]] = list()
182 if test["type"] in ("NDRPDR", ):
183 if "-pdr" in plot_title.lower():
184 y_vals[test["parent"]].\
185 append(test["throughput"]["PDR"]["LOWER"])
186 elif "-ndr" in plot_title.lower():
187 y_vals[test["parent"]]. \
188 append(test["throughput"]["NDR"]["LOWER"])
191 elif test["type"] in ("SOAK", ):
192 y_vals[test["parent"]].\
193 append(test["throughput"]["LOWER"])
196 except (KeyError, TypeError):
197 y_vals[test["parent"]].append(None)
199 # Add None to the lists with missing data
201 nr_of_samples = list()
202 for val in y_vals.values():
203 if len(val) > max_len:
205 nr_of_samples.append(len(val))
206 for key, val in y_vals.items():
207 if len(val) < max_len:
208 val.extend([None for _ in range(max_len - len(val))])
212 df = pd.DataFrame(y_vals)
215 for i, col in enumerate(df.columns):
216 tst_name = re.sub(REGEX_NIC, "",
217 col.lower().replace('-ndrpdr', '').
218 replace('2n1l-', ''))
219 name = "{nr}. ({samples:02d} run{plural}) {name}".\
221 samples=nr_of_samples[i],
222 plural='s' if nr_of_samples[i] > 1 else '',
226 traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
227 y=[y / 1000000 if y else None for y in df[col]],
230 boxpoints="outliers",
233 val_max = max(df[col])
234 except ValueError as err:
235 logging.error(repr(err))
238 y_max.append(int(val_max / 1000000) + 2)
242 layout = deepcopy(plot["layout"])
243 if layout.get("title", None):
244 layout["title"] = "<b>Throughput:</b> {0}". \
245 format(layout["title"])
247 layout["yaxis"]["range"] = [0, max(y_max)]
248 plpl = plgo.Figure(data=traces, layout=layout)
251 file_type = plot.get("output-file-type", ".html")
252 logging.info(" Writing file '{0}{1}'.".
253 format(plot["output-file"], file_type))
254 ploff.plot(plpl, show_link=False, auto_open=False,
255 filename='{0}{1}'.format(plot["output-file"], file_type))
256 except PlotlyError as err:
257 logging.error(" Finished with error: {}".
258 format(repr(err).replace("\n", " ")))
262 def plot_latency_error_bars_name(plot, input_data):
263 """Generate the plot(s) with algorithm: plot_latency_error_bars_name
264 specified in the specification file.
266 :param plot: Plot to generate.
267 :param input_data: Data to process.
268 :type plot: pandas.Series
269 :type input_data: InputData
273 plot_title = plot.get("title", "")
274 logging.info(" Creating the data set for the {0} '{1}'.".
275 format(plot.get("type", ""), plot_title))
276 data = input_data.filter_tests_by_name(
277 plot, params=["latency", "parent", "tags", "type"])
279 logging.error("No data.")
282 # Prepare the data for the plot
283 y_tmp_vals = OrderedDict()
288 logging.debug("test['latency']: {0}\n".
289 format(test["latency"]))
290 except ValueError as err:
291 logging.warning(repr(err))
292 if y_tmp_vals.get(test["parent"], None) is None:
293 y_tmp_vals[test["parent"]] = [
294 list(), # direction1, min
295 list(), # direction1, avg
296 list(), # direction1, max
297 list(), # direction2, min
298 list(), # direction2, avg
299 list() # direction2, max
302 if test["type"] in ("NDRPDR", ):
303 if "-pdr" in plot_title.lower():
305 elif "-ndr" in plot_title.lower():
308 logging.warning("Invalid test type: {0}".
309 format(test["type"]))
311 y_tmp_vals[test["parent"]][0].append(
312 test["latency"][ttype]["direction1"]["min"])
313 y_tmp_vals[test["parent"]][1].append(
314 test["latency"][ttype]["direction1"]["avg"])
315 y_tmp_vals[test["parent"]][2].append(
316 test["latency"][ttype]["direction1"]["max"])
317 y_tmp_vals[test["parent"]][3].append(
318 test["latency"][ttype]["direction2"]["min"])
319 y_tmp_vals[test["parent"]][4].append(
320 test["latency"][ttype]["direction2"]["avg"])
321 y_tmp_vals[test["parent"]][5].append(
322 test["latency"][ttype]["direction2"]["max"])
324 logging.warning("Invalid test type: {0}".
325 format(test["type"]))
327 except (KeyError, TypeError) as err:
328 logging.warning(repr(err))
334 nr_of_samples = list()
335 for key, val in y_tmp_vals.items():
336 name = re.sub(REGEX_NIC, "", key.replace('-ndrpdr', '').
337 replace('2n1l-', ''))
338 x_vals.append(name) # dir 1
339 y_vals.append(mean(val[1]) if val[1] else None)
340 y_mins.append(mean(val[0]) if val[0] else None)
341 y_maxs.append(mean(val[2]) if val[2] else None)
342 nr_of_samples.append(len(val[1]) if val[1] else 0)
343 x_vals.append(name) # dir 2
344 y_vals.append(mean(val[4]) if val[4] else None)
345 y_mins.append(mean(val[3]) if val[3] else None)
346 y_maxs.append(mean(val[5]) if val[5] else None)
347 nr_of_samples.append(len(val[3]) if val[3] else 0)
352 for idx in range(len(x_vals)):
353 if not bool(int(idx % 2)):
354 direction = "West-East"
356 direction = "East-West"
357 hovertext = ("No. of Runs: {nr}<br>"
359 "Direction: {dir}<br>".format(test=x_vals[idx],
361 nr=nr_of_samples[idx]))
362 if isinstance(y_maxs[idx], float):
363 hovertext += "Max: {max:.2f}uSec<br>".format(max=y_maxs[idx])
364 if isinstance(y_vals[idx], float):
365 hovertext += "Mean: {avg:.2f}uSec<br>".format(avg=y_vals[idx])
366 if isinstance(y_mins[idx], float):
367 hovertext += "Min: {min:.2f}uSec".format(min=y_mins[idx])
369 if isinstance(y_maxs[idx], float) and isinstance(y_vals[idx], float):
370 array = [y_maxs[idx] - y_vals[idx], ]
373 if isinstance(y_mins[idx], float) and isinstance(y_vals[idx], float):
374 arrayminus = [y_vals[idx] - y_mins[idx], ]
376 arrayminus = [None, ]
377 traces.append(plgo.Scatter(
381 legendgroup=x_vals[idx],
382 showlegend=bool(int(idx % 2)),
388 arrayminus=arrayminus,
389 color=COLORS[int(idx / 2)]
393 color=COLORS[int(idx / 2)],
398 annotations.append(dict(
405 text="E-W" if bool(int(idx % 2)) else "W-E",
415 file_type = plot.get("output-file-type", ".html")
416 logging.info(" Writing file '{0}{1}'.".
417 format(plot["output-file"], file_type))
418 layout = deepcopy(plot["layout"])
419 if layout.get("title", None):
420 layout["title"] = "<b>Latency:</b> {0}".\
421 format(layout["title"])
422 layout["annotations"] = annotations
423 plpl = plgo.Figure(data=traces, layout=layout)
427 show_link=False, auto_open=False,
428 filename='{0}{1}'.format(plot["output-file"], file_type))
429 except PlotlyError as err:
430 logging.error(" Finished with error: {}".
431 format(str(err).replace("\n", " ")))
435 def plot_throughput_speedup_analysis_name(plot, input_data):
436 """Generate the plot(s) with algorithm:
437 plot_throughput_speedup_analysis_name
438 specified in the specification file.
440 :param plot: Plot to generate.
441 :param input_data: Data to process.
442 :type plot: pandas.Series
443 :type input_data: InputData
447 plot_title = plot.get("title", "")
448 logging.info(" Creating the data set for the {0} '{1}'.".
449 format(plot.get("type", ""), plot_title))
450 data = input_data.filter_tests_by_name(
451 plot, params=["throughput", "parent", "tags", "type"])
453 logging.error("No data.")
456 y_vals = OrderedDict()
460 if y_vals.get(test["parent"], None) is None:
461 y_vals[test["parent"]] = {"1": list(),
465 if test["type"] in ("NDRPDR",):
466 if "-pdr" in plot_title.lower():
468 elif "-ndr" in plot_title.lower():
472 if "1C" in test["tags"]:
473 y_vals[test["parent"]]["1"]. \
474 append(test["throughput"][ttype]["LOWER"])
475 elif "2C" in test["tags"]:
476 y_vals[test["parent"]]["2"]. \
477 append(test["throughput"][ttype]["LOWER"])
478 elif "4C" in test["tags"]:
479 y_vals[test["parent"]]["4"]. \
480 append(test["throughput"][ttype]["LOWER"])
481 except (KeyError, TypeError):
485 logging.warning("No data for the plot '{}'".
486 format(plot.get("title", "")))
490 for test_name, test_vals in y_vals.items():
491 for key, test_val in test_vals.items():
493 avg_val = sum(test_val) / len(test_val)
494 y_vals[test_name][key] = (avg_val, len(test_val))
495 ideal = avg_val / (int(key) * 1000000.0)
496 if test_name not in y_1c_max or ideal > y_1c_max[test_name]:
497 y_1c_max[test_name] = ideal
503 pci_limit = plot["limits"]["pci"]["pci-g3-x8"]
504 for test_name, test_vals in y_vals.items():
506 if test_vals["1"][1]:
507 name = re.sub(REGEX_NIC, "", test_name.replace('-ndrpdr', '').
508 replace('2n1l-', ''))
509 vals[name] = OrderedDict()
510 y_val_1 = test_vals["1"][0] / 1000000.0
511 y_val_2 = test_vals["2"][0] / 1000000.0 if test_vals["2"][0] \
513 y_val_4 = test_vals["4"][0] / 1000000.0 if test_vals["4"][0] \
516 vals[name]["val"] = [y_val_1, y_val_2, y_val_4]
517 vals[name]["rel"] = [1.0, None, None]
518 vals[name]["ideal"] = [y_1c_max[test_name],
519 y_1c_max[test_name] * 2,
520 y_1c_max[test_name] * 4]
521 vals[name]["diff"] = [(y_val_1 - y_1c_max[test_name]) * 100 /
523 vals[name]["count"] = [test_vals["1"][1],
528 val_max = max(vals[name]["val"])
529 except ValueError as err:
530 logging.error(repr(err))
533 y_max.append(val_max)
536 vals[name]["rel"][1] = round(y_val_2 / y_val_1, 2)
537 vals[name]["diff"][1] = \
538 (y_val_2 - vals[name]["ideal"][1]) * 100 / y_val_2
540 vals[name]["rel"][2] = round(y_val_4 / y_val_1, 2)
541 vals[name]["diff"][2] = \
542 (y_val_4 - vals[name]["ideal"][2]) * 100 / y_val_4
543 except IndexError as err:
544 logging.warning("No data for '{0}'".format(test_name))
545 logging.warning(repr(err))
548 if "x520" in test_name:
549 limit = plot["limits"]["nic"]["x520"]
550 elif "x710" in test_name:
551 limit = plot["limits"]["nic"]["x710"]
552 elif "xxv710" in test_name:
553 limit = plot["limits"]["nic"]["xxv710"]
554 elif "xl710" in test_name:
555 limit = plot["limits"]["nic"]["xl710"]
556 elif "x553" in test_name:
557 limit = plot["limits"]["nic"]["x553"]
560 if limit > nic_limit:
563 mul = 2 if "ge2p" in test_name else 1
564 if "10ge" in test_name:
565 limit = plot["limits"]["link"]["10ge"] * mul
566 elif "25ge" in test_name:
567 limit = plot["limits"]["link"]["25ge"] * mul
568 elif "40ge" in test_name:
569 limit = plot["limits"]["link"]["40ge"] * mul
570 elif "100ge" in test_name:
571 limit = plot["limits"]["link"]["100ge"] * mul
574 if limit > lnk_limit:
583 threshold = 1.1 * max(y_max) # 10%
584 except ValueError as err:
587 nic_limit /= 1000000.0
588 traces.append(plgo.Scatter(
590 y=[nic_limit, ] * len(x_vals),
591 name="NIC: {0:.2f}Mpps".format(nic_limit),
600 annotations.append(dict(
607 text="NIC: {0:.2f}Mpps".format(nic_limit),
615 y_max.append(nic_limit)
617 lnk_limit /= 1000000.0
618 if lnk_limit < threshold:
619 traces.append(plgo.Scatter(
621 y=[lnk_limit, ] * len(x_vals),
622 name="Link: {0:.2f}Mpps".format(lnk_limit),
631 annotations.append(dict(
638 text="Link: {0:.2f}Mpps".format(lnk_limit),
646 y_max.append(lnk_limit)
648 pci_limit /= 1000000.0
649 if (pci_limit < threshold and
650 (pci_limit < lnk_limit * 0.95 or lnk_limit > lnk_limit * 1.05)):
651 traces.append(plgo.Scatter(
653 y=[pci_limit, ] * len(x_vals),
654 name="PCIe: {0:.2f}Mpps".format(pci_limit),
663 annotations.append(dict(
670 text="PCIe: {0:.2f}Mpps".format(pci_limit),
678 y_max.append(pci_limit)
680 # Perfect and measured:
682 for name, val in vals.iteritems():
685 for idx in range(len(val["val"])):
687 if isinstance(val["val"][idx], float):
688 htext += "No. of Runs: {1}<br>" \
689 "Mean: {0:.2f}Mpps<br>".format(val["val"][idx],
691 if isinstance(val["diff"][idx], float):
692 htext += "Diff: {0:.0f}%<br>".format(
693 round(val["diff"][idx]))
694 if isinstance(val["rel"][idx], float):
695 htext += "Speedup: {0:.2f}".format(val["rel"][idx])
696 hovertext.append(htext)
697 traces.append(plgo.Scatter(x=x_vals,
701 mode="lines+markers",
710 hoverinfo="text+name"
712 traces.append(plgo.Scatter(x=x_vals,
714 name="{0} perfect".format(name),
722 text=["Perfect: {0:.2f}Mpps".format(y)
723 for y in val["ideal"]],
727 except (IndexError, ValueError, KeyError) as err:
728 logging.warning("No data for '{0}'".format(name))
729 logging.warning(repr(err))
733 file_type = plot.get("output-file-type", ".html")
734 logging.info(" Writing file '{0}{1}'.".
735 format(plot["output-file"], file_type))
736 layout = deepcopy(plot["layout"])
737 if layout.get("title", None):
738 layout["title"] = "<b>Speedup Multi-core:</b> {0}". \
739 format(layout["title"])
740 layout["yaxis"]["range"] = [0, int(max(y_max) * 1.1)]
741 layout["annotations"].extend(annotations)
742 plpl = plgo.Figure(data=traces, layout=layout)
746 show_link=False, auto_open=False,
747 filename='{0}{1}'.format(plot["output-file"], file_type))
748 except PlotlyError as err:
749 logging.error(" Finished with error: {}".
750 format(repr(err).replace("\n", " ")))
754 def plot_performance_box(plot, input_data):
755 """Generate the plot(s) with algorithm: plot_performance_box
756 specified in the specification file.
758 TODO: Remove when not needed.
760 :param plot: Plot to generate.
761 :param input_data: Data to process.
762 :type plot: pandas.Series
763 :type input_data: InputData
767 plot_title = plot.get("title", "")
768 logging.info(" Creating the data set for the {0} '{1}'.".
769 format(plot.get("type", ""), plot_title))
770 data = input_data.filter_data(plot)
772 logging.error("No data.")
775 # Prepare the data for the plot
781 if y_vals.get(test["parent"], None) is None:
782 y_vals[test["parent"]] = list()
783 y_tags[test["parent"]] = test.get("tags", None)
785 if test["type"] in ("NDRPDR", ):
786 if "-pdr" in plot_title.lower():
787 y_vals[test["parent"]].\
788 append(test["throughput"]["PDR"]["LOWER"])
789 elif "-ndr" in plot_title.lower():
790 y_vals[test["parent"]]. \
791 append(test["throughput"]["NDR"]["LOWER"])
794 elif test["type"] in ("SOAK", ):
795 y_vals[test["parent"]].\
796 append(test["throughput"]["LOWER"])
799 except (KeyError, TypeError):
800 y_vals[test["parent"]].append(None)
803 order = plot.get("sort", None)
805 y_sorted = OrderedDict()
806 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
809 for suite, tags in y_tags_l.items():
811 tag = tag.split(" ")[-1]
812 if tag.lower() in tags:
815 if tag.lower() not in tags:
818 y_sorted[suite] = y_vals.pop(suite)
821 except KeyError as err:
822 logging.error("Not found: {0}".format(repr(err)))
828 # Add None to the lists with missing data
830 nr_of_samples = list()
831 for val in y_sorted.values():
832 if len(val) > max_len:
834 nr_of_samples.append(len(val))
835 for key, val in y_sorted.items():
836 if len(val) < max_len:
837 val.extend([None for _ in range(max_len - len(val))])
841 df = pd.DataFrame(y_sorted)
844 for i, col in enumerate(df.columns):
845 tst_name = re.sub(REGEX_NIC, "",
846 col.lower().replace('-ndrpdr', '').
847 replace('2n1l-', ''))
848 name = "{nr}. ({samples:02d} run{plural}) {name}".\
850 samples=nr_of_samples[i],
851 plural='s' if nr_of_samples[i] > 1 else '',
855 traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
856 y=[y / 1000000 if y else None for y in df[col]],
860 val_max = max(df[col])
861 except ValueError as err:
862 logging.error(repr(err))
865 y_max.append(int(val_max / 1000000) + 2)
869 layout = deepcopy(plot["layout"])
870 if layout.get("title", None):
871 layout["title"] = "<b>Throughput:</b> {0}". \
872 format(layout["title"])
874 layout["yaxis"]["range"] = [0, max(y_max)]
875 plpl = plgo.Figure(data=traces, layout=layout)
878 logging.info(" Writing file '{0}{1}'.".
879 format(plot["output-file"], plot["output-file-type"]))
880 ploff.plot(plpl, show_link=False, auto_open=False,
881 filename='{0}{1}'.format(plot["output-file"],
882 plot["output-file-type"]))
883 except PlotlyError as err:
884 logging.error(" Finished with error: {}".
885 format(repr(err).replace("\n", " ")))
889 def plot_soak_bars(plot, input_data):
890 """Generate the plot(s) with algorithm: plot_soak_bars
891 specified in the specification file.
893 :param plot: Plot to generate.
894 :param input_data: Data to process.
895 :type plot: pandas.Series
896 :type input_data: InputData
900 plot_title = plot.get("title", "")
901 logging.info(" Creating the data set for the {0} '{1}'.".
902 format(plot.get("type", ""), plot_title))
903 data = input_data.filter_data(plot)
905 logging.error("No data.")
908 # Prepare the data for the plot
914 if y_vals.get(test["parent"], None) is None:
915 y_tags[test["parent"]] = test.get("tags", None)
917 if test["type"] in ("SOAK", ):
918 y_vals[test["parent"]] = test["throughput"]
921 except (KeyError, TypeError):
922 y_vals[test["parent"]] = dict()
925 order = plot.get("sort", None)
927 y_sorted = OrderedDict()
928 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
931 for suite, tags in y_tags_l.items():
933 tag = tag.split(" ")[-1]
934 if tag.lower() in tags:
937 if tag.lower() not in tags:
940 y_sorted[suite] = y_vals.pop(suite)
943 except KeyError as err:
944 logging.error("Not found: {0}".format(repr(err)))
953 for test_name, test_data in y_sorted.items():
955 name = "{nr}. {name}".\
956 format(nr=idx, name=test_name.lower().replace('-soak', ''))
958 name_lst = name.split('-')
961 for segment in name_lst:
962 if (len(name) + len(segment) + 1) > 50 and split_name:
965 name += segment + '-'
968 y_val = test_data.get("LOWER", None)
974 time = "No Information"
975 result = "No Information"
976 hovertext = ("{name}<br>"
977 "Packet Throughput: {val:.2f}Mpps<br>"
978 "Final Duration: {time}<br>"
979 "Result: {result}".format(name=name,
983 traces.append(plgo.Bar(x=[str(idx) + '.', ],
990 layout = deepcopy(plot["layout"])
991 if layout.get("title", None):
992 layout["title"] = "<b>Packet Throughput:</b> {0}". \
993 format(layout["title"])
995 layout["yaxis"]["range"] = [0, y_max + 1]
996 plpl = plgo.Figure(data=traces, layout=layout)
998 logging.info(" Writing file '{0}{1}'.".
999 format(plot["output-file"], plot["output-file-type"]))
1000 ploff.plot(plpl, show_link=False, auto_open=False,
1001 filename='{0}{1}'.format(plot["output-file"],
1002 plot["output-file-type"]))
1003 except PlotlyError as err:
1004 logging.error(" Finished with error: {}".
1005 format(repr(err).replace("\n", " ")))
1009 def plot_soak_boxes(plot, input_data):
1010 """Generate the plot(s) with algorithm: plot_soak_boxes
1011 specified in the specification file.
1013 :param plot: Plot to generate.
1014 :param input_data: Data to process.
1015 :type plot: pandas.Series
1016 :type input_data: InputData
1019 # Transform the data
1020 plot_title = plot.get("title", "")
1021 logging.info(" Creating the data set for the {0} '{1}'.".
1022 format(plot.get("type", ""), plot_title))
1023 data = input_data.filter_data(plot)
1025 logging.error("No data.")
1028 # Prepare the data for the plot
1034 if y_vals.get(test["parent"], None) is None:
1035 y_tags[test["parent"]] = test.get("tags", None)
1037 if test["type"] in ("SOAK", ):
1038 y_vals[test["parent"]] = test["throughput"]
1041 except (KeyError, TypeError):
1042 y_vals[test["parent"]] = dict()
1045 order = plot.get("sort", None)
1046 if order and y_tags:
1047 y_sorted = OrderedDict()
1048 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
1051 for suite, tags in y_tags_l.items():
1053 tag = tag.split(" ")[-1]
1054 if tag.lower() in tags:
1057 if tag.lower() not in tags:
1060 y_sorted[suite] = y_vals.pop(suite)
1062 logging.debug(suite)
1063 except KeyError as err:
1064 logging.error("Not found: {0}".format(repr(err)))
1073 for test_name, test_data in y_sorted.items():
1075 name = "{nr}. {name}".\
1076 format(nr=idx, name=test_name.lower().replace('-soak', '').
1077 replace('2n1l-', ''))
1079 name_lst = name.split('-')
1082 for segment in name_lst:
1083 if (len(name) + len(segment) + 1) > 55 and split_name:
1086 name += segment + '-'
1089 y_val = test_data.get("UPPER", None)
1095 y_base = test_data.get("LOWER", None)
1099 hovertext = ("Upper bound: {upper:.2f}<br>"
1100 "Lower bound: {lower:.2f}".format(upper=y_val,
1102 traces.append(plgo.Bar(x=[str(idx) + '.', ],
1103 # +0.05 to see the value in case lower == upper
1104 y=[y_val - y_base + 0.05, ],
1111 layout = deepcopy(plot["layout"])
1112 if layout.get("title", None):
1113 layout["title"] = "<b>Throughput:</b> {0}". \
1114 format(layout["title"])
1116 layout["yaxis"]["range"] = [0, y_max + 1]
1117 plpl = plgo.Figure(data=traces, layout=layout)
1119 logging.info(" Writing file '{0}{1}'.".
1120 format(plot["output-file"], plot["output-file-type"]))
1121 ploff.plot(plpl, show_link=False, auto_open=False,
1122 filename='{0}{1}'.format(plot["output-file"],
1123 plot["output-file-type"]))
1124 except PlotlyError as err:
1125 logging.error(" Finished with error: {}".
1126 format(repr(err).replace("\n", " ")))
1130 def plot_latency_error_bars(plot, input_data):
1131 """Generate the plot(s) with algorithm: plot_latency_error_bars
1132 specified in the specification file.
1134 TODO: Remove when not needed.
1136 :param plot: Plot to generate.
1137 :param input_data: Data to process.
1138 :type plot: pandas.Series
1139 :type input_data: InputData
1142 # Transform the data
1143 plot_title = plot.get("title", "")
1144 logging.info(" Creating the data set for the {0} '{1}'.".
1145 format(plot.get("type", ""), plot_title))
1146 data = input_data.filter_data(plot)
1148 logging.error("No data.")
1151 # Prepare the data for the plot
1158 logging.debug("test['latency']: {0}\n".
1159 format(test["latency"]))
1160 except ValueError as err:
1161 logging.warning(repr(err))
1162 if y_tmp_vals.get(test["parent"], None) is None:
1163 y_tmp_vals[test["parent"]] = [
1164 list(), # direction1, min
1165 list(), # direction1, avg
1166 list(), # direction1, max
1167 list(), # direction2, min
1168 list(), # direction2, avg
1169 list() # direction2, max
1171 y_tags[test["parent"]] = test.get("tags", None)
1173 if test["type"] in ("NDRPDR", ):
1174 if "-pdr" in plot_title.lower():
1176 elif "-ndr" in plot_title.lower():
1179 logging.warning("Invalid test type: {0}".
1180 format(test["type"]))
1182 y_tmp_vals[test["parent"]][0].append(
1183 test["latency"][ttype]["direction1"]["min"])
1184 y_tmp_vals[test["parent"]][1].append(
1185 test["latency"][ttype]["direction1"]["avg"])
1186 y_tmp_vals[test["parent"]][2].append(
1187 test["latency"][ttype]["direction1"]["max"])
1188 y_tmp_vals[test["parent"]][3].append(
1189 test["latency"][ttype]["direction2"]["min"])
1190 y_tmp_vals[test["parent"]][4].append(
1191 test["latency"][ttype]["direction2"]["avg"])
1192 y_tmp_vals[test["parent"]][5].append(
1193 test["latency"][ttype]["direction2"]["max"])
1195 logging.warning("Invalid test type: {0}".
1196 format(test["type"]))
1198 except (KeyError, TypeError) as err:
1199 logging.warning(repr(err))
1200 logging.debug("y_tmp_vals: {0}\n".format(y_tmp_vals))
1203 order = plot.get("sort", None)
1204 if order and y_tags:
1205 y_sorted = OrderedDict()
1206 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
1209 for suite, tags in y_tags_l.items():
1211 tag = tag.split(" ")[-1]
1212 if tag.lower() in tags:
1215 if tag.lower() not in tags:
1218 y_sorted[suite] = y_tmp_vals.pop(suite)
1220 logging.debug(suite)
1221 except KeyError as err:
1222 logging.error("Not found: {0}".format(repr(err)))
1226 y_sorted = y_tmp_vals
1228 logging.debug("y_sorted: {0}\n".format(y_sorted))
1233 nr_of_samples = list()
1234 for key, val in y_sorted.items():
1235 name = re.sub(REGEX_NIC, "", key.replace('-ndrpdr', '').
1236 replace('2n1l-', ''))
1237 x_vals.append(name) # dir 1
1238 y_vals.append(mean(val[1]) if val[1] else None)
1239 y_mins.append(mean(val[0]) if val[0] else None)
1240 y_maxs.append(mean(val[2]) if val[2] else None)
1241 nr_of_samples.append(len(val[1]) if val[1] else 0)
1242 x_vals.append(name) # dir 2
1243 y_vals.append(mean(val[4]) if val[4] else None)
1244 y_mins.append(mean(val[3]) if val[3] else None)
1245 y_maxs.append(mean(val[5]) if val[5] else None)
1246 nr_of_samples.append(len(val[3]) if val[3] else 0)
1248 logging.debug("x_vals :{0}\n".format(x_vals))
1249 logging.debug("y_vals :{0}\n".format(y_vals))
1250 logging.debug("y_mins :{0}\n".format(y_mins))
1251 logging.debug("y_maxs :{0}\n".format(y_maxs))
1252 logging.debug("nr_of_samples :{0}\n".format(nr_of_samples))
1254 annotations = list()
1256 for idx in range(len(x_vals)):
1257 if not bool(int(idx % 2)):
1258 direction = "West-East"
1260 direction = "East-West"
1261 hovertext = ("No. of Runs: {nr}<br>"
1263 "Direction: {dir}<br>".format(test=x_vals[idx],
1265 nr=nr_of_samples[idx]))
1266 if isinstance(y_maxs[idx], float):
1267 hovertext += "Max: {max:.2f}uSec<br>".format(max=y_maxs[idx])
1268 if isinstance(y_vals[idx], float):
1269 hovertext += "Mean: {avg:.2f}uSec<br>".format(avg=y_vals[idx])
1270 if isinstance(y_mins[idx], float):
1271 hovertext += "Min: {min:.2f}uSec".format(min=y_mins[idx])
1273 if isinstance(y_maxs[idx], float) and isinstance(y_vals[idx], float):
1274 array = [y_maxs[idx] - y_vals[idx], ]
1277 if isinstance(y_mins[idx], float) and isinstance(y_vals[idx], float):
1278 arrayminus = [y_vals[idx] - y_mins[idx], ]
1280 arrayminus = [None, ]
1281 logging.debug("y_vals[{1}] :{0}\n".format(y_vals[idx], idx))
1282 logging.debug("array :{0}\n".format(array))
1283 logging.debug("arrayminus :{0}\n".format(arrayminus))
1284 traces.append(plgo.Scatter(
1288 legendgroup=x_vals[idx],
1289 showlegend=bool(int(idx % 2)),
1295 arrayminus=arrayminus,
1296 color=COLORS[int(idx / 2)]
1300 color=COLORS[int(idx / 2)],
1305 annotations.append(dict(
1312 text="E-W" if bool(int(idx % 2)) else "W-E",
1322 logging.info(" Writing file '{0}{1}'.".
1323 format(plot["output-file"], plot["output-file-type"]))
1324 layout = deepcopy(plot["layout"])
1325 if layout.get("title", None):
1326 layout["title"] = "<b>Latency:</b> {0}".\
1327 format(layout["title"])
1328 layout["annotations"] = annotations
1329 plpl = plgo.Figure(data=traces, layout=layout)
1333 show_link=False, auto_open=False,
1334 filename='{0}{1}'.format(plot["output-file"],
1335 plot["output-file-type"]))
1336 except PlotlyError as err:
1337 logging.error(" Finished with error: {}".
1338 format(str(err).replace("\n", " ")))
1342 def plot_throughput_speedup_analysis(plot, input_data):
1343 """Generate the plot(s) with algorithm:
1344 plot_throughput_speedup_analysis
1345 specified in the specification file.
1347 TODO: Remove when not needed.
1349 :param plot: Plot to generate.
1350 :param input_data: Data to process.
1351 :type plot: pandas.Series
1352 :type input_data: InputData
1355 # Transform the data
1356 plot_title = plot.get("title", "")
1357 logging.info(" Creating the data set for the {0} '{1}'.".
1358 format(plot.get("type", ""), plot_title))
1359 data = input_data.filter_data(plot)
1361 logging.error("No data.")
1369 if y_vals.get(test["parent"], None) is None:
1370 y_vals[test["parent"]] = {"1": list(),
1373 y_tags[test["parent"]] = test.get("tags", None)
1375 if test["type"] in ("NDRPDR",):
1376 if "-pdr" in plot_title.lower():
1378 elif "-ndr" in plot_title.lower():
1382 if "1C" in test["tags"]:
1383 y_vals[test["parent"]]["1"]. \
1384 append(test["throughput"][ttype]["LOWER"])
1385 elif "2C" in test["tags"]:
1386 y_vals[test["parent"]]["2"]. \
1387 append(test["throughput"][ttype]["LOWER"])
1388 elif "4C" in test["tags"]:
1389 y_vals[test["parent"]]["4"]. \
1390 append(test["throughput"][ttype]["LOWER"])
1391 except (KeyError, TypeError):
1395 logging.warning("No data for the plot '{}'".
1396 format(plot.get("title", "")))
1400 for test_name, test_vals in y_vals.items():
1401 for key, test_val in test_vals.items():
1403 avg_val = sum(test_val) / len(test_val)
1404 y_vals[test_name][key] = (avg_val, len(test_val))
1405 ideal = avg_val / (int(key) * 1000000.0)
1406 if test_name not in y_1c_max or ideal > y_1c_max[test_name]:
1407 y_1c_max[test_name] = ideal
1413 pci_limit = plot["limits"]["pci"]["pci-g3-x8"]
1414 for test_name, test_vals in y_vals.items():
1416 if test_vals["1"][1]:
1417 name = re.sub(REGEX_NIC, "", test_name.replace('-ndrpdr', '').
1418 replace('2n1l-', ''))
1420 y_val_1 = test_vals["1"][0] / 1000000.0
1421 y_val_2 = test_vals["2"][0] / 1000000.0 if test_vals["2"][0] \
1423 y_val_4 = test_vals["4"][0] / 1000000.0 if test_vals["4"][0] \
1426 vals[name]["val"] = [y_val_1, y_val_2, y_val_4]
1427 vals[name]["rel"] = [1.0, None, None]
1428 vals[name]["ideal"] = [y_1c_max[test_name],
1429 y_1c_max[test_name] * 2,
1430 y_1c_max[test_name] * 4]
1431 vals[name]["diff"] = [(y_val_1 - y_1c_max[test_name]) * 100 /
1432 y_val_1, None, None]
1433 vals[name]["count"] = [test_vals["1"][1],
1438 # val_max = max(max(vals[name]["val"], vals[name]["ideal"]))
1439 val_max = max(vals[name]["val"])
1440 except ValueError as err:
1444 # y_max.append(int((val_max / 10) + 1) * 10)
1445 y_max.append(val_max)
1448 vals[name]["rel"][1] = round(y_val_2 / y_val_1, 2)
1449 vals[name]["diff"][1] = \
1450 (y_val_2 - vals[name]["ideal"][1]) * 100 / y_val_2
1452 vals[name]["rel"][2] = round(y_val_4 / y_val_1, 2)
1453 vals[name]["diff"][2] = \
1454 (y_val_4 - vals[name]["ideal"][2]) * 100 / y_val_4
1455 except IndexError as err:
1456 logging.warning("No data for '{0}'".format(test_name))
1457 logging.warning(repr(err))
1460 if "x520" in test_name:
1461 limit = plot["limits"]["nic"]["x520"]
1462 elif "x710" in test_name:
1463 limit = plot["limits"]["nic"]["x710"]
1464 elif "xxv710" in test_name:
1465 limit = plot["limits"]["nic"]["xxv710"]
1466 elif "xl710" in test_name:
1467 limit = plot["limits"]["nic"]["xl710"]
1468 elif "x553" in test_name:
1469 limit = plot["limits"]["nic"]["x553"]
1472 if limit > nic_limit:
1475 mul = 2 if "ge2p" in test_name else 1
1476 if "10ge" in test_name:
1477 limit = plot["limits"]["link"]["10ge"] * mul
1478 elif "25ge" in test_name:
1479 limit = plot["limits"]["link"]["25ge"] * mul
1480 elif "40ge" in test_name:
1481 limit = plot["limits"]["link"]["40ge"] * mul
1482 elif "100ge" in test_name:
1483 limit = plot["limits"]["link"]["100ge"] * mul
1486 if limit > lnk_limit:
1490 order = plot.get("sort", None)
1491 if order and y_tags:
1492 y_sorted = OrderedDict()
1493 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
1495 for test, tags in y_tags_l.items():
1496 if tag.lower() in tags:
1497 name = re.sub(REGEX_NIC, "",
1498 test.replace('-ndrpdr', '').
1499 replace('2n1l-', ''))
1501 y_sorted[name] = vals.pop(name)
1503 except KeyError as err:
1504 logging.error("Not found: {0}".format(err))
1511 annotations = list()
1516 threshold = 1.1 * max(y_max) # 10%
1517 except ValueError as err:
1520 nic_limit /= 1000000.0
1521 # if nic_limit < threshold:
1522 traces.append(plgo.Scatter(
1524 y=[nic_limit, ] * len(x_vals),
1525 name="NIC: {0:.2f}Mpps".format(nic_limit),
1534 annotations.append(dict(
1541 text="NIC: {0:.2f}Mpps".format(nic_limit),
1549 # y_max.append(int((nic_limit / 10) + 1) * 10)
1550 y_max.append(nic_limit)
1552 lnk_limit /= 1000000.0
1553 if lnk_limit < threshold:
1554 traces.append(plgo.Scatter(
1556 y=[lnk_limit, ] * len(x_vals),
1557 name="Link: {0:.2f}Mpps".format(lnk_limit),
1566 annotations.append(dict(
1573 text="Link: {0:.2f}Mpps".format(lnk_limit),
1581 # y_max.append(int((lnk_limit / 10) + 1) * 10)
1582 y_max.append(lnk_limit)
1584 pci_limit /= 1000000.0
1585 if (pci_limit < threshold and
1586 (pci_limit < lnk_limit * 0.95 or lnk_limit > lnk_limit * 1.05)):
1587 traces.append(plgo.Scatter(
1589 y=[pci_limit, ] * len(x_vals),
1590 name="PCIe: {0:.2f}Mpps".format(pci_limit),
1599 annotations.append(dict(
1606 text="PCIe: {0:.2f}Mpps".format(pci_limit),
1614 # y_max.append(int((pci_limit / 10) + 1) * 10)
1615 y_max.append(pci_limit)
1617 # Perfect and measured:
1619 for name, val in y_sorted.iteritems():
1622 for idx in range(len(val["val"])):
1624 if isinstance(val["val"][idx], float):
1625 htext += "No. of Runs: {1}<br>" \
1626 "Mean: {0:.2f}Mpps<br>".format(val["val"][idx],
1628 if isinstance(val["diff"][idx], float):
1629 htext += "Diff: {0:.0f}%<br>".format(round(val["diff"][idx]))
1630 if isinstance(val["rel"][idx], float):
1631 htext += "Speedup: {0:.2f}".format(val["rel"][idx])
1632 hovertext.append(htext)
1633 traces.append(plgo.Scatter(x=x_vals,
1637 mode="lines+markers",
1646 hoverinfo="text+name"
1648 traces.append(plgo.Scatter(x=x_vals,
1650 name="{0} perfect".format(name),
1658 text=["Perfect: {0:.2f}Mpps".format(y)
1659 for y in val["ideal"]],
1663 except (IndexError, ValueError, KeyError) as err:
1664 logging.warning("No data for '{0}'".format(name))
1665 logging.warning(repr(err))
1669 logging.info(" Writing file '{0}{1}'.".
1670 format(plot["output-file"], plot["output-file-type"]))
1671 layout = deepcopy(plot["layout"])
1672 if layout.get("title", None):
1673 layout["title"] = "<b>Speedup Multi-core:</b> {0}". \
1674 format(layout["title"])
1675 # layout["yaxis"]["range"] = [0, int((max(y_max) / 10) + 1) * 10]
1676 layout["yaxis"]["range"] = [0, int(max(y_max) * 1.1)]
1677 layout["annotations"].extend(annotations)
1678 plpl = plgo.Figure(data=traces, layout=layout)
1682 show_link=False, auto_open=False,
1683 filename='{0}{1}'.format(plot["output-file"],
1684 plot["output-file-type"]))
1685 except PlotlyError as err:
1686 logging.error(" Finished with error: {}".
1687 format(str(err).replace("\n", " ")))
1691 def plot_http_server_performance_box(plot, input_data):
1692 """Generate the plot(s) with algorithm: plot_http_server_performance_box
1693 specified in the specification file.
1695 :param plot: Plot to generate.
1696 :param input_data: Data to process.
1697 :type plot: pandas.Series
1698 :type input_data: InputData
1701 # Transform the data
1702 logging.info(" Creating the data set for the {0} '{1}'.".
1703 format(plot.get("type", ""), plot.get("title", "")))
1704 data = input_data.filter_data(plot)
1706 logging.error("No data.")
1709 # Prepare the data for the plot
1714 if y_vals.get(test["name"], None) is None:
1715 y_vals[test["name"]] = list()
1717 y_vals[test["name"]].append(test["result"])
1718 except (KeyError, TypeError):
1719 y_vals[test["name"]].append(None)
1721 # Add None to the lists with missing data
1723 nr_of_samples = list()
1724 for val in y_vals.values():
1725 if len(val) > max_len:
1727 nr_of_samples.append(len(val))
1728 for key, val in y_vals.items():
1729 if len(val) < max_len:
1730 val.extend([None for _ in range(max_len - len(val))])
1734 df = pd.DataFrame(y_vals)
1736 for i, col in enumerate(df.columns):
1737 name = "{nr}. ({samples:02d} run{plural}) {name}".\
1739 samples=nr_of_samples[i],
1740 plural='s' if nr_of_samples[i] > 1 else '',
1741 name=col.lower().replace('-ndrpdr', ''))
1743 name_lst = name.split('-')
1746 for segment in name_lst:
1747 if (len(name) + len(segment) + 1) > 50 and split_name:
1750 name += segment + '-'
1753 traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
1759 plpl = plgo.Figure(data=traces, layout=plot["layout"])
1762 logging.info(" Writing file '{0}{1}'.".
1763 format(plot["output-file"], plot["output-file-type"]))
1764 ploff.plot(plpl, show_link=False, auto_open=False,
1765 filename='{0}{1}'.format(plot["output-file"],
1766 plot["output-file-type"]))
1767 except PlotlyError as err:
1768 logging.error(" Finished with error: {}".
1769 format(str(err).replace("\n", " ")))
1773 def plot_service_density_heatmap(plot, input_data):
1774 """Generate the plot(s) with algorithm: plot_service_density_heatmap
1775 specified in the specification file.
1777 :param plot: Plot to generate.
1778 :param input_data: Data to process.
1779 :type plot: pandas.Series
1780 :type input_data: InputData
1783 REGEX_CN = re.compile(r'^(\d*)R(\d*)C$')
1784 REGEX_TEST_NAME = re.compile(r'^.*-(\d+ch|\d+pl)-'
1786 r'(\d+vm\d+t|\d+dcr\d+t).*$')
1792 # Transform the data
1793 logging.info(" Creating the data set for the {0} '{1}'.".
1794 format(plot.get("type", ""), plot.get("title", "")))
1795 data = input_data.filter_data(plot, continue_on_error=True)
1796 if data is None or data.empty:
1797 logging.error("No data.")
1803 for tag in test['tags']:
1804 groups = re.search(REGEX_CN, tag)
1806 c = str(groups.group(1))
1807 n = str(groups.group(2))
1811 groups = re.search(REGEX_TEST_NAME, test["name"])
1812 if groups and len(groups.groups()) == 3:
1813 hover_name = "{chain}-{vhost}-{vm}".format(
1814 chain=str(groups.group(1)),
1815 vhost=str(groups.group(2)),
1816 vm=str(groups.group(3)))
1819 if vals.get(c, None) is None:
1821 if vals[c].get(n, None) is None:
1822 vals[c][n] = dict(name=hover_name,
1828 if plot["include-tests"] == "MRR":
1829 result = test["result"]["receive-rate"].avg
1830 elif plot["include-tests"] == "PDR":
1831 result = test["throughput"]["PDR"]["LOWER"]
1832 elif plot["include-tests"] == "NDR":
1833 result = test["throughput"]["NDR"]["LOWER"]
1840 vals[c][n]["vals"].append(result)
1843 logging.error("No data.")
1846 for key_c in vals.keys():
1847 txt_chains.append(key_c)
1848 for key_n in vals[key_c].keys():
1849 txt_nodes.append(key_n)
1850 if vals[key_c][key_n]["vals"]:
1851 vals[key_c][key_n]["nr"] = len(vals[key_c][key_n]["vals"])
1852 vals[key_c][key_n]["mean"] = \
1853 round(mean(vals[key_c][key_n]["vals"]) / 1000000, 1)
1854 vals[key_c][key_n]["stdev"] = \
1855 round(stdev(vals[key_c][key_n]["vals"]) / 1000000, 1)
1856 txt_nodes = list(set(txt_nodes))
1858 txt_chains = sorted(txt_chains, key=lambda chain: int(chain))
1859 txt_nodes = sorted(txt_nodes, key=lambda node: int(node))
1861 chains = [i + 1 for i in range(len(txt_chains))]
1862 nodes = [i + 1 for i in range(len(txt_nodes))]
1864 data = [list() for _ in range(len(chains))]
1868 val = vals[txt_chains[c - 1]][txt_nodes[n - 1]]["mean"]
1869 except (KeyError, IndexError):
1871 data[c - 1].append(val)
1874 my_green = [[0.0, 'rgb(235, 249, 242)'],
1875 [1.0, 'rgb(45, 134, 89)']]
1877 my_blue = [[0.0, 'rgb(236, 242, 248)'],
1878 [1.0, 'rgb(57, 115, 172)']]
1880 my_grey = [[0.0, 'rgb(230, 230, 230)'],
1881 [1.0, 'rgb(102, 102, 102)']]
1884 annotations = list()
1886 text = ("Test: {name}<br>"
1891 for c in range(len(txt_chains)):
1893 for n in range(len(txt_nodes)):
1894 if data[c][n] is not None:
1895 annotations.append(dict(
1902 text=str(data[c][n]),
1909 hover_line.append(text.format(
1910 name=vals[txt_chains[c]][txt_nodes[n]]["name"],
1911 nr=vals[txt_chains[c]][txt_nodes[n]]["nr"],
1913 stdev=vals[txt_chains[c]][txt_nodes[n]]["stdev"]))
1914 hovertext.append(hover_line)
1917 plgo.Heatmap(x=nodes,
1921 title=plot.get("z-axis", ""),
1935 colorscale=my_green,
1940 for idx, item in enumerate(txt_nodes):
1942 annotations.append(dict(
1956 for idx, item in enumerate(txt_chains):
1958 annotations.append(dict(
1973 annotations.append(dict(
1980 text=plot.get("x-axis", ""),
1988 annotations.append(dict(
1995 text=plot.get("y-axis", ""),
2003 updatemenus = list([
2012 args=[{"colorscale": [my_green, ], "reversescale": False}],
2017 args=[{"colorscale": [my_blue, ], "reversescale": False}],
2022 args=[{"colorscale": [my_grey, ], "reversescale": False}],
2031 layout = deepcopy(plot["layout"])
2032 except KeyError as err:
2033 logging.error("Finished with error: No layout defined")
2034 logging.error(repr(err))
2037 layout["annotations"] = annotations
2038 layout['updatemenus'] = updatemenus
2042 plpl = plgo.Figure(data=traces, layout=layout)
2045 logging.info(" Writing file '{0}{1}'.".
2046 format(plot["output-file"], plot["output-file-type"]))
2047 ploff.plot(plpl, show_link=False, auto_open=False,
2048 filename='{0}{1}'.format(plot["output-file"],
2049 plot["output-file-type"]))
2050 except PlotlyError as err:
2051 logging.error(" Finished with error: {}".
2052 format(str(err).replace("\n", " ")))
2056 def plot_service_density_heatmap_compare(plot, input_data):
2057 """Generate the plot(s) with algorithm: plot_service_density_heatmap_compare
2058 specified in the specification file.
2060 :param plot: Plot to generate.
2061 :param input_data: Data to process.
2062 :type plot: pandas.Series
2063 :type input_data: InputData
2066 REGEX_CN = re.compile(r'^(\d*)R(\d*)C$')
2067 REGEX_TEST_NAME = re.compile(r'^.*-(\d+ch|\d+pl)-'
2069 r'(\d+vm\d+t|\d+dcr\d+t).*$')
2070 REGEX_THREADS = re.compile(r'^(\d+)(VM|DCR)(\d+)T$')
2076 # Transform the data
2077 logging.info(" Creating the data set for the {0} '{1}'.".
2078 format(plot.get("type", ""), plot.get("title", "")))
2079 data = input_data.filter_data(plot, continue_on_error=True)
2080 if data is None or data.empty:
2081 logging.error("No data.")
2087 for tag in test['tags']:
2088 groups = re.search(REGEX_CN, tag)
2090 c = str(groups.group(1))
2091 n = str(groups.group(2))
2095 groups = re.search(REGEX_TEST_NAME, test["name"])
2096 if groups and len(groups.groups()) == 3:
2097 hover_name = "{chain}-{vhost}-{vm}".format(
2098 chain=str(groups.group(1)),
2099 vhost=str(groups.group(2)),
2100 vm=str(groups.group(3)))
2103 if vals.get(c, None) is None:
2105 if vals[c].get(n, None) is None:
2106 vals[c][n] = dict(name=hover_name,
2116 if plot["include-tests"] == "MRR":
2117 result = test["result"]["receive-rate"].avg
2118 elif plot["include-tests"] == "PDR":
2119 result = test["throughput"]["PDR"]["LOWER"]
2120 elif plot["include-tests"] == "NDR":
2121 result = test["throughput"]["NDR"]["LOWER"]
2128 for tag in test['tags']:
2129 groups = re.search(REGEX_THREADS, tag)
2130 if groups and len(groups.groups()) == 3:
2131 if str(groups.group(3)) == \
2132 plot["reference"]["include"]:
2133 vals[c][n]["vals_r"].append(result)
2134 elif str(groups.group(3)) == \
2135 plot["compare"]["include"]:
2136 vals[c][n]["vals_c"].append(result)
2139 logging.error("No data.")
2142 for key_c in vals.keys():
2143 txt_chains.append(key_c)
2144 for key_n in vals[key_c].keys():
2145 txt_nodes.append(key_n)
2146 if vals[key_c][key_n]["vals_r"]:
2147 vals[key_c][key_n]["nr_r"] = len(vals[key_c][key_n]["vals_r"])
2148 vals[key_c][key_n]["mean_r"] = \
2149 mean(vals[key_c][key_n]["vals_r"])
2150 vals[key_c][key_n]["stdev_r"] = \
2151 round(stdev(vals[key_c][key_n]["vals_r"]) / 1000000, 1)
2152 if vals[key_c][key_n]["vals_c"]:
2153 vals[key_c][key_n]["nr_c"] = len(vals[key_c][key_n]["vals_c"])
2154 vals[key_c][key_n]["mean_c"] = \
2155 mean(vals[key_c][key_n]["vals_c"])
2156 vals[key_c][key_n]["stdev_c"] = \
2157 round(stdev(vals[key_c][key_n]["vals_c"]) / 1000000, 1)
2159 txt_nodes = list(set(txt_nodes))
2161 txt_chains = sorted(txt_chains, key=lambda chain: int(chain))
2162 txt_nodes = sorted(txt_nodes, key=lambda node: int(node))
2164 chains = [i + 1 for i in range(len(txt_chains))]
2165 nodes = [i + 1 for i in range(len(txt_nodes))]
2167 data_r = [list() for _ in range(len(chains))]
2168 data_c = [list() for _ in range(len(chains))]
2169 diff = [list() for _ in range(len(chains))]
2173 val_r = vals[txt_chains[c - 1]][txt_nodes[n - 1]]["mean_r"]
2174 except (KeyError, IndexError):
2177 val_c = vals[txt_chains[c - 1]][txt_nodes[n - 1]]["mean_c"]
2178 except (KeyError, IndexError):
2180 if val_c is not None and val_r:
2181 val_d = (val_c - val_r) * 100 / val_r
2185 if val_r is not None:
2186 val_r = round(val_r / 1000000, 1)
2187 data_r[c - 1].append(val_r)
2188 if val_c is not None:
2189 val_c = round(val_c / 1000000, 1)
2190 data_c[c - 1].append(val_c)
2191 if val_d is not None:
2192 val_d = int(round(val_d, 0))
2193 diff[c - 1].append(val_d)
2196 my_green = [[0.0, 'rgb(235, 249, 242)'],
2197 [1.0, 'rgb(45, 134, 89)']]
2199 my_blue = [[0.0, 'rgb(236, 242, 248)'],
2200 [1.0, 'rgb(57, 115, 172)']]
2202 my_grey = [[0.0, 'rgb(230, 230, 230)'],
2203 [1.0, 'rgb(102, 102, 102)']]
2207 annotations = list()
2208 annotations_r = list()
2209 annotations_c = list()
2210 annotations_diff = list()
2212 text = ("Test: {name}"
2213 "<br>{title_r}: {text_r}"
2214 "<br>{title_c}: {text_c}{text_diff}")
2215 text_r = "Thput: {val_r}; StDev: {stdev_r}; Runs: {nr_r}"
2216 text_c = "Thput: {val_c}; StDev: {stdev_c}; Runs: {nr_c}"
2217 text_diff = "<br>Relative Difference {title_c} vs. {title_r}: {diff}%"
2219 for c in range(len(txt_chains)):
2221 for n in range(len(txt_nodes)):
2237 point_text_r = "Not present"
2238 point_text_c = "Not present"
2239 point_text_diff = ""
2241 point_r = data_r[c][n]
2242 if point_r is not None:
2243 point_text_r = text_r.format(
2245 stdev_r=vals[txt_chains[c]][txt_nodes[n]]["stdev_r"],
2246 nr_r=vals[txt_chains[c]][txt_nodes[n]]["nr_r"])
2249 point["text"] = "" if point_r is None else point_r
2250 annotations_r.append(deepcopy(point))
2253 point_c = data_c[c][n]
2254 if point_c is not None:
2255 point_text_c = text_c.format(
2257 stdev_c=vals[txt_chains[c]][txt_nodes[n]]["stdev_c"],
2258 nr_c=vals[txt_chains[c]][txt_nodes[n]]["nr_c"])
2261 point["text"] = "" if point_c is None else point_c
2262 annotations_c.append(deepcopy(point))
2265 point_d = diff[c][n]
2266 if point_d is not None:
2267 point_text_diff = text_diff.format(
2268 title_r=plot["reference"]["name"],
2269 title_c=plot["compare"]["name"],
2273 point["text"] = "" if point_d is None else point_d
2274 annotations_diff.append(deepcopy(point))
2277 name = vals[txt_chains[c]][txt_nodes[n]]["name"]
2281 hover_line.append(text.format(
2283 title_r=plot["reference"]["name"],
2284 text_r=point_text_r,
2285 title_c=plot["compare"]["name"],
2286 text_c=point_text_c,
2287 text_diff=point_text_diff
2290 hovertext.append(hover_line)
2293 plgo.Heatmap(x=nodes,
2298 title=plot.get("z-axis", ""),
2312 colorscale=my_green,
2316 plgo.Heatmap(x=nodes,
2321 title=plot.get("z-axis", ""),
2339 plgo.Heatmap(x=nodes,
2345 title="Relative Difference {name_c} vs. {name_r} [%]".
2346 format(name_c=plot["compare"]["name"],
2347 name_r=plot["reference"]["name"]),
2367 for idx, item in enumerate(txt_nodes):
2369 annotations.append(dict(
2383 for idx, item in enumerate(txt_chains):
2385 annotations.append(dict(
2400 annotations.append(dict(
2407 text=plot.get("x-axis", ""),
2415 annotations.append(dict(
2422 text=plot.get("y-axis", ""),
2430 updatemenus = list([
2440 label=plot["reference"]["name"],
2444 "visible": [True, False, False]
2447 "colorscale": [my_green, ],
2448 "reversescale": False,
2449 "annotations": annotations + annotations_r,
2454 label=plot["compare"]["name"],
2458 "visible": [False, True, False]
2461 "colorscale": [my_blue, ],
2462 "reversescale": False,
2463 "annotations": annotations + annotations_c,
2472 "visible": [False, False, True]
2475 "colorscale": [my_grey, ],
2476 "reversescale": False,
2477 "annotations": annotations + annotations_diff,
2486 layout = deepcopy(plot["layout"])
2487 except KeyError as err:
2488 logging.error("Finished with error: No layout defined")
2489 logging.error(repr(err))
2492 layout["annotations"] = annotations + annotations_r
2493 layout['updatemenus'] = updatemenus
2497 plpl = plgo.Figure(data=traces, layout=layout)
2500 logging.info(" Writing file '{0}{1}'.".
2501 format(plot["output-file"], plot["output-file-type"]))
2502 ploff.plot(plpl, show_link=False, auto_open=False,
2503 filename='{0}{1}'.format(plot["output-file"],
2504 plot["output-file-type"]))
2505 except PlotlyError as err:
2506 logging.error(" Finished with error: {}".
2507 format(str(err).replace("\n", " ")))