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]],
133 layout = deepcopy(plot["layout"])
134 layout["title"] = "<b>Time Lost:</b> {0}".format(layout["title"])
135 layout["yaxis"]["title"] = "<b>Implied Time Lost [s]</b>"
136 layout["legend"]["font"]["size"] = 14
137 layout["yaxis"].pop("range")
138 plpl = plgo.Figure(data=traces, layout=layout)
141 file_type = plot.get("output-file-type", ".html")
142 logging.info(" Writing file '{0}{1}'.".
143 format(plot["output-file"], file_type))
144 ploff.plot(plpl, show_link=False, auto_open=False,
145 filename='{0}{1}'.format(plot["output-file"], file_type))
146 except PlotlyError as err:
147 logging.error(" Finished with error: {}".
148 format(repr(err).replace("\n", " ")))
152 def plot_performance_box_name(plot, input_data):
153 """Generate the plot(s) with algorithm: plot_performance_box_name
154 specified in the specification file.
156 :param plot: Plot to generate.
157 :param input_data: Data to process.
158 :type plot: pandas.Series
159 :type input_data: InputData
163 plot_title = plot.get("title", "")
164 logging.info(" Creating the data set for the {0} '{1}'.".
165 format(plot.get("type", ""), plot_title))
166 data = input_data.filter_tests_by_name(
167 plot, params=["throughput", "parent", "tags", "type"])
169 logging.error("No data.")
172 # Prepare the data for the plot
173 y_vals = OrderedDict()
177 if y_vals.get(test["parent"], None) is None:
178 y_vals[test["parent"]] = list()
180 if test["type"] in ("NDRPDR", ):
181 if "-pdr" in plot_title.lower():
182 y_vals[test["parent"]].\
183 append(test["throughput"]["PDR"]["LOWER"])
184 elif "-ndr" in plot_title.lower():
185 y_vals[test["parent"]]. \
186 append(test["throughput"]["NDR"]["LOWER"])
189 elif test["type"] in ("SOAK", ):
190 y_vals[test["parent"]].\
191 append(test["throughput"]["LOWER"])
194 except (KeyError, TypeError):
195 y_vals[test["parent"]].append(None)
197 # Add None to the lists with missing data
199 nr_of_samples = list()
200 for val in y_vals.values():
201 if len(val) > max_len:
203 nr_of_samples.append(len(val))
204 for key, val in y_vals.items():
205 if len(val) < max_len:
206 val.extend([None for _ in range(max_len - len(val))])
210 df = pd.DataFrame(y_vals)
213 for i, col in enumerate(df.columns):
214 tst_name = re.sub(REGEX_NIC, "",
215 col.lower().replace('-ndrpdr', '').
216 replace('2n1l-', ''))
217 name = "{nr}. ({samples:02d} run{plural}) {name}".\
219 samples=nr_of_samples[i],
220 plural='s' if nr_of_samples[i] > 1 else '',
224 traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
225 y=[y / 1000000 if y else None for y in df[col]],
229 val_max = max(df[col])
230 except ValueError as err:
231 logging.error(repr(err))
234 y_max.append(int(val_max / 1000000) + 2)
238 layout = deepcopy(plot["layout"])
239 if layout.get("title", None):
240 layout["title"] = "<b>Throughput:</b> {0}". \
241 format(layout["title"])
243 layout["yaxis"]["range"] = [0, max(y_max)]
244 plpl = plgo.Figure(data=traces, layout=layout)
247 file_type = plot.get("output-file-type", ".html")
248 logging.info(" Writing file '{0}{1}'.".
249 format(plot["output-file"], file_type))
250 ploff.plot(plpl, show_link=False, auto_open=False,
251 filename='{0}{1}'.format(plot["output-file"], file_type))
252 except PlotlyError as err:
253 logging.error(" Finished with error: {}".
254 format(repr(err).replace("\n", " ")))
258 def plot_latency_error_bars_name(plot, input_data):
259 """Generate the plot(s) with algorithm: plot_latency_error_bars_name
260 specified in the specification file.
262 :param plot: Plot to generate.
263 :param input_data: Data to process.
264 :type plot: pandas.Series
265 :type input_data: InputData
269 plot_title = plot.get("title", "")
270 logging.info(" Creating the data set for the {0} '{1}'.".
271 format(plot.get("type", ""), plot_title))
272 data = input_data.filter_tests_by_name(
273 plot, params=["latency", "parent", "tags", "type"])
275 logging.error("No data.")
278 # Prepare the data for the plot
279 y_tmp_vals = OrderedDict()
284 logging.debug("test['latency']: {0}\n".
285 format(test["latency"]))
286 except ValueError as err:
287 logging.warning(repr(err))
288 if y_tmp_vals.get(test["parent"], None) is None:
289 y_tmp_vals[test["parent"]] = [
290 list(), # direction1, min
291 list(), # direction1, avg
292 list(), # direction1, max
293 list(), # direction2, min
294 list(), # direction2, avg
295 list() # direction2, max
298 if test["type"] in ("NDRPDR", ):
299 if "-pdr" in plot_title.lower():
301 elif "-ndr" in plot_title.lower():
304 logging.warning("Invalid test type: {0}".
305 format(test["type"]))
307 y_tmp_vals[test["parent"]][0].append(
308 test["latency"][ttype]["direction1"]["min"])
309 y_tmp_vals[test["parent"]][1].append(
310 test["latency"][ttype]["direction1"]["avg"])
311 y_tmp_vals[test["parent"]][2].append(
312 test["latency"][ttype]["direction1"]["max"])
313 y_tmp_vals[test["parent"]][3].append(
314 test["latency"][ttype]["direction2"]["min"])
315 y_tmp_vals[test["parent"]][4].append(
316 test["latency"][ttype]["direction2"]["avg"])
317 y_tmp_vals[test["parent"]][5].append(
318 test["latency"][ttype]["direction2"]["max"])
320 logging.warning("Invalid test type: {0}".
321 format(test["type"]))
323 except (KeyError, TypeError) as err:
324 logging.warning(repr(err))
330 nr_of_samples = list()
331 for key, val in y_tmp_vals.items():
332 name = re.sub(REGEX_NIC, "", key.replace('-ndrpdr', '').
333 replace('2n1l-', ''))
334 x_vals.append(name) # dir 1
335 y_vals.append(mean(val[1]) if val[1] else None)
336 y_mins.append(mean(val[0]) if val[0] else None)
337 y_maxs.append(mean(val[2]) if val[2] else None)
338 nr_of_samples.append(len(val[1]) if val[1] else 0)
339 x_vals.append(name) # dir 2
340 y_vals.append(mean(val[4]) if val[4] else None)
341 y_mins.append(mean(val[3]) if val[3] else None)
342 y_maxs.append(mean(val[5]) if val[5] else None)
343 nr_of_samples.append(len(val[3]) if val[3] else 0)
348 for idx in range(len(x_vals)):
349 if not bool(int(idx % 2)):
350 direction = "West-East"
352 direction = "East-West"
353 hovertext = ("No. of Runs: {nr}<br>"
355 "Direction: {dir}<br>".format(test=x_vals[idx],
357 nr=nr_of_samples[idx]))
358 if isinstance(y_maxs[idx], float):
359 hovertext += "Max: {max:.2f}uSec<br>".format(max=y_maxs[idx])
360 if isinstance(y_vals[idx], float):
361 hovertext += "Mean: {avg:.2f}uSec<br>".format(avg=y_vals[idx])
362 if isinstance(y_mins[idx], float):
363 hovertext += "Min: {min:.2f}uSec".format(min=y_mins[idx])
365 if isinstance(y_maxs[idx], float) and isinstance(y_vals[idx], float):
366 array = [y_maxs[idx] - y_vals[idx], ]
369 if isinstance(y_mins[idx], float) and isinstance(y_vals[idx], float):
370 arrayminus = [y_vals[idx] - y_mins[idx], ]
372 arrayminus = [None, ]
373 traces.append(plgo.Scatter(
377 legendgroup=x_vals[idx],
378 showlegend=bool(int(idx % 2)),
384 arrayminus=arrayminus,
385 color=COLORS[int(idx / 2)]
389 color=COLORS[int(idx / 2)],
394 annotations.append(dict(
401 text="E-W" if bool(int(idx % 2)) else "W-E",
411 file_type = plot.get("output-file-type", ".html")
412 logging.info(" Writing file '{0}{1}'.".
413 format(plot["output-file"], file_type))
414 layout = deepcopy(plot["layout"])
415 if layout.get("title", None):
416 layout["title"] = "<b>Latency:</b> {0}".\
417 format(layout["title"])
418 layout["annotations"] = annotations
419 plpl = plgo.Figure(data=traces, layout=layout)
423 show_link=False, auto_open=False,
424 filename='{0}{1}'.format(plot["output-file"], file_type))
425 except PlotlyError as err:
426 logging.error(" Finished with error: {}".
427 format(str(err).replace("\n", " ")))
431 def plot_throughput_speedup_analysis_name(plot, input_data):
432 """Generate the plot(s) with algorithm:
433 plot_throughput_speedup_analysis_name
434 specified in the specification file.
436 :param plot: Plot to generate.
437 :param input_data: Data to process.
438 :type plot: pandas.Series
439 :type input_data: InputData
443 plot_title = plot.get("title", "")
444 logging.info(" Creating the data set for the {0} '{1}'.".
445 format(plot.get("type", ""), plot_title))
446 data = input_data.filter_tests_by_name(
447 plot, params=["throughput", "parent", "tags", "type"])
449 logging.error("No data.")
452 y_vals = OrderedDict()
456 if y_vals.get(test["parent"], None) is None:
457 y_vals[test["parent"]] = {"1": list(),
461 if test["type"] in ("NDRPDR",):
462 if "-pdr" in plot_title.lower():
464 elif "-ndr" in plot_title.lower():
468 if "1C" in test["tags"]:
469 y_vals[test["parent"]]["1"]. \
470 append(test["throughput"][ttype]["LOWER"])
471 elif "2C" in test["tags"]:
472 y_vals[test["parent"]]["2"]. \
473 append(test["throughput"][ttype]["LOWER"])
474 elif "4C" in test["tags"]:
475 y_vals[test["parent"]]["4"]. \
476 append(test["throughput"][ttype]["LOWER"])
477 except (KeyError, TypeError):
481 logging.warning("No data for the plot '{}'".
482 format(plot.get("title", "")))
486 for test_name, test_vals in y_vals.items():
487 for key, test_val in test_vals.items():
489 avg_val = sum(test_val) / len(test_val)
490 y_vals[test_name][key] = (avg_val, len(test_val))
491 ideal = avg_val / (int(key) * 1000000.0)
492 if test_name not in y_1c_max or ideal > y_1c_max[test_name]:
493 y_1c_max[test_name] = ideal
499 pci_limit = plot["limits"]["pci"]["pci-g3-x8"]
500 for test_name, test_vals in y_vals.items():
502 if test_vals["1"][1]:
503 name = re.sub(REGEX_NIC, "", test_name.replace('-ndrpdr', '').
504 replace('2n1l-', ''))
505 vals[name] = OrderedDict()
506 y_val_1 = test_vals["1"][0] / 1000000.0
507 y_val_2 = test_vals["2"][0] / 1000000.0 if test_vals["2"][0] \
509 y_val_4 = test_vals["4"][0] / 1000000.0 if test_vals["4"][0] \
512 vals[name]["val"] = [y_val_1, y_val_2, y_val_4]
513 vals[name]["rel"] = [1.0, None, None]
514 vals[name]["ideal"] = [y_1c_max[test_name],
515 y_1c_max[test_name] * 2,
516 y_1c_max[test_name] * 4]
517 vals[name]["diff"] = [(y_val_1 - y_1c_max[test_name]) * 100 /
519 vals[name]["count"] = [test_vals["1"][1],
524 val_max = max(vals[name]["val"])
525 except ValueError as err:
526 logging.error(repr(err))
529 y_max.append(val_max)
532 vals[name]["rel"][1] = round(y_val_2 / y_val_1, 2)
533 vals[name]["diff"][1] = \
534 (y_val_2 - vals[name]["ideal"][1]) * 100 / y_val_2
536 vals[name]["rel"][2] = round(y_val_4 / y_val_1, 2)
537 vals[name]["diff"][2] = \
538 (y_val_4 - vals[name]["ideal"][2]) * 100 / y_val_4
539 except IndexError as err:
540 logging.warning("No data for '{0}'".format(test_name))
541 logging.warning(repr(err))
544 if "x520" in test_name:
545 limit = plot["limits"]["nic"]["x520"]
546 elif "x710" in test_name:
547 limit = plot["limits"]["nic"]["x710"]
548 elif "xxv710" in test_name:
549 limit = plot["limits"]["nic"]["xxv710"]
550 elif "xl710" in test_name:
551 limit = plot["limits"]["nic"]["xl710"]
552 elif "x553" in test_name:
553 limit = plot["limits"]["nic"]["x553"]
556 if limit > nic_limit:
559 mul = 2 if "ge2p" in test_name else 1
560 if "10ge" in test_name:
561 limit = plot["limits"]["link"]["10ge"] * mul
562 elif "25ge" in test_name:
563 limit = plot["limits"]["link"]["25ge"] * mul
564 elif "40ge" in test_name:
565 limit = plot["limits"]["link"]["40ge"] * mul
566 elif "100ge" in test_name:
567 limit = plot["limits"]["link"]["100ge"] * mul
570 if limit > lnk_limit:
579 threshold = 1.1 * max(y_max) # 10%
580 except ValueError as err:
583 nic_limit /= 1000000.0
584 traces.append(plgo.Scatter(
586 y=[nic_limit, ] * len(x_vals),
587 name="NIC: {0:.2f}Mpps".format(nic_limit),
596 annotations.append(dict(
603 text="NIC: {0:.2f}Mpps".format(nic_limit),
611 y_max.append(nic_limit)
613 lnk_limit /= 1000000.0
614 if lnk_limit < threshold:
615 traces.append(plgo.Scatter(
617 y=[lnk_limit, ] * len(x_vals),
618 name="Link: {0:.2f}Mpps".format(lnk_limit),
627 annotations.append(dict(
634 text="Link: {0:.2f}Mpps".format(lnk_limit),
642 y_max.append(lnk_limit)
644 pci_limit /= 1000000.0
645 if (pci_limit < threshold and
646 (pci_limit < lnk_limit * 0.95 or lnk_limit > lnk_limit * 1.05)):
647 traces.append(plgo.Scatter(
649 y=[pci_limit, ] * len(x_vals),
650 name="PCIe: {0:.2f}Mpps".format(pci_limit),
659 annotations.append(dict(
666 text="PCIe: {0:.2f}Mpps".format(pci_limit),
674 y_max.append(pci_limit)
676 # Perfect and measured:
678 for name, val in vals.iteritems():
681 for idx in range(len(val["val"])):
683 if isinstance(val["val"][idx], float):
684 htext += "No. of Runs: {1}<br>" \
685 "Mean: {0:.2f}Mpps<br>".format(val["val"][idx],
687 if isinstance(val["diff"][idx], float):
688 htext += "Diff: {0:.0f}%<br>".format(
689 round(val["diff"][idx]))
690 if isinstance(val["rel"][idx], float):
691 htext += "Speedup: {0:.2f}".format(val["rel"][idx])
692 hovertext.append(htext)
693 traces.append(plgo.Scatter(x=x_vals,
697 mode="lines+markers",
706 hoverinfo="text+name"
708 traces.append(plgo.Scatter(x=x_vals,
710 name="{0} perfect".format(name),
718 text=["Perfect: {0:.2f}Mpps".format(y)
719 for y in val["ideal"]],
723 except (IndexError, ValueError, KeyError) as err:
724 logging.warning("No data for '{0}'".format(name))
725 logging.warning(repr(err))
729 file_type = plot.get("output-file-type", ".html")
730 logging.info(" Writing file '{0}{1}'.".
731 format(plot["output-file"], file_type))
732 layout = deepcopy(plot["layout"])
733 if layout.get("title", None):
734 layout["title"] = "<b>Speedup Multi-core:</b> {0}". \
735 format(layout["title"])
736 layout["yaxis"]["range"] = [0, int(max(y_max) * 1.1)]
737 layout["annotations"].extend(annotations)
738 plpl = plgo.Figure(data=traces, layout=layout)
742 show_link=False, auto_open=False,
743 filename='{0}{1}'.format(plot["output-file"], file_type))
744 except PlotlyError as err:
745 logging.error(" Finished with error: {}".
746 format(repr(err).replace("\n", " ")))
750 def plot_performance_box(plot, input_data):
751 """Generate the plot(s) with algorithm: plot_performance_box
752 specified in the specification file.
754 TODO: Remove when not needed.
756 :param plot: Plot to generate.
757 :param input_data: Data to process.
758 :type plot: pandas.Series
759 :type input_data: InputData
763 plot_title = plot.get("title", "")
764 logging.info(" Creating the data set for the {0} '{1}'.".
765 format(plot.get("type", ""), plot_title))
766 data = input_data.filter_data(plot)
768 logging.error("No data.")
771 # Prepare the data for the plot
777 if y_vals.get(test["parent"], None) is None:
778 y_vals[test["parent"]] = list()
779 y_tags[test["parent"]] = test.get("tags", None)
781 if test["type"] in ("NDRPDR", ):
782 if "-pdr" in plot_title.lower():
783 y_vals[test["parent"]].\
784 append(test["throughput"]["PDR"]["LOWER"])
785 elif "-ndr" in plot_title.lower():
786 y_vals[test["parent"]]. \
787 append(test["throughput"]["NDR"]["LOWER"])
790 elif test["type"] in ("SOAK", ):
791 y_vals[test["parent"]].\
792 append(test["throughput"]["LOWER"])
795 except (KeyError, TypeError):
796 y_vals[test["parent"]].append(None)
799 order = plot.get("sort", None)
801 y_sorted = OrderedDict()
802 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
805 for suite, tags in y_tags_l.items():
807 tag = tag.split(" ")[-1]
808 if tag.lower() in tags:
811 if tag.lower() not in tags:
814 y_sorted[suite] = y_vals.pop(suite)
817 except KeyError as err:
818 logging.error("Not found: {0}".format(repr(err)))
824 # Add None to the lists with missing data
826 nr_of_samples = list()
827 for val in y_sorted.values():
828 if len(val) > max_len:
830 nr_of_samples.append(len(val))
831 for key, val in y_sorted.items():
832 if len(val) < max_len:
833 val.extend([None for _ in range(max_len - len(val))])
837 df = pd.DataFrame(y_sorted)
840 for i, col in enumerate(df.columns):
841 tst_name = re.sub(REGEX_NIC, "",
842 col.lower().replace('-ndrpdr', '').
843 replace('2n1l-', ''))
844 name = "{nr}. ({samples:02d} run{plural}) {name}".\
846 samples=nr_of_samples[i],
847 plural='s' if nr_of_samples[i] > 1 else '',
851 traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
852 y=[y / 1000000 if y else None for y in df[col]],
856 val_max = max(df[col])
857 except ValueError as err:
858 logging.error(repr(err))
861 y_max.append(int(val_max / 1000000) + 2)
865 layout = deepcopy(plot["layout"])
866 if layout.get("title", None):
867 layout["title"] = "<b>Throughput:</b> {0}". \
868 format(layout["title"])
870 layout["yaxis"]["range"] = [0, max(y_max)]
871 plpl = plgo.Figure(data=traces, layout=layout)
874 logging.info(" Writing file '{0}{1}'.".
875 format(plot["output-file"], plot["output-file-type"]))
876 ploff.plot(plpl, show_link=False, auto_open=False,
877 filename='{0}{1}'.format(plot["output-file"],
878 plot["output-file-type"]))
879 except PlotlyError as err:
880 logging.error(" Finished with error: {}".
881 format(repr(err).replace("\n", " ")))
885 def plot_soak_bars(plot, input_data):
886 """Generate the plot(s) with algorithm: plot_soak_bars
887 specified in the specification file.
889 :param plot: Plot to generate.
890 :param input_data: Data to process.
891 :type plot: pandas.Series
892 :type input_data: InputData
896 plot_title = plot.get("title", "")
897 logging.info(" Creating the data set for the {0} '{1}'.".
898 format(plot.get("type", ""), plot_title))
899 data = input_data.filter_data(plot)
901 logging.error("No data.")
904 # Prepare the data for the plot
910 if y_vals.get(test["parent"], None) is None:
911 y_tags[test["parent"]] = test.get("tags", None)
913 if test["type"] in ("SOAK", ):
914 y_vals[test["parent"]] = test["throughput"]
917 except (KeyError, TypeError):
918 y_vals[test["parent"]] = dict()
921 order = plot.get("sort", None)
923 y_sorted = OrderedDict()
924 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
927 for suite, tags in y_tags_l.items():
929 tag = tag.split(" ")[-1]
930 if tag.lower() in tags:
933 if tag.lower() not in tags:
936 y_sorted[suite] = y_vals.pop(suite)
939 except KeyError as err:
940 logging.error("Not found: {0}".format(repr(err)))
949 for test_name, test_data in y_sorted.items():
951 name = "{nr}. {name}".\
952 format(nr=idx, name=test_name.lower().replace('-soak', ''))
954 name_lst = name.split('-')
957 for segment in name_lst:
958 if (len(name) + len(segment) + 1) > 50 and split_name:
961 name += segment + '-'
964 y_val = test_data.get("LOWER", None)
970 time = "No Information"
971 result = "No Information"
972 hovertext = ("{name}<br>"
973 "Packet Throughput: {val:.2f}Mpps<br>"
974 "Final Duration: {time}<br>"
975 "Result: {result}".format(name=name,
979 traces.append(plgo.Bar(x=[str(idx) + '.', ],
986 layout = deepcopy(plot["layout"])
987 if layout.get("title", None):
988 layout["title"] = "<b>Packet Throughput:</b> {0}". \
989 format(layout["title"])
991 layout["yaxis"]["range"] = [0, y_max + 1]
992 plpl = plgo.Figure(data=traces, layout=layout)
994 logging.info(" Writing file '{0}{1}'.".
995 format(plot["output-file"], plot["output-file-type"]))
996 ploff.plot(plpl, show_link=False, auto_open=False,
997 filename='{0}{1}'.format(plot["output-file"],
998 plot["output-file-type"]))
999 except PlotlyError as err:
1000 logging.error(" Finished with error: {}".
1001 format(repr(err).replace("\n", " ")))
1005 def plot_soak_boxes(plot, input_data):
1006 """Generate the plot(s) with algorithm: plot_soak_boxes
1007 specified in the specification file.
1009 :param plot: Plot to generate.
1010 :param input_data: Data to process.
1011 :type plot: pandas.Series
1012 :type input_data: InputData
1015 # Transform the data
1016 plot_title = plot.get("title", "")
1017 logging.info(" Creating the data set for the {0} '{1}'.".
1018 format(plot.get("type", ""), plot_title))
1019 data = input_data.filter_data(plot)
1021 logging.error("No data.")
1024 # Prepare the data for the plot
1030 if y_vals.get(test["parent"], None) is None:
1031 y_tags[test["parent"]] = test.get("tags", None)
1033 if test["type"] in ("SOAK", ):
1034 y_vals[test["parent"]] = test["throughput"]
1037 except (KeyError, TypeError):
1038 y_vals[test["parent"]] = dict()
1041 order = plot.get("sort", None)
1042 if order and y_tags:
1043 y_sorted = OrderedDict()
1044 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
1047 for suite, tags in y_tags_l.items():
1049 tag = tag.split(" ")[-1]
1050 if tag.lower() in tags:
1053 if tag.lower() not in tags:
1056 y_sorted[suite] = y_vals.pop(suite)
1058 logging.debug(suite)
1059 except KeyError as err:
1060 logging.error("Not found: {0}".format(repr(err)))
1069 for test_name, test_data in y_sorted.items():
1071 name = "{nr}. {name}".\
1072 format(nr=idx, name=test_name.lower().replace('-soak', '').
1073 replace('2n1l-', ''))
1075 name_lst = name.split('-')
1078 for segment in name_lst:
1079 if (len(name) + len(segment) + 1) > 55 and split_name:
1082 name += segment + '-'
1085 y_val = test_data.get("UPPER", None)
1091 y_base = test_data.get("LOWER", None)
1095 hovertext = ("Upper bound: {upper:.2f}<br>"
1096 "Lower bound: {lower:.2f}".format(upper=y_val,
1098 traces.append(plgo.Bar(x=[str(idx) + '.', ],
1099 # +0.05 to see the value in case lower == upper
1100 y=[y_val - y_base + 0.05, ],
1107 layout = deepcopy(plot["layout"])
1108 if layout.get("title", None):
1109 layout["title"] = "<b>Throughput:</b> {0}". \
1110 format(layout["title"])
1112 layout["yaxis"]["range"] = [0, y_max + 1]
1113 plpl = plgo.Figure(data=traces, layout=layout)
1115 logging.info(" Writing file '{0}{1}'.".
1116 format(plot["output-file"], plot["output-file-type"]))
1117 ploff.plot(plpl, show_link=False, auto_open=False,
1118 filename='{0}{1}'.format(plot["output-file"],
1119 plot["output-file-type"]))
1120 except PlotlyError as err:
1121 logging.error(" Finished with error: {}".
1122 format(repr(err).replace("\n", " ")))
1126 def plot_latency_error_bars(plot, input_data):
1127 """Generate the plot(s) with algorithm: plot_latency_error_bars
1128 specified in the specification file.
1130 TODO: Remove when not needed.
1132 :param plot: Plot to generate.
1133 :param input_data: Data to process.
1134 :type plot: pandas.Series
1135 :type input_data: InputData
1138 # Transform the data
1139 plot_title = plot.get("title", "")
1140 logging.info(" Creating the data set for the {0} '{1}'.".
1141 format(plot.get("type", ""), plot_title))
1142 data = input_data.filter_data(plot)
1144 logging.error("No data.")
1147 # Prepare the data for the plot
1154 logging.debug("test['latency']: {0}\n".
1155 format(test["latency"]))
1156 except ValueError as err:
1157 logging.warning(repr(err))
1158 if y_tmp_vals.get(test["parent"], None) is None:
1159 y_tmp_vals[test["parent"]] = [
1160 list(), # direction1, min
1161 list(), # direction1, avg
1162 list(), # direction1, max
1163 list(), # direction2, min
1164 list(), # direction2, avg
1165 list() # direction2, max
1167 y_tags[test["parent"]] = test.get("tags", None)
1169 if test["type"] in ("NDRPDR", ):
1170 if "-pdr" in plot_title.lower():
1172 elif "-ndr" in plot_title.lower():
1175 logging.warning("Invalid test type: {0}".
1176 format(test["type"]))
1178 y_tmp_vals[test["parent"]][0].append(
1179 test["latency"][ttype]["direction1"]["min"])
1180 y_tmp_vals[test["parent"]][1].append(
1181 test["latency"][ttype]["direction1"]["avg"])
1182 y_tmp_vals[test["parent"]][2].append(
1183 test["latency"][ttype]["direction1"]["max"])
1184 y_tmp_vals[test["parent"]][3].append(
1185 test["latency"][ttype]["direction2"]["min"])
1186 y_tmp_vals[test["parent"]][4].append(
1187 test["latency"][ttype]["direction2"]["avg"])
1188 y_tmp_vals[test["parent"]][5].append(
1189 test["latency"][ttype]["direction2"]["max"])
1191 logging.warning("Invalid test type: {0}".
1192 format(test["type"]))
1194 except (KeyError, TypeError) as err:
1195 logging.warning(repr(err))
1196 logging.debug("y_tmp_vals: {0}\n".format(y_tmp_vals))
1199 order = plot.get("sort", None)
1200 if order and y_tags:
1201 y_sorted = OrderedDict()
1202 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
1205 for suite, tags in y_tags_l.items():
1207 tag = tag.split(" ")[-1]
1208 if tag.lower() in tags:
1211 if tag.lower() not in tags:
1214 y_sorted[suite] = y_tmp_vals.pop(suite)
1216 logging.debug(suite)
1217 except KeyError as err:
1218 logging.error("Not found: {0}".format(repr(err)))
1222 y_sorted = y_tmp_vals
1224 logging.debug("y_sorted: {0}\n".format(y_sorted))
1229 nr_of_samples = list()
1230 for key, val in y_sorted.items():
1231 name = re.sub(REGEX_NIC, "", key.replace('-ndrpdr', '').
1232 replace('2n1l-', ''))
1233 x_vals.append(name) # dir 1
1234 y_vals.append(mean(val[1]) if val[1] else None)
1235 y_mins.append(mean(val[0]) if val[0] else None)
1236 y_maxs.append(mean(val[2]) if val[2] else None)
1237 nr_of_samples.append(len(val[1]) if val[1] else 0)
1238 x_vals.append(name) # dir 2
1239 y_vals.append(mean(val[4]) if val[4] else None)
1240 y_mins.append(mean(val[3]) if val[3] else None)
1241 y_maxs.append(mean(val[5]) if val[5] else None)
1242 nr_of_samples.append(len(val[3]) if val[3] else 0)
1244 logging.debug("x_vals :{0}\n".format(x_vals))
1245 logging.debug("y_vals :{0}\n".format(y_vals))
1246 logging.debug("y_mins :{0}\n".format(y_mins))
1247 logging.debug("y_maxs :{0}\n".format(y_maxs))
1248 logging.debug("nr_of_samples :{0}\n".format(nr_of_samples))
1250 annotations = list()
1252 for idx in range(len(x_vals)):
1253 if not bool(int(idx % 2)):
1254 direction = "West-East"
1256 direction = "East-West"
1257 hovertext = ("No. of Runs: {nr}<br>"
1259 "Direction: {dir}<br>".format(test=x_vals[idx],
1261 nr=nr_of_samples[idx]))
1262 if isinstance(y_maxs[idx], float):
1263 hovertext += "Max: {max:.2f}uSec<br>".format(max=y_maxs[idx])
1264 if isinstance(y_vals[idx], float):
1265 hovertext += "Mean: {avg:.2f}uSec<br>".format(avg=y_vals[idx])
1266 if isinstance(y_mins[idx], float):
1267 hovertext += "Min: {min:.2f}uSec".format(min=y_mins[idx])
1269 if isinstance(y_maxs[idx], float) and isinstance(y_vals[idx], float):
1270 array = [y_maxs[idx] - y_vals[idx], ]
1273 if isinstance(y_mins[idx], float) and isinstance(y_vals[idx], float):
1274 arrayminus = [y_vals[idx] - y_mins[idx], ]
1276 arrayminus = [None, ]
1277 logging.debug("y_vals[{1}] :{0}\n".format(y_vals[idx], idx))
1278 logging.debug("array :{0}\n".format(array))
1279 logging.debug("arrayminus :{0}\n".format(arrayminus))
1280 traces.append(plgo.Scatter(
1284 legendgroup=x_vals[idx],
1285 showlegend=bool(int(idx % 2)),
1291 arrayminus=arrayminus,
1292 color=COLORS[int(idx / 2)]
1296 color=COLORS[int(idx / 2)],
1301 annotations.append(dict(
1308 text="E-W" if bool(int(idx % 2)) else "W-E",
1318 logging.info(" Writing file '{0}{1}'.".
1319 format(plot["output-file"], plot["output-file-type"]))
1320 layout = deepcopy(plot["layout"])
1321 if layout.get("title", None):
1322 layout["title"] = "<b>Latency:</b> {0}".\
1323 format(layout["title"])
1324 layout["annotations"] = annotations
1325 plpl = plgo.Figure(data=traces, layout=layout)
1329 show_link=False, auto_open=False,
1330 filename='{0}{1}'.format(plot["output-file"],
1331 plot["output-file-type"]))
1332 except PlotlyError as err:
1333 logging.error(" Finished with error: {}".
1334 format(str(err).replace("\n", " ")))
1338 def plot_throughput_speedup_analysis(plot, input_data):
1339 """Generate the plot(s) with algorithm:
1340 plot_throughput_speedup_analysis
1341 specified in the specification file.
1343 TODO: Remove when not needed.
1345 :param plot: Plot to generate.
1346 :param input_data: Data to process.
1347 :type plot: pandas.Series
1348 :type input_data: InputData
1351 # Transform the data
1352 plot_title = plot.get("title", "")
1353 logging.info(" Creating the data set for the {0} '{1}'.".
1354 format(plot.get("type", ""), plot_title))
1355 data = input_data.filter_data(plot)
1357 logging.error("No data.")
1365 if y_vals.get(test["parent"], None) is None:
1366 y_vals[test["parent"]] = {"1": list(),
1369 y_tags[test["parent"]] = test.get("tags", None)
1371 if test["type"] in ("NDRPDR",):
1372 if "-pdr" in plot_title.lower():
1374 elif "-ndr" in plot_title.lower():
1378 if "1C" in test["tags"]:
1379 y_vals[test["parent"]]["1"]. \
1380 append(test["throughput"][ttype]["LOWER"])
1381 elif "2C" in test["tags"]:
1382 y_vals[test["parent"]]["2"]. \
1383 append(test["throughput"][ttype]["LOWER"])
1384 elif "4C" in test["tags"]:
1385 y_vals[test["parent"]]["4"]. \
1386 append(test["throughput"][ttype]["LOWER"])
1387 except (KeyError, TypeError):
1391 logging.warning("No data for the plot '{}'".
1392 format(plot.get("title", "")))
1396 for test_name, test_vals in y_vals.items():
1397 for key, test_val in test_vals.items():
1399 avg_val = sum(test_val) / len(test_val)
1400 y_vals[test_name][key] = (avg_val, len(test_val))
1401 ideal = avg_val / (int(key) * 1000000.0)
1402 if test_name not in y_1c_max or ideal > y_1c_max[test_name]:
1403 y_1c_max[test_name] = ideal
1409 pci_limit = plot["limits"]["pci"]["pci-g3-x8"]
1410 for test_name, test_vals in y_vals.items():
1412 if test_vals["1"][1]:
1413 name = re.sub(REGEX_NIC, "", test_name.replace('-ndrpdr', '').
1414 replace('2n1l-', ''))
1416 y_val_1 = test_vals["1"][0] / 1000000.0
1417 y_val_2 = test_vals["2"][0] / 1000000.0 if test_vals["2"][0] \
1419 y_val_4 = test_vals["4"][0] / 1000000.0 if test_vals["4"][0] \
1422 vals[name]["val"] = [y_val_1, y_val_2, y_val_4]
1423 vals[name]["rel"] = [1.0, None, None]
1424 vals[name]["ideal"] = [y_1c_max[test_name],
1425 y_1c_max[test_name] * 2,
1426 y_1c_max[test_name] * 4]
1427 vals[name]["diff"] = [(y_val_1 - y_1c_max[test_name]) * 100 /
1428 y_val_1, None, None]
1429 vals[name]["count"] = [test_vals["1"][1],
1434 # val_max = max(max(vals[name]["val"], vals[name]["ideal"]))
1435 val_max = max(vals[name]["val"])
1436 except ValueError as err:
1440 # y_max.append(int((val_max / 10) + 1) * 10)
1441 y_max.append(val_max)
1444 vals[name]["rel"][1] = round(y_val_2 / y_val_1, 2)
1445 vals[name]["diff"][1] = \
1446 (y_val_2 - vals[name]["ideal"][1]) * 100 / y_val_2
1448 vals[name]["rel"][2] = round(y_val_4 / y_val_1, 2)
1449 vals[name]["diff"][2] = \
1450 (y_val_4 - vals[name]["ideal"][2]) * 100 / y_val_4
1451 except IndexError as err:
1452 logging.warning("No data for '{0}'".format(test_name))
1453 logging.warning(repr(err))
1456 if "x520" in test_name:
1457 limit = plot["limits"]["nic"]["x520"]
1458 elif "x710" in test_name:
1459 limit = plot["limits"]["nic"]["x710"]
1460 elif "xxv710" in test_name:
1461 limit = plot["limits"]["nic"]["xxv710"]
1462 elif "xl710" in test_name:
1463 limit = plot["limits"]["nic"]["xl710"]
1464 elif "x553" in test_name:
1465 limit = plot["limits"]["nic"]["x553"]
1468 if limit > nic_limit:
1471 mul = 2 if "ge2p" in test_name else 1
1472 if "10ge" in test_name:
1473 limit = plot["limits"]["link"]["10ge"] * mul
1474 elif "25ge" in test_name:
1475 limit = plot["limits"]["link"]["25ge"] * mul
1476 elif "40ge" in test_name:
1477 limit = plot["limits"]["link"]["40ge"] * mul
1478 elif "100ge" in test_name:
1479 limit = plot["limits"]["link"]["100ge"] * mul
1482 if limit > lnk_limit:
1486 order = plot.get("sort", None)
1487 if order and y_tags:
1488 y_sorted = OrderedDict()
1489 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
1491 for test, tags in y_tags_l.items():
1492 if tag.lower() in tags:
1493 name = re.sub(REGEX_NIC, "",
1494 test.replace('-ndrpdr', '').
1495 replace('2n1l-', ''))
1497 y_sorted[name] = vals.pop(name)
1499 except KeyError as err:
1500 logging.error("Not found: {0}".format(err))
1507 annotations = list()
1512 threshold = 1.1 * max(y_max) # 10%
1513 except ValueError as err:
1516 nic_limit /= 1000000.0
1517 # if nic_limit < threshold:
1518 traces.append(plgo.Scatter(
1520 y=[nic_limit, ] * len(x_vals),
1521 name="NIC: {0:.2f}Mpps".format(nic_limit),
1530 annotations.append(dict(
1537 text="NIC: {0:.2f}Mpps".format(nic_limit),
1545 # y_max.append(int((nic_limit / 10) + 1) * 10)
1546 y_max.append(nic_limit)
1548 lnk_limit /= 1000000.0
1549 if lnk_limit < threshold:
1550 traces.append(plgo.Scatter(
1552 y=[lnk_limit, ] * len(x_vals),
1553 name="Link: {0:.2f}Mpps".format(lnk_limit),
1562 annotations.append(dict(
1569 text="Link: {0:.2f}Mpps".format(lnk_limit),
1577 # y_max.append(int((lnk_limit / 10) + 1) * 10)
1578 y_max.append(lnk_limit)
1580 pci_limit /= 1000000.0
1581 if (pci_limit < threshold and
1582 (pci_limit < lnk_limit * 0.95 or lnk_limit > lnk_limit * 1.05)):
1583 traces.append(plgo.Scatter(
1585 y=[pci_limit, ] * len(x_vals),
1586 name="PCIe: {0:.2f}Mpps".format(pci_limit),
1595 annotations.append(dict(
1602 text="PCIe: {0:.2f}Mpps".format(pci_limit),
1610 # y_max.append(int((pci_limit / 10) + 1) * 10)
1611 y_max.append(pci_limit)
1613 # Perfect and measured:
1615 for name, val in y_sorted.iteritems():
1618 for idx in range(len(val["val"])):
1620 if isinstance(val["val"][idx], float):
1621 htext += "No. of Runs: {1}<br>" \
1622 "Mean: {0:.2f}Mpps<br>".format(val["val"][idx],
1624 if isinstance(val["diff"][idx], float):
1625 htext += "Diff: {0:.0f}%<br>".format(round(val["diff"][idx]))
1626 if isinstance(val["rel"][idx], float):
1627 htext += "Speedup: {0:.2f}".format(val["rel"][idx])
1628 hovertext.append(htext)
1629 traces.append(plgo.Scatter(x=x_vals,
1633 mode="lines+markers",
1642 hoverinfo="text+name"
1644 traces.append(plgo.Scatter(x=x_vals,
1646 name="{0} perfect".format(name),
1654 text=["Perfect: {0:.2f}Mpps".format(y)
1655 for y in val["ideal"]],
1659 except (IndexError, ValueError, KeyError) as err:
1660 logging.warning("No data for '{0}'".format(name))
1661 logging.warning(repr(err))
1665 logging.info(" Writing file '{0}{1}'.".
1666 format(plot["output-file"], plot["output-file-type"]))
1667 layout = deepcopy(plot["layout"])
1668 if layout.get("title", None):
1669 layout["title"] = "<b>Speedup Multi-core:</b> {0}". \
1670 format(layout["title"])
1671 # layout["yaxis"]["range"] = [0, int((max(y_max) / 10) + 1) * 10]
1672 layout["yaxis"]["range"] = [0, int(max(y_max) * 1.1)]
1673 layout["annotations"].extend(annotations)
1674 plpl = plgo.Figure(data=traces, layout=layout)
1678 show_link=False, auto_open=False,
1679 filename='{0}{1}'.format(plot["output-file"],
1680 plot["output-file-type"]))
1681 except PlotlyError as err:
1682 logging.error(" Finished with error: {}".
1683 format(str(err).replace("\n", " ")))
1687 def plot_http_server_performance_box(plot, input_data):
1688 """Generate the plot(s) with algorithm: plot_http_server_performance_box
1689 specified in the specification file.
1691 :param plot: Plot to generate.
1692 :param input_data: Data to process.
1693 :type plot: pandas.Series
1694 :type input_data: InputData
1697 # Transform the data
1698 logging.info(" Creating the data set for the {0} '{1}'.".
1699 format(plot.get("type", ""), plot.get("title", "")))
1700 data = input_data.filter_data(plot)
1702 logging.error("No data.")
1705 # Prepare the data for the plot
1710 if y_vals.get(test["name"], None) is None:
1711 y_vals[test["name"]] = list()
1713 y_vals[test["name"]].append(test["result"])
1714 except (KeyError, TypeError):
1715 y_vals[test["name"]].append(None)
1717 # Add None to the lists with missing data
1719 nr_of_samples = list()
1720 for val in y_vals.values():
1721 if len(val) > max_len:
1723 nr_of_samples.append(len(val))
1724 for key, val in y_vals.items():
1725 if len(val) < max_len:
1726 val.extend([None for _ in range(max_len - len(val))])
1730 df = pd.DataFrame(y_vals)
1732 for i, col in enumerate(df.columns):
1733 name = "{nr}. ({samples:02d} run{plural}) {name}".\
1735 samples=nr_of_samples[i],
1736 plural='s' if nr_of_samples[i] > 1 else '',
1737 name=col.lower().replace('-ndrpdr', ''))
1739 name_lst = name.split('-')
1742 for segment in name_lst:
1743 if (len(name) + len(segment) + 1) > 50 and split_name:
1746 name += segment + '-'
1749 traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
1755 plpl = plgo.Figure(data=traces, layout=plot["layout"])
1758 logging.info(" Writing file '{0}{1}'.".
1759 format(plot["output-file"], plot["output-file-type"]))
1760 ploff.plot(plpl, show_link=False, auto_open=False,
1761 filename='{0}{1}'.format(plot["output-file"],
1762 plot["output-file-type"]))
1763 except PlotlyError as err:
1764 logging.error(" Finished with error: {}".
1765 format(str(err).replace("\n", " ")))
1769 def plot_service_density_heatmap(plot, input_data):
1770 """Generate the plot(s) with algorithm: plot_service_density_heatmap
1771 specified in the specification file.
1773 :param plot: Plot to generate.
1774 :param input_data: Data to process.
1775 :type plot: pandas.Series
1776 :type input_data: InputData
1779 REGEX_CN = re.compile(r'^(\d*)R(\d*)C$')
1780 REGEX_TEST_NAME = re.compile(r'^.*-(\d+ch|\d+pl)-'
1782 r'(\d+vm\d+t|\d+dcr\d+t).*$')
1788 # Transform the data
1789 logging.info(" Creating the data set for the {0} '{1}'.".
1790 format(plot.get("type", ""), plot.get("title", "")))
1791 data = input_data.filter_data(plot, continue_on_error=True)
1792 if data is None or data.empty:
1793 logging.error("No data.")
1799 for tag in test['tags']:
1800 groups = re.search(REGEX_CN, tag)
1802 c = str(groups.group(1))
1803 n = str(groups.group(2))
1807 groups = re.search(REGEX_TEST_NAME, test["name"])
1808 if groups and len(groups.groups()) == 3:
1809 hover_name = "{chain}-{vhost}-{vm}".format(
1810 chain=str(groups.group(1)),
1811 vhost=str(groups.group(2)),
1812 vm=str(groups.group(3)))
1815 if vals.get(c, None) is None:
1817 if vals[c].get(n, None) is None:
1818 vals[c][n] = dict(name=hover_name,
1824 if plot["include-tests"] == "MRR":
1825 result = test["result"]["receive-rate"].avg
1826 elif plot["include-tests"] == "PDR":
1827 result = test["throughput"]["PDR"]["LOWER"]
1828 elif plot["include-tests"] == "NDR":
1829 result = test["throughput"]["NDR"]["LOWER"]
1836 vals[c][n]["vals"].append(result)
1839 logging.error("No data.")
1842 for key_c in vals.keys():
1843 txt_chains.append(key_c)
1844 for key_n in vals[key_c].keys():
1845 txt_nodes.append(key_n)
1846 if vals[key_c][key_n]["vals"]:
1847 vals[key_c][key_n]["nr"] = len(vals[key_c][key_n]["vals"])
1848 vals[key_c][key_n]["mean"] = \
1849 round(mean(vals[key_c][key_n]["vals"]) / 1000000, 1)
1850 vals[key_c][key_n]["stdev"] = \
1851 round(stdev(vals[key_c][key_n]["vals"]) / 1000000, 1)
1852 txt_nodes = list(set(txt_nodes))
1854 txt_chains = sorted(txt_chains, key=lambda chain: int(chain))
1855 txt_nodes = sorted(txt_nodes, key=lambda node: int(node))
1857 chains = [i + 1 for i in range(len(txt_chains))]
1858 nodes = [i + 1 for i in range(len(txt_nodes))]
1860 data = [list() for _ in range(len(chains))]
1864 val = vals[txt_chains[c - 1]][txt_nodes[n - 1]]["mean"]
1865 except (KeyError, IndexError):
1867 data[c - 1].append(val)
1870 my_green = [[0.0, 'rgb(235, 249, 242)'],
1871 [1.0, 'rgb(45, 134, 89)']]
1873 my_blue = [[0.0, 'rgb(236, 242, 248)'],
1874 [1.0, 'rgb(57, 115, 172)']]
1876 my_grey = [[0.0, 'rgb(230, 230, 230)'],
1877 [1.0, 'rgb(102, 102, 102)']]
1880 annotations = list()
1882 text = ("Test: {name}<br>"
1887 for c in range(len(txt_chains)):
1889 for n in range(len(txt_nodes)):
1890 if data[c][n] is not None:
1891 annotations.append(dict(
1898 text=str(data[c][n]),
1905 hover_line.append(text.format(
1906 name=vals[txt_chains[c]][txt_nodes[n]]["name"],
1907 nr=vals[txt_chains[c]][txt_nodes[n]]["nr"],
1909 stdev=vals[txt_chains[c]][txt_nodes[n]]["stdev"]))
1910 hovertext.append(hover_line)
1913 plgo.Heatmap(x=nodes,
1917 title=plot.get("z-axis", ""),
1931 colorscale=my_green,
1936 for idx, item in enumerate(txt_nodes):
1938 annotations.append(dict(
1952 for idx, item in enumerate(txt_chains):
1954 annotations.append(dict(
1969 annotations.append(dict(
1976 text=plot.get("x-axis", ""),
1984 annotations.append(dict(
1991 text=plot.get("y-axis", ""),
1999 updatemenus = list([
2008 args=[{"colorscale": [my_green, ], "reversescale": False}],
2013 args=[{"colorscale": [my_blue, ], "reversescale": False}],
2018 args=[{"colorscale": [my_grey, ], "reversescale": False}],
2027 layout = deepcopy(plot["layout"])
2028 except KeyError as err:
2029 logging.error("Finished with error: No layout defined")
2030 logging.error(repr(err))
2033 layout["annotations"] = annotations
2034 layout['updatemenus'] = updatemenus
2038 plpl = plgo.Figure(data=traces, layout=layout)
2041 logging.info(" Writing file '{0}{1}'.".
2042 format(plot["output-file"], plot["output-file-type"]))
2043 ploff.plot(plpl, show_link=False, auto_open=False,
2044 filename='{0}{1}'.format(plot["output-file"],
2045 plot["output-file-type"]))
2046 except PlotlyError as err:
2047 logging.error(" Finished with error: {}".
2048 format(str(err).replace("\n", " ")))
2052 def plot_service_density_heatmap_compare(plot, input_data):
2053 """Generate the plot(s) with algorithm: plot_service_density_heatmap_compare
2054 specified in the specification file.
2056 :param plot: Plot to generate.
2057 :param input_data: Data to process.
2058 :type plot: pandas.Series
2059 :type input_data: InputData
2062 REGEX_CN = re.compile(r'^(\d*)R(\d*)C$')
2063 REGEX_TEST_NAME = re.compile(r'^.*-(\d+ch|\d+pl)-'
2065 r'(\d+vm\d+t|\d+dcr\d+t).*$')
2066 REGEX_THREADS = re.compile(r'^(\d+)(VM|DCR)(\d+)T$')
2072 # Transform the data
2073 logging.info(" Creating the data set for the {0} '{1}'.".
2074 format(plot.get("type", ""), plot.get("title", "")))
2075 data = input_data.filter_data(plot, continue_on_error=True)
2076 if data is None or data.empty:
2077 logging.error("No data.")
2083 for tag in test['tags']:
2084 groups = re.search(REGEX_CN, tag)
2086 c = str(groups.group(1))
2087 n = str(groups.group(2))
2091 groups = re.search(REGEX_TEST_NAME, test["name"])
2092 if groups and len(groups.groups()) == 3:
2093 hover_name = "{chain}-{vhost}-{vm}".format(
2094 chain=str(groups.group(1)),
2095 vhost=str(groups.group(2)),
2096 vm=str(groups.group(3)))
2099 if vals.get(c, None) is None:
2101 if vals[c].get(n, None) is None:
2102 vals[c][n] = dict(name=hover_name,
2112 if plot["include-tests"] == "MRR":
2113 result = test["result"]["receive-rate"].avg
2114 elif plot["include-tests"] == "PDR":
2115 result = test["throughput"]["PDR"]["LOWER"]
2116 elif plot["include-tests"] == "NDR":
2117 result = test["throughput"]["NDR"]["LOWER"]
2124 for tag in test['tags']:
2125 groups = re.search(REGEX_THREADS, tag)
2126 if groups and len(groups.groups()) == 3:
2127 if str(groups.group(3)) == \
2128 plot["reference"]["include"]:
2129 vals[c][n]["vals_r"].append(result)
2130 elif str(groups.group(3)) == \
2131 plot["compare"]["include"]:
2132 vals[c][n]["vals_c"].append(result)
2135 logging.error("No data.")
2138 for key_c in vals.keys():
2139 txt_chains.append(key_c)
2140 for key_n in vals[key_c].keys():
2141 txt_nodes.append(key_n)
2142 if vals[key_c][key_n]["vals_r"]:
2143 vals[key_c][key_n]["nr_r"] = len(vals[key_c][key_n]["vals_r"])
2144 vals[key_c][key_n]["mean_r"] = \
2145 mean(vals[key_c][key_n]["vals_r"])
2146 vals[key_c][key_n]["stdev_r"] = \
2147 round(stdev(vals[key_c][key_n]["vals_r"]) / 1000000, 1)
2148 if vals[key_c][key_n]["vals_c"]:
2149 vals[key_c][key_n]["nr_c"] = len(vals[key_c][key_n]["vals_c"])
2150 vals[key_c][key_n]["mean_c"] = \
2151 mean(vals[key_c][key_n]["vals_c"])
2152 vals[key_c][key_n]["stdev_c"] = \
2153 round(stdev(vals[key_c][key_n]["vals_c"]) / 1000000, 1)
2155 txt_nodes = list(set(txt_nodes))
2157 txt_chains = sorted(txt_chains, key=lambda chain: int(chain))
2158 txt_nodes = sorted(txt_nodes, key=lambda node: int(node))
2160 chains = [i + 1 for i in range(len(txt_chains))]
2161 nodes = [i + 1 for i in range(len(txt_nodes))]
2163 data_r = [list() for _ in range(len(chains))]
2164 data_c = [list() for _ in range(len(chains))]
2165 diff = [list() for _ in range(len(chains))]
2169 val_r = vals[txt_chains[c - 1]][txt_nodes[n - 1]]["mean_r"]
2170 except (KeyError, IndexError):
2173 val_c = vals[txt_chains[c - 1]][txt_nodes[n - 1]]["mean_c"]
2174 except (KeyError, IndexError):
2176 if val_c is not None and val_r:
2177 val_d = (val_c - val_r) * 100 / val_r
2181 if val_r is not None:
2182 val_r = round(val_r / 1000000, 1)
2183 data_r[c - 1].append(val_r)
2184 if val_c is not None:
2185 val_c = round(val_c / 1000000, 1)
2186 data_c[c - 1].append(val_c)
2187 if val_d is not None:
2188 val_d = int(round(val_d, 0))
2189 diff[c - 1].append(val_d)
2192 my_green = [[0.0, 'rgb(235, 249, 242)'],
2193 [1.0, 'rgb(45, 134, 89)']]
2195 my_blue = [[0.0, 'rgb(236, 242, 248)'],
2196 [1.0, 'rgb(57, 115, 172)']]
2198 my_grey = [[0.0, 'rgb(230, 230, 230)'],
2199 [1.0, 'rgb(102, 102, 102)']]
2203 annotations = list()
2204 annotations_r = list()
2205 annotations_c = list()
2206 annotations_diff = list()
2208 text = ("Test: {name}"
2209 "<br>{title_r}: {text_r}"
2210 "<br>{title_c}: {text_c}{text_diff}")
2211 text_r = "Thput: {val_r}; StDev: {stdev_r}; Runs: {nr_r}"
2212 text_c = "Thput: {val_c}; StDev: {stdev_c}; Runs: {nr_c}"
2213 text_diff = "<br>Relative Difference {title_c} vs. {title_r}: {diff}%"
2215 for c in range(len(txt_chains)):
2217 for n in range(len(txt_nodes)):
2233 point_text_r = "Not present"
2234 point_text_c = "Not present"
2235 point_text_diff = ""
2237 point_r = data_r[c][n]
2238 if point_r is not None:
2239 point_text_r = text_r.format(
2241 stdev_r=vals[txt_chains[c]][txt_nodes[n]]["stdev_r"],
2242 nr_r=vals[txt_chains[c]][txt_nodes[n]]["nr_r"])
2245 point["text"] = "" if point_r is None else point_r
2246 annotations_r.append(deepcopy(point))
2249 point_c = data_c[c][n]
2250 if point_c is not None:
2251 point_text_c = text_c.format(
2253 stdev_c=vals[txt_chains[c]][txt_nodes[n]]["stdev_c"],
2254 nr_c=vals[txt_chains[c]][txt_nodes[n]]["nr_c"])
2257 point["text"] = "" if point_c is None else point_c
2258 annotations_c.append(deepcopy(point))
2261 point_d = diff[c][n]
2262 if point_d is not None:
2263 point_text_diff = text_diff.format(
2264 title_r=plot["reference"]["name"],
2265 title_c=plot["compare"]["name"],
2269 point["text"] = "" if point_d is None else point_d
2270 annotations_diff.append(deepcopy(point))
2273 name = vals[txt_chains[c]][txt_nodes[n]]["name"]
2277 hover_line.append(text.format(
2279 title_r=plot["reference"]["name"],
2280 text_r=point_text_r,
2281 title_c=plot["compare"]["name"],
2282 text_c=point_text_c,
2283 text_diff=point_text_diff
2286 hovertext.append(hover_line)
2289 plgo.Heatmap(x=nodes,
2294 title=plot.get("z-axis", ""),
2308 colorscale=my_green,
2312 plgo.Heatmap(x=nodes,
2317 title=plot.get("z-axis", ""),
2335 plgo.Heatmap(x=nodes,
2341 title="Relative Difference {name_c} vs. {name_r} [%]".
2342 format(name_c=plot["compare"]["name"],
2343 name_r=plot["reference"]["name"]),
2363 for idx, item in enumerate(txt_nodes):
2365 annotations.append(dict(
2379 for idx, item in enumerate(txt_chains):
2381 annotations.append(dict(
2396 annotations.append(dict(
2403 text=plot.get("x-axis", ""),
2411 annotations.append(dict(
2418 text=plot.get("y-axis", ""),
2426 updatemenus = list([
2436 label=plot["reference"]["name"],
2440 "visible": [True, False, False]
2443 "colorscale": [my_green, ],
2444 "reversescale": False,
2445 "annotations": annotations + annotations_r,
2450 label=plot["compare"]["name"],
2454 "visible": [False, True, False]
2457 "colorscale": [my_blue, ],
2458 "reversescale": False,
2459 "annotations": annotations + annotations_c,
2468 "visible": [False, False, True]
2471 "colorscale": [my_grey, ],
2472 "reversescale": False,
2473 "annotations": annotations + annotations_diff,
2482 layout = deepcopy(plot["layout"])
2483 except KeyError as err:
2484 logging.error("Finished with error: No layout defined")
2485 logging.error(repr(err))
2488 layout["annotations"] = annotations + annotations_r
2489 layout['updatemenus'] = updatemenus
2493 plpl = plgo.Figure(data=traces, layout=layout)
2496 logging.info(" Writing file '{0}{1}'.".
2497 format(plot["output-file"], plot["output-file-type"]))
2498 ploff.plot(plpl, show_link=False, auto_open=False,
2499 filename='{0}{1}'.format(plot["output-file"],
2500 plot["output-file-type"]))
2501 except PlotlyError as err:
2502 logging.error(" Finished with error: {}".
2503 format(str(err).replace("\n", " ")))