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_performance_box(plot, input_data):
65 """Generate the plot(s) with algorithm: plot_performance_box
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_data(plot)
80 logging.error("No data.")
83 # Prepare the data for the plot
89 if y_vals.get(test["parent"], None) is None:
90 y_vals[test["parent"]] = list()
91 y_tags[test["parent"]] = test.get("tags", None)
93 if test["type"] in ("NDRPDR", ):
94 if "-pdr" in plot_title.lower():
95 y_vals[test["parent"]].\
96 append(test["throughput"]["PDR"]["LOWER"])
97 elif "-ndr" in plot_title.lower():
98 y_vals[test["parent"]]. \
99 append(test["throughput"]["NDR"]["LOWER"])
102 elif test["type"] in ("SOAK", ):
103 y_vals[test["parent"]].\
104 append(test["throughput"]["LOWER"])
107 except (KeyError, TypeError):
108 y_vals[test["parent"]].append(None)
111 order = plot.get("sort", None)
113 y_sorted = OrderedDict()
114 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
117 for suite, tags in y_tags_l.items():
119 tag = tag.split(" ")[-1]
120 if tag.lower() in tags:
123 if tag.lower() not in tags:
126 y_sorted[suite] = y_vals.pop(suite)
129 except KeyError as err:
130 logging.error("Not found: {0}".format(repr(err)))
136 # Add None to the lists with missing data
138 nr_of_samples = list()
139 for val in y_sorted.values():
140 if len(val) > max_len:
142 nr_of_samples.append(len(val))
143 for key, val in y_sorted.items():
144 if len(val) < max_len:
145 val.extend([None for _ in range(max_len - len(val))])
149 df = pd.DataFrame(y_sorted)
152 for i, col in enumerate(df.columns):
153 tst_name = re.sub(REGEX_NIC, "",
154 col.lower().replace('-ndrpdr', '').
155 replace('2n1l-', ''))
156 name = "{nr}. ({samples:02d} run{plural}) {name}".\
158 samples=nr_of_samples[i],
159 plural='s' if nr_of_samples[i] > 1 else '',
163 traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
164 y=[y / 1000000 if y else None for y in df[col]],
168 val_max = max(df[col])
169 except ValueError as err:
170 logging.error(repr(err))
173 y_max.append(int(val_max / 1000000) + 2)
177 layout = deepcopy(plot["layout"])
178 if layout.get("title", None):
179 layout["title"] = "<b>Throughput:</b> {0}". \
180 format(layout["title"])
182 layout["yaxis"]["range"] = [0, max(y_max)]
183 plpl = plgo.Figure(data=traces, layout=layout)
186 logging.info(" Writing file '{0}{1}'.".
187 format(plot["output-file"], plot["output-file-type"]))
188 ploff.plot(plpl, show_link=False, auto_open=False,
189 filename='{0}{1}'.format(plot["output-file"],
190 plot["output-file-type"]))
191 except PlotlyError as err:
192 logging.error(" Finished with error: {}".
193 format(repr(err).replace("\n", " ")))
197 def plot_soak_bars(plot, input_data):
198 """Generate the plot(s) with algorithm: plot_soak_bars
199 specified in the specification file.
201 :param plot: Plot to generate.
202 :param input_data: Data to process.
203 :type plot: pandas.Series
204 :type input_data: InputData
208 plot_title = plot.get("title", "")
209 logging.info(" Creating the data set for the {0} '{1}'.".
210 format(plot.get("type", ""), plot_title))
211 data = input_data.filter_data(plot)
213 logging.error("No data.")
216 # Prepare the data for the plot
222 if y_vals.get(test["parent"], None) is None:
223 y_tags[test["parent"]] = test.get("tags", None)
225 if test["type"] in ("SOAK", ):
226 y_vals[test["parent"]] = test["throughput"]
229 except (KeyError, TypeError):
230 y_vals[test["parent"]] = dict()
233 order = plot.get("sort", None)
235 y_sorted = OrderedDict()
236 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
239 for suite, tags in y_tags_l.items():
241 tag = tag.split(" ")[-1]
242 if tag.lower() in tags:
245 if tag.lower() not in tags:
248 y_sorted[suite] = y_vals.pop(suite)
251 except KeyError as err:
252 logging.error("Not found: {0}".format(repr(err)))
261 for test_name, test_data in y_sorted.items():
263 name = "{nr}. {name}".\
264 format(nr=idx, name=test_name.lower().replace('-soak', ''))
266 name_lst = name.split('-')
269 for segment in name_lst:
270 if (len(name) + len(segment) + 1) > 50 and split_name:
273 name += segment + '-'
276 y_val = test_data.get("LOWER", None)
282 time = "No Information"
283 result = "No Information"
284 hovertext = ("{name}<br>"
285 "Packet Throughput: {val:.2f}Mpps<br>"
286 "Final Duration: {time}<br>"
287 "Result: {result}".format(name=name,
291 traces.append(plgo.Bar(x=[str(idx) + '.', ],
298 layout = deepcopy(plot["layout"])
299 if layout.get("title", None):
300 layout["title"] = "<b>Packet Throughput:</b> {0}". \
301 format(layout["title"])
303 layout["yaxis"]["range"] = [0, y_max + 1]
304 plpl = plgo.Figure(data=traces, layout=layout)
306 logging.info(" Writing file '{0}{1}'.".
307 format(plot["output-file"], plot["output-file-type"]))
308 ploff.plot(plpl, show_link=False, auto_open=False,
309 filename='{0}{1}'.format(plot["output-file"],
310 plot["output-file-type"]))
311 except PlotlyError as err:
312 logging.error(" Finished with error: {}".
313 format(repr(err).replace("\n", " ")))
317 def plot_soak_boxes(plot, input_data):
318 """Generate the plot(s) with algorithm: plot_soak_boxes
319 specified in the specification file.
321 :param plot: Plot to generate.
322 :param input_data: Data to process.
323 :type plot: pandas.Series
324 :type input_data: InputData
328 plot_title = plot.get("title", "")
329 logging.info(" Creating the data set for the {0} '{1}'.".
330 format(plot.get("type", ""), plot_title))
331 data = input_data.filter_data(plot)
333 logging.error("No data.")
336 # Prepare the data for the plot
342 if y_vals.get(test["parent"], None) is None:
343 y_tags[test["parent"]] = test.get("tags", None)
345 if test["type"] in ("SOAK", ):
346 y_vals[test["parent"]] = test["throughput"]
349 except (KeyError, TypeError):
350 y_vals[test["parent"]] = dict()
353 order = plot.get("sort", None)
355 y_sorted = OrderedDict()
356 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
359 for suite, tags in y_tags_l.items():
361 tag = tag.split(" ")[-1]
362 if tag.lower() in tags:
365 if tag.lower() not in tags:
368 y_sorted[suite] = y_vals.pop(suite)
371 except KeyError as err:
372 logging.error("Not found: {0}".format(repr(err)))
381 for test_name, test_data in y_sorted.items():
383 name = "{nr}. {name}".\
384 format(nr=idx, name=test_name.lower().replace('-soak', '').
385 replace('2n1l-', ''))
387 name_lst = name.split('-')
390 for segment in name_lst:
391 if (len(name) + len(segment) + 1) > 55 and split_name:
394 name += segment + '-'
397 y_val = test_data.get("UPPER", None)
403 y_base = test_data.get("LOWER", None)
407 hovertext = ("Upper bound: {upper:.2f}<br>"
408 "Lower bound: {lower:.2f}".format(upper=y_val,
410 traces.append(plgo.Bar(x=[str(idx) + '.', ],
411 # +0.05 to see the value in case lower == upper
412 y=[y_val - y_base + 0.05, ],
419 layout = deepcopy(plot["layout"])
420 if layout.get("title", None):
421 layout["title"] = "<b>Throughput:</b> {0}". \
422 format(layout["title"])
424 layout["yaxis"]["range"] = [0, y_max + 1]
425 plpl = plgo.Figure(data=traces, layout=layout)
427 logging.info(" Writing file '{0}{1}'.".
428 format(plot["output-file"], plot["output-file-type"]))
429 ploff.plot(plpl, show_link=False, auto_open=False,
430 filename='{0}{1}'.format(plot["output-file"],
431 plot["output-file-type"]))
432 except PlotlyError as err:
433 logging.error(" Finished with error: {}".
434 format(repr(err).replace("\n", " ")))
438 def plot_latency_error_bars(plot, input_data):
439 """Generate the plot(s) with algorithm: plot_latency_error_bars
440 specified in the specification file.
442 :param plot: Plot to generate.
443 :param input_data: Data to process.
444 :type plot: pandas.Series
445 :type input_data: InputData
449 plot_title = plot.get("title", "")
450 logging.info(" Creating the data set for the {0} '{1}'.".
451 format(plot.get("type", ""), plot_title))
452 data = input_data.filter_data(plot)
454 logging.error("No data.")
457 # Prepare the data for the plot
464 logging.debug("test['latency']: {0}\n".
465 format(test["latency"]))
466 except ValueError as err:
467 logging.warning(repr(err))
468 if y_tmp_vals.get(test["parent"], None) is None:
469 y_tmp_vals[test["parent"]] = [
470 list(), # direction1, min
471 list(), # direction1, avg
472 list(), # direction1, max
473 list(), # direction2, min
474 list(), # direction2, avg
475 list() # direction2, max
477 y_tags[test["parent"]] = test.get("tags", None)
479 if test["type"] in ("NDRPDR", ):
480 if "-pdr" in plot_title.lower():
482 elif "-ndr" in plot_title.lower():
485 logging.warning("Invalid test type: {0}".
486 format(test["type"]))
488 y_tmp_vals[test["parent"]][0].append(
489 test["latency"][ttype]["direction1"]["min"])
490 y_tmp_vals[test["parent"]][1].append(
491 test["latency"][ttype]["direction1"]["avg"])
492 y_tmp_vals[test["parent"]][2].append(
493 test["latency"][ttype]["direction1"]["max"])
494 y_tmp_vals[test["parent"]][3].append(
495 test["latency"][ttype]["direction2"]["min"])
496 y_tmp_vals[test["parent"]][4].append(
497 test["latency"][ttype]["direction2"]["avg"])
498 y_tmp_vals[test["parent"]][5].append(
499 test["latency"][ttype]["direction2"]["max"])
501 logging.warning("Invalid test type: {0}".
502 format(test["type"]))
504 except (KeyError, TypeError) as err:
505 logging.warning(repr(err))
506 logging.debug("y_tmp_vals: {0}\n".format(y_tmp_vals))
509 order = plot.get("sort", None)
511 y_sorted = OrderedDict()
512 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
515 for suite, tags in y_tags_l.items():
517 tag = tag.split(" ")[-1]
518 if tag.lower() in tags:
521 if tag.lower() not in tags:
524 y_sorted[suite] = y_tmp_vals.pop(suite)
527 except KeyError as err:
528 logging.error("Not found: {0}".format(repr(err)))
532 y_sorted = y_tmp_vals
534 logging.debug("y_sorted: {0}\n".format(y_sorted))
539 nr_of_samples = list()
540 for key, val in y_sorted.items():
541 name = re.sub(REGEX_NIC, "", key.replace('-ndrpdr', '').
542 replace('2n1l-', ''))
543 x_vals.append(name) # dir 1
544 y_vals.append(mean(val[1]) if val[1] else None)
545 y_mins.append(mean(val[0]) if val[0] else None)
546 y_maxs.append(mean(val[2]) if val[2] else None)
547 nr_of_samples.append(len(val[1]) if val[1] else 0)
548 x_vals.append(name) # dir 2
549 y_vals.append(mean(val[4]) if val[4] else None)
550 y_mins.append(mean(val[3]) if val[3] else None)
551 y_maxs.append(mean(val[5]) if val[5] else None)
552 nr_of_samples.append(len(val[3]) if val[3] else 0)
554 logging.debug("x_vals :{0}\n".format(x_vals))
555 logging.debug("y_vals :{0}\n".format(y_vals))
556 logging.debug("y_mins :{0}\n".format(y_mins))
557 logging.debug("y_maxs :{0}\n".format(y_maxs))
558 logging.debug("nr_of_samples :{0}\n".format(nr_of_samples))
562 for idx in range(len(x_vals)):
563 if not bool(int(idx % 2)):
564 direction = "West-East"
566 direction = "East-West"
567 hovertext = ("No. of Runs: {nr}<br>"
569 "Direction: {dir}<br>".format(test=x_vals[idx],
571 nr=nr_of_samples[idx]))
572 if isinstance(y_maxs[idx], float):
573 hovertext += "Max: {max:.2f}uSec<br>".format(max=y_maxs[idx])
574 if isinstance(y_vals[idx], float):
575 hovertext += "Mean: {avg:.2f}uSec<br>".format(avg=y_vals[idx])
576 if isinstance(y_mins[idx], float):
577 hovertext += "Min: {min:.2f}uSec".format(min=y_mins[idx])
579 if isinstance(y_maxs[idx], float) and isinstance(y_vals[idx], float):
580 array = [y_maxs[idx] - y_vals[idx], ]
583 if isinstance(y_mins[idx], float) and isinstance(y_vals[idx], float):
584 arrayminus = [y_vals[idx] - y_mins[idx], ]
586 arrayminus = [None, ]
587 logging.debug("y_vals[{1}] :{0}\n".format(y_vals[idx], idx))
588 logging.debug("array :{0}\n".format(array))
589 logging.debug("arrayminus :{0}\n".format(arrayminus))
590 traces.append(plgo.Scatter(
594 legendgroup=x_vals[idx],
595 showlegend=bool(int(idx % 2)),
601 arrayminus=arrayminus,
602 color=COLORS[int(idx / 2)]
606 color=COLORS[int(idx / 2)],
611 annotations.append(dict(
618 text="E-W" if bool(int(idx % 2)) else "W-E",
628 logging.info(" Writing file '{0}{1}'.".
629 format(plot["output-file"], plot["output-file-type"]))
630 layout = deepcopy(plot["layout"])
631 if layout.get("title", None):
632 layout["title"] = "<b>Latency:</b> {0}".\
633 format(layout["title"])
634 layout["annotations"] = annotations
635 plpl = plgo.Figure(data=traces, layout=layout)
639 show_link=False, auto_open=False,
640 filename='{0}{1}'.format(plot["output-file"],
641 plot["output-file-type"]))
642 except PlotlyError as err:
643 logging.error(" Finished with error: {}".
644 format(str(err).replace("\n", " ")))
648 def plot_throughput_speedup_analysis(plot, input_data):
649 """Generate the plot(s) with algorithm:
650 plot_throughput_speedup_analysis
651 specified in the specification file.
653 :param plot: Plot to generate.
654 :param input_data: Data to process.
655 :type plot: pandas.Series
656 :type input_data: InputData
660 plot_title = plot.get("title", "")
661 logging.info(" Creating the data set for the {0} '{1}'.".
662 format(plot.get("type", ""), plot_title))
663 data = input_data.filter_data(plot)
665 logging.error("No data.")
673 if y_vals.get(test["parent"], None) is None:
674 y_vals[test["parent"]] = {"1": list(),
677 y_tags[test["parent"]] = test.get("tags", None)
679 if test["type"] in ("NDRPDR",):
680 if "-pdr" in plot_title.lower():
682 elif "-ndr" in plot_title.lower():
686 if "1C" in test["tags"]:
687 y_vals[test["parent"]]["1"]. \
688 append(test["throughput"][ttype]["LOWER"])
689 elif "2C" in test["tags"]:
690 y_vals[test["parent"]]["2"]. \
691 append(test["throughput"][ttype]["LOWER"])
692 elif "4C" in test["tags"]:
693 y_vals[test["parent"]]["4"]. \
694 append(test["throughput"][ttype]["LOWER"])
695 except (KeyError, TypeError):
699 logging.warning("No data for the plot '{}'".
700 format(plot.get("title", "")))
704 for test_name, test_vals in y_vals.items():
705 for key, test_val in test_vals.items():
707 avg_val = sum(test_val) / len(test_val)
708 y_vals[test_name][key] = (avg_val, len(test_val))
709 ideal = avg_val / (int(key) * 1000000.0)
710 if test_name not in y_1c_max or ideal > y_1c_max[test_name]:
711 y_1c_max[test_name] = ideal
717 pci_limit = plot["limits"]["pci"]["pci-g3-x8"]
718 for test_name, test_vals in y_vals.items():
720 if test_vals["1"][1]:
721 name = re.sub(REGEX_NIC, "", test_name.replace('-ndrpdr', '').
722 replace('2n1l-', ''))
724 y_val_1 = test_vals["1"][0] / 1000000.0
725 y_val_2 = test_vals["2"][0] / 1000000.0 if test_vals["2"][0] \
727 y_val_4 = test_vals["4"][0] / 1000000.0 if test_vals["4"][0] \
730 vals[name]["val"] = [y_val_1, y_val_2, y_val_4]
731 vals[name]["rel"] = [1.0, None, None]
732 vals[name]["ideal"] = [y_1c_max[test_name],
733 y_1c_max[test_name] * 2,
734 y_1c_max[test_name] * 4]
735 vals[name]["diff"] = [(y_val_1 - y_1c_max[test_name]) * 100 /
737 vals[name]["count"] = [test_vals["1"][1],
742 # val_max = max(max(vals[name]["val"], vals[name]["ideal"]))
743 val_max = max(vals[name]["val"])
744 except ValueError as err:
748 # y_max.append(int((val_max / 10) + 1) * 10)
749 y_max.append(val_max)
752 vals[name]["rel"][1] = round(y_val_2 / y_val_1, 2)
753 vals[name]["diff"][1] = \
754 (y_val_2 - vals[name]["ideal"][1]) * 100 / y_val_2
756 vals[name]["rel"][2] = round(y_val_4 / y_val_1, 2)
757 vals[name]["diff"][2] = \
758 (y_val_4 - vals[name]["ideal"][2]) * 100 / y_val_4
759 except IndexError as err:
760 logging.warning("No data for '{0}'".format(test_name))
761 logging.warning(repr(err))
764 if "x520" in test_name:
765 limit = plot["limits"]["nic"]["x520"]
766 elif "x710" in test_name:
767 limit = plot["limits"]["nic"]["x710"]
768 elif "xxv710" in test_name:
769 limit = plot["limits"]["nic"]["xxv710"]
770 elif "xl710" in test_name:
771 limit = plot["limits"]["nic"]["xl710"]
772 elif "x553" in test_name:
773 limit = plot["limits"]["nic"]["x553"]
776 if limit > nic_limit:
779 mul = 2 if "ge2p" in test_name else 1
780 if "10ge" in test_name:
781 limit = plot["limits"]["link"]["10ge"] * mul
782 elif "25ge" in test_name:
783 limit = plot["limits"]["link"]["25ge"] * mul
784 elif "40ge" in test_name:
785 limit = plot["limits"]["link"]["40ge"] * mul
786 elif "100ge" in test_name:
787 limit = plot["limits"]["link"]["100ge"] * mul
790 if limit > lnk_limit:
794 order = plot.get("sort", None)
796 y_sorted = OrderedDict()
797 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
799 for test, tags in y_tags_l.items():
800 if tag.lower() in tags:
801 name = re.sub(REGEX_NIC, "",
802 test.replace('-ndrpdr', '').
803 replace('2n1l-', ''))
805 y_sorted[name] = vals.pop(name)
807 except KeyError as err:
808 logging.error("Not found: {0}".format(err))
820 threshold = 1.1 * max(y_max) # 10%
821 except ValueError as err:
824 nic_limit /= 1000000.0
825 # if nic_limit < threshold:
826 traces.append(plgo.Scatter(
828 y=[nic_limit, ] * len(x_vals),
829 name="NIC: {0:.2f}Mpps".format(nic_limit),
838 annotations.append(dict(
845 text="NIC: {0:.2f}Mpps".format(nic_limit),
853 # y_max.append(int((nic_limit / 10) + 1) * 10)
854 y_max.append(nic_limit)
856 lnk_limit /= 1000000.0
857 if lnk_limit < threshold:
858 traces.append(plgo.Scatter(
860 y=[lnk_limit, ] * len(x_vals),
861 name="Link: {0:.2f}Mpps".format(lnk_limit),
870 annotations.append(dict(
877 text="Link: {0:.2f}Mpps".format(lnk_limit),
885 # y_max.append(int((lnk_limit / 10) + 1) * 10)
886 y_max.append(lnk_limit)
888 pci_limit /= 1000000.0
889 if (pci_limit < threshold and
890 (pci_limit < lnk_limit * 0.95 or lnk_limit > lnk_limit * 1.05)):
891 traces.append(plgo.Scatter(
893 y=[pci_limit, ] * len(x_vals),
894 name="PCIe: {0:.2f}Mpps".format(pci_limit),
903 annotations.append(dict(
910 text="PCIe: {0:.2f}Mpps".format(pci_limit),
918 # y_max.append(int((pci_limit / 10) + 1) * 10)
919 y_max.append(pci_limit)
921 # Perfect and measured:
923 for name, val in y_sorted.iteritems():
926 for idx in range(len(val["val"])):
928 if isinstance(val["val"][idx], float):
929 htext += "No. of Runs: {1}<br>" \
930 "Mean: {0:.2f}Mpps<br>".format(val["val"][idx],
932 if isinstance(val["diff"][idx], float):
933 htext += "Diff: {0:.0f}%<br>".format(round(val["diff"][idx]))
934 if isinstance(val["rel"][idx], float):
935 htext += "Speedup: {0:.2f}".format(val["rel"][idx])
936 hovertext.append(htext)
937 traces.append(plgo.Scatter(x=x_vals,
941 mode="lines+markers",
950 hoverinfo="text+name"
952 traces.append(plgo.Scatter(x=x_vals,
954 name="{0} perfect".format(name),
962 text=["Perfect: {0:.2f}Mpps".format(y)
963 for y in val["ideal"]],
967 except (IndexError, ValueError, KeyError) as err:
968 logging.warning("No data for '{0}'".format(name))
969 logging.warning(repr(err))
973 logging.info(" Writing file '{0}{1}'.".
974 format(plot["output-file"], plot["output-file-type"]))
975 layout = deepcopy(plot["layout"])
976 if layout.get("title", None):
977 layout["title"] = "<b>Speedup Multi-core:</b> {0}". \
978 format(layout["title"])
979 # layout["yaxis"]["range"] = [0, int((max(y_max) / 10) + 1) * 10]
980 layout["yaxis"]["range"] = [0, int(max(y_max) * 1.1)]
981 layout["annotations"].extend(annotations)
982 plpl = plgo.Figure(data=traces, layout=layout)
986 show_link=False, auto_open=False,
987 filename='{0}{1}'.format(plot["output-file"],
988 plot["output-file-type"]))
989 except PlotlyError as err:
990 logging.error(" Finished with error: {}".
991 format(str(err).replace("\n", " ")))
995 def plot_http_server_performance_box(plot, input_data):
996 """Generate the plot(s) with algorithm: plot_http_server_performance_box
997 specified in the specification file.
999 :param plot: Plot to generate.
1000 :param input_data: Data to process.
1001 :type plot: pandas.Series
1002 :type input_data: InputData
1005 # Transform the data
1006 logging.info(" Creating the data set for the {0} '{1}'.".
1007 format(plot.get("type", ""), plot.get("title", "")))
1008 data = input_data.filter_data(plot)
1010 logging.error("No data.")
1013 # Prepare the data for the plot
1018 if y_vals.get(test["name"], None) is None:
1019 y_vals[test["name"]] = list()
1021 y_vals[test["name"]].append(test["result"])
1022 except (KeyError, TypeError):
1023 y_vals[test["name"]].append(None)
1025 # Add None to the lists with missing data
1027 nr_of_samples = list()
1028 for val in y_vals.values():
1029 if len(val) > max_len:
1031 nr_of_samples.append(len(val))
1032 for key, val in y_vals.items():
1033 if len(val) < max_len:
1034 val.extend([None for _ in range(max_len - len(val))])
1038 df = pd.DataFrame(y_vals)
1040 for i, col in enumerate(df.columns):
1041 name = "{nr}. ({samples:02d} run{plural}) {name}".\
1043 samples=nr_of_samples[i],
1044 plural='s' if nr_of_samples[i] > 1 else '',
1045 name=col.lower().replace('-ndrpdr', ''))
1047 name_lst = name.split('-')
1050 for segment in name_lst:
1051 if (len(name) + len(segment) + 1) > 50 and split_name:
1054 name += segment + '-'
1057 traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
1063 plpl = plgo.Figure(data=traces, layout=plot["layout"])
1066 logging.info(" Writing file '{0}{1}'.".
1067 format(plot["output-file"], plot["output-file-type"]))
1068 ploff.plot(plpl, show_link=False, auto_open=False,
1069 filename='{0}{1}'.format(plot["output-file"],
1070 plot["output-file-type"]))
1071 except PlotlyError as err:
1072 logging.error(" Finished with error: {}".
1073 format(str(err).replace("\n", " ")))
1077 def plot_service_density_heatmap(plot, input_data):
1078 """Generate the plot(s) with algorithm: plot_service_density_heatmap
1079 specified in the specification file.
1081 :param plot: Plot to generate.
1082 :param input_data: Data to process.
1083 :type plot: pandas.Series
1084 :type input_data: InputData
1087 REGEX_CN = re.compile(r'^(\d*)R(\d*)C$')
1088 REGEX_TEST_NAME = re.compile(r'^.*-(\d+vhost|\d+memif)-'
1089 r'(\d+chain|\d+pipe)-'
1090 r'(\d+vm|\d+dcr|\d+drc).*$')
1096 # Transform the data
1097 logging.info(" Creating the data set for the {0} '{1}'.".
1098 format(plot.get("type", ""), plot.get("title", "")))
1099 data = input_data.filter_data(plot, continue_on_error=True)
1100 if data is None or data.empty:
1101 logging.error("No data.")
1107 for tag in test['tags']:
1108 groups = re.search(REGEX_CN, tag)
1110 c = str(groups.group(1))
1111 n = str(groups.group(2))
1115 groups = re.search(REGEX_TEST_NAME, test["name"])
1116 if groups and len(groups.groups()) == 3:
1117 hover_name = "{vhost}-{chain}-{vm}".format(
1118 vhost=str(groups.group(1)),
1119 chain=str(groups.group(2)),
1120 vm=str(groups.group(3)))
1123 if vals.get(c, None) is None:
1125 if vals[c].get(n, None) is None:
1126 vals[c][n] = dict(name=hover_name,
1132 if plot["include-tests"] == "MRR":
1133 result = test["result"]["receive-rate"].avg
1134 elif plot["include-tests"] == "PDR":
1135 result = test["throughput"]["PDR"]["LOWER"]
1136 elif plot["include-tests"] == "NDR":
1137 result = test["throughput"]["NDR"]["LOWER"]
1144 vals[c][n]["vals"].append(result)
1147 logging.error("No data.")
1150 for key_c in vals.keys():
1151 txt_chains.append(key_c)
1152 for key_n in vals[key_c].keys():
1153 txt_nodes.append(key_n)
1154 if vals[key_c][key_n]["vals"]:
1155 vals[key_c][key_n]["nr"] = len(vals[key_c][key_n]["vals"])
1156 vals[key_c][key_n]["mean"] = \
1157 round(mean(vals[key_c][key_n]["vals"]) / 1000000, 1)
1158 vals[key_c][key_n]["stdev"] = \
1159 round(stdev(vals[key_c][key_n]["vals"]) / 1000000, 1)
1160 txt_nodes = list(set(txt_nodes))
1162 txt_chains = sorted(txt_chains, key=lambda chain: int(chain))
1163 txt_nodes = sorted(txt_nodes, key=lambda node: int(node))
1165 chains = [i + 1 for i in range(len(txt_chains))]
1166 nodes = [i + 1 for i in range(len(txt_nodes))]
1168 data = [list() for _ in range(len(chains))]
1172 val = vals[txt_chains[c - 1]][txt_nodes[n - 1]]["mean"]
1173 except (KeyError, IndexError):
1175 data[c - 1].append(val)
1178 my_green = [[0.0, 'rgb(235, 249, 242)'],
1179 [1.0, 'rgb(45, 134, 89)']]
1181 my_blue = [[0.0, 'rgb(236, 242, 248)'],
1182 [1.0, 'rgb(57, 115, 172)']]
1184 my_grey = [[0.0, 'rgb(230, 230, 230)'],
1185 [1.0, 'rgb(102, 102, 102)']]
1188 annotations = list()
1190 text = ("Test: {name}<br>"
1195 for c in range(len(txt_chains)):
1197 for n in range(len(txt_nodes)):
1198 if data[c][n] is not None:
1199 annotations.append(dict(
1206 text=str(data[c][n]),
1213 hover_line.append(text.format(
1214 name=vals[txt_chains[c]][txt_nodes[n]]["name"],
1215 nr=vals[txt_chains[c]][txt_nodes[n]]["nr"],
1217 stdev=vals[txt_chains[c]][txt_nodes[n]]["stdev"]))
1218 hovertext.append(hover_line)
1221 plgo.Heatmap(x=nodes,
1225 title=plot.get("z-axis", ""),
1239 colorscale=my_green,
1244 for idx, item in enumerate(txt_nodes):
1246 annotations.append(dict(
1260 for idx, item in enumerate(txt_chains):
1262 annotations.append(dict(
1277 annotations.append(dict(
1284 text=plot.get("x-axis", ""),
1292 annotations.append(dict(
1299 text=plot.get("y-axis", ""),
1307 updatemenus = list([
1316 args=[{"colorscale": [my_green, ], "reversescale": False}],
1321 args=[{"colorscale": [my_blue, ], "reversescale": False}],
1326 args=[{"colorscale": [my_grey, ], "reversescale": False}],
1335 layout = deepcopy(plot["layout"])
1336 except KeyError as err:
1337 logging.error("Finished with error: No layout defined")
1338 logging.error(repr(err))
1341 layout["annotations"] = annotations
1342 layout['updatemenus'] = updatemenus
1346 plpl = plgo.Figure(data=traces, layout=layout)
1349 logging.info(" Writing file '{0}{1}'.".
1350 format(plot["output-file"], plot["output-file-type"]))
1351 ploff.plot(plpl, show_link=False, auto_open=False,
1352 filename='{0}{1}'.format(plot["output-file"],
1353 plot["output-file-type"]))
1354 except PlotlyError as err:
1355 logging.error(" Finished with error: {}".
1356 format(str(err).replace("\n", " ")))
1360 def plot_service_density_heatmap_compare(plot, input_data):
1361 """Generate the plot(s) with algorithm: plot_service_density_heatmap_compare
1362 specified in the specification file.
1364 :param plot: Plot to generate.
1365 :param input_data: Data to process.
1366 :type plot: pandas.Series
1367 :type input_data: InputData
1370 REGEX_CN = re.compile(r'^(\d*)R(\d*)C$')
1371 REGEX_TEST_NAME = re.compile(r'^.*-(\d+ch|\d+pl)-'
1373 r'(\d+vm|\d+dcr).*$')
1374 REGEX_THREADS = re.compile(r'^(\d+)(VM|DCR)(\d+)T$')
1380 # Transform the data
1381 logging.info(" Creating the data set for the {0} '{1}'.".
1382 format(plot.get("type", ""), plot.get("title", "")))
1383 data = input_data.filter_data(plot, continue_on_error=True)
1384 if data is None or data.empty:
1385 logging.error("No data.")
1391 for tag in test['tags']:
1392 groups = re.search(REGEX_CN, tag)
1394 c = str(groups.group(1))
1395 n = str(groups.group(2))
1399 groups = re.search(REGEX_TEST_NAME, test["name"])
1400 if groups and len(groups.groups()) == 3:
1401 hover_name = "{chain}-{vhost}-{vm}".format(
1402 chain=str(groups.group(1)),
1403 vhost=str(groups.group(2)),
1404 vm=str(groups.group(3)))
1407 if vals.get(c, None) is None:
1409 if vals[c].get(n, None) is None:
1410 vals[c][n] = dict(name=hover_name,
1420 if plot["include-tests"] == "MRR":
1421 result = test["result"]["receive-rate"].avg
1422 elif plot["include-tests"] == "PDR":
1423 result = test["throughput"]["PDR"]["LOWER"]
1424 elif plot["include-tests"] == "NDR":
1425 result = test["throughput"]["NDR"]["LOWER"]
1432 for tag in test['tags']:
1433 groups = re.search(REGEX_THREADS, tag)
1434 if groups and len(groups.groups()) == 3:
1435 if str(groups.group(3)) == \
1436 plot["reference"]["include"]:
1437 vals[c][n]["vals_r"].append(result)
1438 elif str(groups.group(3)) == \
1439 plot["compare"]["include"]:
1440 vals[c][n]["vals_c"].append(result)
1443 logging.error("No data.")
1446 for key_c in vals.keys():
1447 txt_chains.append(key_c)
1448 for key_n in vals[key_c].keys():
1449 txt_nodes.append(key_n)
1450 if vals[key_c][key_n]["vals_r"]:
1451 vals[key_c][key_n]["nr_r"] = len(vals[key_c][key_n]["vals_r"])
1452 vals[key_c][key_n]["mean_r"] = \
1453 mean(vals[key_c][key_n]["vals_r"])
1454 vals[key_c][key_n]["stdev_r"] = \
1455 round(stdev(vals[key_c][key_n]["vals_r"]) / 1000000, 1)
1456 if vals[key_c][key_n]["vals_c"]:
1457 vals[key_c][key_n]["nr_c"] = len(vals[key_c][key_n]["vals_c"])
1458 vals[key_c][key_n]["mean_c"] = \
1459 mean(vals[key_c][key_n]["vals_c"])
1460 vals[key_c][key_n]["stdev_c"] = \
1461 round(stdev(vals[key_c][key_n]["vals_c"]) / 1000000, 1)
1463 txt_nodes = list(set(txt_nodes))
1465 txt_chains = sorted(txt_chains, key=lambda chain: int(chain))
1466 txt_nodes = sorted(txt_nodes, key=lambda node: int(node))
1468 chains = [i + 1 for i in range(len(txt_chains))]
1469 nodes = [i + 1 for i in range(len(txt_nodes))]
1471 data_r = [list() for _ in range(len(chains))]
1472 data_c = [list() for _ in range(len(chains))]
1473 diff = [list() for _ in range(len(chains))]
1477 val_r = vals[txt_chains[c - 1]][txt_nodes[n - 1]]["mean_r"]
1478 except (KeyError, IndexError):
1481 val_c = vals[txt_chains[c - 1]][txt_nodes[n - 1]]["mean_c"]
1482 except (KeyError, IndexError):
1484 if val_c is not None and val_r:
1485 val_d = (val_c - val_r) * 100 / val_r
1489 if val_r is not None:
1490 val_r = round(val_r / 1000000, 1)
1491 data_r[c - 1].append(val_r)
1492 if val_c is not None:
1493 val_c = round(val_c / 1000000, 1)
1494 data_c[c - 1].append(val_c)
1495 if val_d is not None:
1496 val_d = int(round(val_d, 0))
1497 diff[c - 1].append(val_d)
1500 my_green = [[0.0, 'rgb(235, 249, 242)'],
1501 [1.0, 'rgb(45, 134, 89)']]
1503 my_blue = [[0.0, 'rgb(236, 242, 248)'],
1504 [1.0, 'rgb(57, 115, 172)']]
1506 my_grey = [[0.0, 'rgb(230, 230, 230)'],
1507 [1.0, 'rgb(102, 102, 102)']]
1511 annotations = list()
1512 annotations_r = list()
1513 annotations_c = list()
1514 annotations_diff = list()
1516 text = ("Test: {name}"
1517 "<br>{title_r}: {text_r}"
1518 "<br>{title_c}: {text_c}{text_diff}")
1519 text_r = "Thput: {val_r}; StDev: {stdev_r}; Runs: {nr_r}"
1520 text_c = "Thput: {val_c}; StDev: {stdev_c}; Runs: {nr_c}"
1521 text_diff = "<br>Relative Difference {title_c} vs. {title_r}: {diff}%"
1523 for c in range(len(txt_chains)):
1525 for n in range(len(txt_nodes)):
1541 point_text_r = "Not present"
1542 point_text_c = "Not present"
1543 point_text_diff = ""
1545 point_r = data_r[c][n]
1546 if point_r is not None:
1547 point_text_r = text_r.format(
1549 stdev_r=vals[txt_chains[c]][txt_nodes[n]]["stdev_r"],
1550 nr_r=vals[txt_chains[c]][txt_nodes[n]]["nr_r"])
1553 point["text"] = "" if point_r is None else point_r
1554 annotations_r.append(deepcopy(point))
1557 point_c = data_c[c][n]
1558 if point_c is not None:
1559 point_text_c = text_c.format(
1561 stdev_c=vals[txt_chains[c]][txt_nodes[n]]["stdev_c"],
1562 nr_c=vals[txt_chains[c]][txt_nodes[n]]["nr_c"])
1565 point["text"] = "" if point_c is None else point_c
1566 annotations_c.append(deepcopy(point))
1569 point_d = diff[c][n]
1570 if point_d is not None:
1571 point_text_diff = text_diff.format(
1572 title_r=plot["reference"]["name"],
1573 title_c=plot["compare"]["name"],
1577 point["text"] = "" if point_d is None else point_d
1578 annotations_diff.append(deepcopy(point))
1581 name = vals[txt_chains[c]][txt_nodes[n]]["name"]
1585 hover_line.append(text.format(
1587 title_r=plot["reference"]["name"],
1588 text_r=point_text_r,
1589 title_c=plot["compare"]["name"],
1590 text_c=point_text_c,
1591 text_diff=point_text_diff
1594 hovertext.append(hover_line)
1597 plgo.Heatmap(x=nodes,
1602 title=plot.get("z-axis", ""),
1616 colorscale=my_green,
1620 plgo.Heatmap(x=nodes,
1625 title=plot.get("z-axis", ""),
1643 plgo.Heatmap(x=nodes,
1649 title="Relative Difference {name_c} vs. {name_r} [%]".
1650 format(name_c=plot["compare"]["name"],
1651 name_r=plot["reference"]["name"]),
1671 for idx, item in enumerate(txt_nodes):
1673 annotations.append(dict(
1687 for idx, item in enumerate(txt_chains):
1689 annotations.append(dict(
1704 annotations.append(dict(
1711 text=plot.get("x-axis", ""),
1719 annotations.append(dict(
1726 text=plot.get("y-axis", ""),
1734 updatemenus = list([
1744 label=plot["reference"]["name"],
1748 "visible": [True, False, False]
1751 "colorscale": [my_green, ],
1752 "reversescale": False,
1753 "annotations": annotations + annotations_r,
1758 label=plot["compare"]["name"],
1762 "visible": [False, True, False]
1765 "colorscale": [my_blue, ],
1766 "reversescale": False,
1767 "annotations": annotations + annotations_c,
1776 "visible": [False, False, True]
1779 "colorscale": [my_grey, ],
1780 "reversescale": False,
1781 "annotations": annotations + annotations_diff,
1790 layout = deepcopy(plot["layout"])
1791 except KeyError as err:
1792 logging.error("Finished with error: No layout defined")
1793 logging.error(repr(err))
1796 layout["annotations"] = annotations + annotations_r
1797 layout['updatemenus'] = updatemenus
1801 plpl = plgo.Figure(data=traces, layout=layout)
1804 logging.info(" Writing file '{0}{1}'.".
1805 format(plot["output-file"], plot["output-file-type"]))
1806 ploff.plot(plpl, show_link=False, auto_open=False,
1807 filename='{0}{1}'.format(plot["output-file"],
1808 plot["output-file-type"]))
1809 except PlotlyError as err:
1810 logging.error(" Finished with error: {}".
1811 format(str(err).replace("\n", " ")))