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_name_box(plot, input_data):
65 """Generate the plot(s) with algorithm: plot_performance_name_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_tests_by_name(
79 plot, params=["throughput", "parent", "tags", "type"])
81 logging.error("No data.")
84 # Prepare the data for the plot
85 y_vals = OrderedDict()
89 if y_vals.get(test["parent"], None) is None:
90 y_vals[test["parent"]] = list()
92 if test["type"] in ("NDRPDR", ):
93 if "-pdr" in plot_title.lower():
94 y_vals[test["parent"]].\
95 append(test["throughput"]["PDR"]["LOWER"])
96 elif "-ndr" in plot_title.lower():
97 y_vals[test["parent"]]. \
98 append(test["throughput"]["NDR"]["LOWER"])
101 elif test["type"] in ("SOAK", ):
102 y_vals[test["parent"]].\
103 append(test["throughput"]["LOWER"])
106 except (KeyError, TypeError):
107 y_vals[test["parent"]].append(None)
109 # Add None to the lists with missing data
111 nr_of_samples = list()
112 for val in y_vals.values():
113 if len(val) > max_len:
115 nr_of_samples.append(len(val))
116 for key, val in y_vals.items():
117 if len(val) < max_len:
118 val.extend([None for _ in range(max_len - len(val))])
122 df = pd.DataFrame(y_vals)
125 for i, col in enumerate(df.columns):
126 tst_name = re.sub(REGEX_NIC, "",
127 col.lower().replace('-ndrpdr', '').
128 replace('2n1l-', ''))
129 name = "{nr}. ({samples:02d} run{plural}) {name}".\
131 samples=nr_of_samples[i],
132 plural='s' if nr_of_samples[i] > 1 else '',
136 traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
137 y=[y / 1000000 if y else None for y in df[col]],
140 boxpoints="outliers",
143 val_max = max(df[col])
144 except ValueError as err:
145 logging.error(repr(err))
148 y_max.append(int(val_max / 1000000) + 2)
152 layout = deepcopy(plot["layout"])
153 if layout.get("title", None):
154 layout["title"] = "<b>Throughput:</b> {0}". \
155 format(layout["title"])
157 layout["yaxis"]["range"] = [0, max(y_max)]
158 plpl = plgo.Figure(data=traces, layout=layout)
161 file_type = plot.get("output-file-type", ".html")
162 logging.info(" Writing file '{0}{1}'.".
163 format(plot["output-file"], file_type))
164 ploff.plot(plpl, show_link=False, auto_open=False,
165 filename='{0}{1}'.format(plot["output-file"], file_type))
166 except PlotlyError as err:
167 logging.error(" Finished with error: {}".
168 format(repr(err).replace("\n", " ")))
172 def plot_latency_error_bars_name(plot, input_data):
173 """Generate the plot(s) with algorithm: plot_latency_error_bars_name
174 specified in the specification file.
176 :param plot: Plot to generate.
177 :param input_data: Data to process.
178 :type plot: pandas.Series
179 :type input_data: InputData
183 plot_title = plot.get("title", "")
184 logging.info(" Creating the data set for the {0} '{1}'.".
185 format(plot.get("type", ""), plot_title))
186 data = input_data.filter_tests_by_name(
187 plot, params=["latency", "parent", "tags", "type"])
189 logging.error("No data.")
192 # Prepare the data for the plot
193 y_tmp_vals = OrderedDict()
198 logging.debug("test['latency']: {0}\n".
199 format(test["latency"]))
200 except ValueError as err:
201 logging.warning(repr(err))
202 if y_tmp_vals.get(test["parent"], None) is None:
203 y_tmp_vals[test["parent"]] = [
204 list(), # direction1, min
205 list(), # direction1, avg
206 list(), # direction1, max
207 list(), # direction2, min
208 list(), # direction2, avg
209 list() # direction2, max
212 if test["type"] in ("NDRPDR", ):
213 if "-pdr" in plot_title.lower():
215 elif "-ndr" in plot_title.lower():
218 logging.warning("Invalid test type: {0}".
219 format(test["type"]))
221 y_tmp_vals[test["parent"]][0].append(
222 test["latency"][ttype]["direction1"]["min"])
223 y_tmp_vals[test["parent"]][1].append(
224 test["latency"][ttype]["direction1"]["avg"])
225 y_tmp_vals[test["parent"]][2].append(
226 test["latency"][ttype]["direction1"]["max"])
227 y_tmp_vals[test["parent"]][3].append(
228 test["latency"][ttype]["direction2"]["min"])
229 y_tmp_vals[test["parent"]][4].append(
230 test["latency"][ttype]["direction2"]["avg"])
231 y_tmp_vals[test["parent"]][5].append(
232 test["latency"][ttype]["direction2"]["max"])
234 logging.warning("Invalid test type: {0}".
235 format(test["type"]))
237 except (KeyError, TypeError) as err:
238 logging.warning(repr(err))
244 nr_of_samples = list()
245 for key, val in y_tmp_vals.items():
246 name = re.sub(REGEX_NIC, "", key.replace('-ndrpdr', '').
247 replace('2n1l-', ''))
248 x_vals.append(name) # dir 1
249 y_vals.append(mean(val[1]) if val[1] else None)
250 y_mins.append(mean(val[0]) if val[0] else None)
251 y_maxs.append(mean(val[2]) if val[2] else None)
252 nr_of_samples.append(len(val[1]) if val[1] else 0)
253 x_vals.append(name) # dir 2
254 y_vals.append(mean(val[4]) if val[4] else None)
255 y_mins.append(mean(val[3]) if val[3] else None)
256 y_maxs.append(mean(val[5]) if val[5] else None)
257 nr_of_samples.append(len(val[3]) if val[3] else 0)
262 for idx in range(len(x_vals)):
263 if not bool(int(idx % 2)):
264 direction = "West-East"
266 direction = "East-West"
267 hovertext = ("No. of Runs: {nr}<br>"
269 "Direction: {dir}<br>".format(test=x_vals[idx],
271 nr=nr_of_samples[idx]))
272 if isinstance(y_maxs[idx], float):
273 hovertext += "Max: {max:.2f}uSec<br>".format(max=y_maxs[idx])
274 if isinstance(y_vals[idx], float):
275 hovertext += "Mean: {avg:.2f}uSec<br>".format(avg=y_vals[idx])
276 if isinstance(y_mins[idx], float):
277 hovertext += "Min: {min:.2f}uSec".format(min=y_mins[idx])
279 if isinstance(y_maxs[idx], float) and isinstance(y_vals[idx], float):
280 array = [y_maxs[idx] - y_vals[idx], ]
283 if isinstance(y_mins[idx], float) and isinstance(y_vals[idx], float):
284 arrayminus = [y_vals[idx] - y_mins[idx], ]
286 arrayminus = [None, ]
287 traces.append(plgo.Scatter(
291 legendgroup=x_vals[idx],
292 showlegend=bool(int(idx % 2)),
298 arrayminus=arrayminus,
299 color=COLORS[int(idx / 2)]
303 color=COLORS[int(idx / 2)],
308 annotations.append(dict(
315 text="E-W" if bool(int(idx % 2)) else "W-E",
325 file_type = plot.get("output-file-type", ".html")
326 logging.info(" Writing file '{0}{1}'.".
327 format(plot["output-file"], file_type))
328 layout = deepcopy(plot["layout"])
329 if layout.get("title", None):
330 layout["title"] = "<b>Latency:</b> {0}".\
331 format(layout["title"])
332 layout["annotations"] = annotations
333 plpl = plgo.Figure(data=traces, layout=layout)
337 show_link=False, auto_open=False,
338 filename='{0}{1}'.format(plot["output-file"], file_type))
339 except PlotlyError as err:
340 logging.error(" Finished with error: {}".
341 format(str(err).replace("\n", " ")))
345 def plot_throughput_speedup_analysis_name(plot, input_data):
346 """Generate the plot(s) with algorithm:
347 plot_throughput_speedup_analysis_name
348 specified in the specification file.
350 :param plot: Plot to generate.
351 :param input_data: Data to process.
352 :type plot: pandas.Series
353 :type input_data: InputData
357 plot_title = plot.get("title", "")
358 logging.info(" Creating the data set for the {0} '{1}'.".
359 format(plot.get("type", ""), plot_title))
360 data = input_data.filter_tests_by_name(
361 plot, params=["throughput", "parent", "tags", "type"])
363 logging.error("No data.")
366 y_vals = OrderedDict()
370 if y_vals.get(test["parent"], None) is None:
371 y_vals[test["parent"]] = {"1": list(),
375 if test["type"] in ("NDRPDR",):
376 if "-pdr" in plot_title.lower():
378 elif "-ndr" in plot_title.lower():
382 if "1C" in test["tags"]:
383 y_vals[test["parent"]]["1"]. \
384 append(test["throughput"][ttype]["LOWER"])
385 elif "2C" in test["tags"]:
386 y_vals[test["parent"]]["2"]. \
387 append(test["throughput"][ttype]["LOWER"])
388 elif "4C" in test["tags"]:
389 y_vals[test["parent"]]["4"]. \
390 append(test["throughput"][ttype]["LOWER"])
391 except (KeyError, TypeError):
395 logging.warning("No data for the plot '{}'".
396 format(plot.get("title", "")))
400 for test_name, test_vals in y_vals.items():
401 for key, test_val in test_vals.items():
403 avg_val = sum(test_val) / len(test_val)
404 y_vals[test_name][key] = (avg_val, len(test_val))
405 ideal = avg_val / (int(key) * 1000000.0)
406 if test_name not in y_1c_max or ideal > y_1c_max[test_name]:
407 y_1c_max[test_name] = ideal
413 pci_limit = plot["limits"]["pci"]["pci-g3-x8"]
414 for test_name, test_vals in y_vals.items():
416 if test_vals["1"][1]:
417 name = re.sub(REGEX_NIC, "", test_name.replace('-ndrpdr', '').
418 replace('2n1l-', ''))
419 vals[name] = OrderedDict()
420 y_val_1 = test_vals["1"][0] / 1000000.0
421 y_val_2 = test_vals["2"][0] / 1000000.0 if test_vals["2"][0] \
423 y_val_4 = test_vals["4"][0] / 1000000.0 if test_vals["4"][0] \
426 vals[name]["val"] = [y_val_1, y_val_2, y_val_4]
427 vals[name]["rel"] = [1.0, None, None]
428 vals[name]["ideal"] = [y_1c_max[test_name],
429 y_1c_max[test_name] * 2,
430 y_1c_max[test_name] * 4]
431 vals[name]["diff"] = [(y_val_1 - y_1c_max[test_name]) * 100 /
433 vals[name]["count"] = [test_vals["1"][1],
438 val_max = max(vals[name]["val"])
439 except ValueError as err:
440 logging.error(repr(err))
443 y_max.append(val_max)
446 vals[name]["rel"][1] = round(y_val_2 / y_val_1, 2)
447 vals[name]["diff"][1] = \
448 (y_val_2 - vals[name]["ideal"][1]) * 100 / y_val_2
450 vals[name]["rel"][2] = round(y_val_4 / y_val_1, 2)
451 vals[name]["diff"][2] = \
452 (y_val_4 - vals[name]["ideal"][2]) * 100 / y_val_4
453 except IndexError as err:
454 logging.warning("No data for '{0}'".format(test_name))
455 logging.warning(repr(err))
458 if "x520" in test_name:
459 limit = plot["limits"]["nic"]["x520"]
460 elif "x710" in test_name:
461 limit = plot["limits"]["nic"]["x710"]
462 elif "xxv710" in test_name:
463 limit = plot["limits"]["nic"]["xxv710"]
464 elif "xl710" in test_name:
465 limit = plot["limits"]["nic"]["xl710"]
466 elif "x553" in test_name:
467 limit = plot["limits"]["nic"]["x553"]
470 if limit > nic_limit:
473 mul = 2 if "ge2p" in test_name else 1
474 if "10ge" in test_name:
475 limit = plot["limits"]["link"]["10ge"] * mul
476 elif "25ge" in test_name:
477 limit = plot["limits"]["link"]["25ge"] * mul
478 elif "40ge" in test_name:
479 limit = plot["limits"]["link"]["40ge"] * mul
480 elif "100ge" in test_name:
481 limit = plot["limits"]["link"]["100ge"] * mul
484 if limit > lnk_limit:
493 threshold = 1.1 * max(y_max) # 10%
494 except ValueError as err:
497 nic_limit /= 1000000.0
498 traces.append(plgo.Scatter(
500 y=[nic_limit, ] * len(x_vals),
501 name="NIC: {0:.2f}Mpps".format(nic_limit),
510 annotations.append(dict(
517 text="NIC: {0:.2f}Mpps".format(nic_limit),
525 y_max.append(nic_limit)
527 lnk_limit /= 1000000.0
528 if lnk_limit < threshold:
529 traces.append(plgo.Scatter(
531 y=[lnk_limit, ] * len(x_vals),
532 name="Link: {0:.2f}Mpps".format(lnk_limit),
541 annotations.append(dict(
548 text="Link: {0:.2f}Mpps".format(lnk_limit),
556 y_max.append(lnk_limit)
558 pci_limit /= 1000000.0
559 if (pci_limit < threshold and
560 (pci_limit < lnk_limit * 0.95 or lnk_limit > lnk_limit * 1.05)):
561 traces.append(plgo.Scatter(
563 y=[pci_limit, ] * len(x_vals),
564 name="PCIe: {0:.2f}Mpps".format(pci_limit),
573 annotations.append(dict(
580 text="PCIe: {0:.2f}Mpps".format(pci_limit),
588 y_max.append(pci_limit)
590 # Perfect and measured:
592 for name, val in vals.iteritems():
595 for idx in range(len(val["val"])):
597 if isinstance(val["val"][idx], float):
598 htext += "No. of Runs: {1}<br>" \
599 "Mean: {0:.2f}Mpps<br>".format(val["val"][idx],
601 if isinstance(val["diff"][idx], float):
602 htext += "Diff: {0:.0f}%<br>".format(
603 round(val["diff"][idx]))
604 if isinstance(val["rel"][idx], float):
605 htext += "Speedup: {0:.2f}".format(val["rel"][idx])
606 hovertext.append(htext)
607 traces.append(plgo.Scatter(x=x_vals,
611 mode="lines+markers",
620 hoverinfo="text+name"
622 traces.append(plgo.Scatter(x=x_vals,
624 name="{0} perfect".format(name),
632 text=["Perfect: {0:.2f}Mpps".format(y)
633 for y in val["ideal"]],
637 except (IndexError, ValueError, KeyError) as err:
638 logging.warning("No data for '{0}'".format(name))
639 logging.warning(repr(err))
643 file_type = plot.get("output-file-type", ".html")
644 logging.info(" Writing file '{0}{1}'.".
645 format(plot["output-file"], file_type))
646 layout = deepcopy(plot["layout"])
647 if layout.get("title", None):
648 layout["title"] = "<b>Speedup Multi-core:</b> {0}". \
649 format(layout["title"])
650 layout["yaxis"]["range"] = [0, int(max(y_max) * 1.1)]
651 layout["annotations"].extend(annotations)
652 plpl = plgo.Figure(data=traces, layout=layout)
656 show_link=False, auto_open=False,
657 filename='{0}{1}'.format(plot["output-file"], file_type))
658 except PlotlyError as err:
659 logging.error(" Finished with error: {}".
660 format(repr(err).replace("\n", " ")))
664 def plot_performance_box(plot, input_data):
665 """Generate the plot(s) with algorithm: plot_performance_box
666 specified in the specification file.
668 TODO: Remove when not needed.
670 :param plot: Plot to generate.
671 :param input_data: Data to process.
672 :type plot: pandas.Series
673 :type input_data: InputData
677 plot_title = plot.get("title", "")
678 logging.info(" Creating the data set for the {0} '{1}'.".
679 format(plot.get("type", ""), plot_title))
680 data = input_data.filter_data(plot)
682 logging.error("No data.")
685 # Prepare the data for the plot
691 if y_vals.get(test["parent"], None) is None:
692 y_vals[test["parent"]] = list()
693 y_tags[test["parent"]] = test.get("tags", None)
695 if test["type"] in ("NDRPDR", ):
696 if "-pdr" in plot_title.lower():
697 y_vals[test["parent"]].\
698 append(test["throughput"]["PDR"]["LOWER"])
699 elif "-ndr" in plot_title.lower():
700 y_vals[test["parent"]]. \
701 append(test["throughput"]["NDR"]["LOWER"])
704 elif test["type"] in ("SOAK", ):
705 y_vals[test["parent"]].\
706 append(test["throughput"]["LOWER"])
709 except (KeyError, TypeError):
710 y_vals[test["parent"]].append(None)
713 order = plot.get("sort", None)
715 y_sorted = OrderedDict()
716 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
719 for suite, tags in y_tags_l.items():
721 tag = tag.split(" ")[-1]
722 if tag.lower() in tags:
725 if tag.lower() not in tags:
728 y_sorted[suite] = y_vals.pop(suite)
731 except KeyError as err:
732 logging.error("Not found: {0}".format(repr(err)))
738 # Add None to the lists with missing data
740 nr_of_samples = list()
741 for val in y_sorted.values():
742 if len(val) > max_len:
744 nr_of_samples.append(len(val))
745 for key, val in y_sorted.items():
746 if len(val) < max_len:
747 val.extend([None for _ in range(max_len - len(val))])
751 df = pd.DataFrame(y_sorted)
754 for i, col in enumerate(df.columns):
755 tst_name = re.sub(REGEX_NIC, "",
756 col.lower().replace('-ndrpdr', '').
757 replace('2n1l-', ''))
758 name = "{nr}. ({samples:02d} run{plural}) {name}".\
760 samples=nr_of_samples[i],
761 plural='s' if nr_of_samples[i] > 1 else '',
765 traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
766 y=[y / 1000000 if y else None for y in df[col]],
770 val_max = max(df[col])
771 except ValueError as err:
772 logging.error(repr(err))
775 y_max.append(int(val_max / 1000000) + 2)
779 layout = deepcopy(plot["layout"])
780 if layout.get("title", None):
781 layout["title"] = "<b>Throughput:</b> {0}". \
782 format(layout["title"])
784 layout["yaxis"]["range"] = [0, max(y_max)]
785 plpl = plgo.Figure(data=traces, layout=layout)
788 logging.info(" Writing file '{0}{1}'.".
789 format(plot["output-file"], plot["output-file-type"]))
790 ploff.plot(plpl, show_link=False, auto_open=False,
791 filename='{0}{1}'.format(plot["output-file"],
792 plot["output-file-type"]))
793 except PlotlyError as err:
794 logging.error(" Finished with error: {}".
795 format(repr(err).replace("\n", " ")))
799 def plot_soak_bars(plot, input_data):
800 """Generate the plot(s) with algorithm: plot_soak_bars
801 specified in the specification file.
803 :param plot: Plot to generate.
804 :param input_data: Data to process.
805 :type plot: pandas.Series
806 :type input_data: InputData
810 plot_title = plot.get("title", "")
811 logging.info(" Creating the data set for the {0} '{1}'.".
812 format(plot.get("type", ""), plot_title))
813 data = input_data.filter_data(plot)
815 logging.error("No data.")
818 # Prepare the data for the plot
824 if y_vals.get(test["parent"], None) is None:
825 y_tags[test["parent"]] = test.get("tags", None)
827 if test["type"] in ("SOAK", ):
828 y_vals[test["parent"]] = test["throughput"]
831 except (KeyError, TypeError):
832 y_vals[test["parent"]] = dict()
835 order = plot.get("sort", None)
837 y_sorted = OrderedDict()
838 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
841 for suite, tags in y_tags_l.items():
843 tag = tag.split(" ")[-1]
844 if tag.lower() in tags:
847 if tag.lower() not in tags:
850 y_sorted[suite] = y_vals.pop(suite)
853 except KeyError as err:
854 logging.error("Not found: {0}".format(repr(err)))
863 for test_name, test_data in y_sorted.items():
865 name = "{nr}. {name}".\
866 format(nr=idx, name=test_name.lower().replace('-soak', ''))
868 name_lst = name.split('-')
871 for segment in name_lst:
872 if (len(name) + len(segment) + 1) > 50 and split_name:
875 name += segment + '-'
878 y_val = test_data.get("LOWER", None)
884 time = "No Information"
885 result = "No Information"
886 hovertext = ("{name}<br>"
887 "Packet Throughput: {val:.2f}Mpps<br>"
888 "Final Duration: {time}<br>"
889 "Result: {result}".format(name=name,
893 traces.append(plgo.Bar(x=[str(idx) + '.', ],
900 layout = deepcopy(plot["layout"])
901 if layout.get("title", None):
902 layout["title"] = "<b>Packet Throughput:</b> {0}". \
903 format(layout["title"])
905 layout["yaxis"]["range"] = [0, y_max + 1]
906 plpl = plgo.Figure(data=traces, layout=layout)
908 logging.info(" Writing file '{0}{1}'.".
909 format(plot["output-file"], plot["output-file-type"]))
910 ploff.plot(plpl, show_link=False, auto_open=False,
911 filename='{0}{1}'.format(plot["output-file"],
912 plot["output-file-type"]))
913 except PlotlyError as err:
914 logging.error(" Finished with error: {}".
915 format(repr(err).replace("\n", " ")))
919 def plot_soak_boxes(plot, input_data):
920 """Generate the plot(s) with algorithm: plot_soak_boxes
921 specified in the specification file.
923 :param plot: Plot to generate.
924 :param input_data: Data to process.
925 :type plot: pandas.Series
926 :type input_data: InputData
930 plot_title = plot.get("title", "")
931 logging.info(" Creating the data set for the {0} '{1}'.".
932 format(plot.get("type", ""), plot_title))
933 data = input_data.filter_data(plot)
935 logging.error("No data.")
938 # Prepare the data for the plot
944 if y_vals.get(test["parent"], None) is None:
945 y_tags[test["parent"]] = test.get("tags", None)
947 if test["type"] in ("SOAK", ):
948 y_vals[test["parent"]] = test["throughput"]
951 except (KeyError, TypeError):
952 y_vals[test["parent"]] = dict()
955 order = plot.get("sort", None)
957 y_sorted = OrderedDict()
958 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
961 for suite, tags in y_tags_l.items():
963 tag = tag.split(" ")[-1]
964 if tag.lower() in tags:
967 if tag.lower() not in tags:
970 y_sorted[suite] = y_vals.pop(suite)
973 except KeyError as err:
974 logging.error("Not found: {0}".format(repr(err)))
983 for test_name, test_data in y_sorted.items():
985 name = "{nr}. {name}".\
986 format(nr=idx, name=test_name.lower().replace('-soak', '').
987 replace('2n1l-', ''))
989 name_lst = name.split('-')
992 for segment in name_lst:
993 if (len(name) + len(segment) + 1) > 55 and split_name:
996 name += segment + '-'
999 y_val = test_data.get("UPPER", None)
1005 y_base = test_data.get("LOWER", None)
1009 hovertext = ("Upper bound: {upper:.2f}<br>"
1010 "Lower bound: {lower:.2f}".format(upper=y_val,
1012 traces.append(plgo.Bar(x=[str(idx) + '.', ],
1013 # +0.05 to see the value in case lower == upper
1014 y=[y_val - y_base + 0.05, ],
1021 layout = deepcopy(plot["layout"])
1022 if layout.get("title", None):
1023 layout["title"] = "<b>Throughput:</b> {0}". \
1024 format(layout["title"])
1026 layout["yaxis"]["range"] = [0, y_max + 1]
1027 plpl = plgo.Figure(data=traces, layout=layout)
1029 logging.info(" Writing file '{0}{1}'.".
1030 format(plot["output-file"], plot["output-file-type"]))
1031 ploff.plot(plpl, show_link=False, auto_open=False,
1032 filename='{0}{1}'.format(plot["output-file"],
1033 plot["output-file-type"]))
1034 except PlotlyError as err:
1035 logging.error(" Finished with error: {}".
1036 format(repr(err).replace("\n", " ")))
1040 def plot_latency_error_bars(plot, input_data):
1041 """Generate the plot(s) with algorithm: plot_latency_error_bars
1042 specified in the specification file.
1044 TODO: Remove when not needed.
1046 :param plot: Plot to generate.
1047 :param input_data: Data to process.
1048 :type plot: pandas.Series
1049 :type input_data: InputData
1052 # Transform the data
1053 plot_title = plot.get("title", "")
1054 logging.info(" Creating the data set for the {0} '{1}'.".
1055 format(plot.get("type", ""), plot_title))
1056 data = input_data.filter_data(plot)
1058 logging.error("No data.")
1061 # Prepare the data for the plot
1068 logging.debug("test['latency']: {0}\n".
1069 format(test["latency"]))
1070 except ValueError as err:
1071 logging.warning(repr(err))
1072 if y_tmp_vals.get(test["parent"], None) is None:
1073 y_tmp_vals[test["parent"]] = [
1074 list(), # direction1, min
1075 list(), # direction1, avg
1076 list(), # direction1, max
1077 list(), # direction2, min
1078 list(), # direction2, avg
1079 list() # direction2, max
1081 y_tags[test["parent"]] = test.get("tags", None)
1083 if test["type"] in ("NDRPDR", ):
1084 if "-pdr" in plot_title.lower():
1086 elif "-ndr" in plot_title.lower():
1089 logging.warning("Invalid test type: {0}".
1090 format(test["type"]))
1092 y_tmp_vals[test["parent"]][0].append(
1093 test["latency"][ttype]["direction1"]["min"])
1094 y_tmp_vals[test["parent"]][1].append(
1095 test["latency"][ttype]["direction1"]["avg"])
1096 y_tmp_vals[test["parent"]][2].append(
1097 test["latency"][ttype]["direction1"]["max"])
1098 y_tmp_vals[test["parent"]][3].append(
1099 test["latency"][ttype]["direction2"]["min"])
1100 y_tmp_vals[test["parent"]][4].append(
1101 test["latency"][ttype]["direction2"]["avg"])
1102 y_tmp_vals[test["parent"]][5].append(
1103 test["latency"][ttype]["direction2"]["max"])
1105 logging.warning("Invalid test type: {0}".
1106 format(test["type"]))
1108 except (KeyError, TypeError) as err:
1109 logging.warning(repr(err))
1110 logging.debug("y_tmp_vals: {0}\n".format(y_tmp_vals))
1113 order = plot.get("sort", None)
1114 if order and y_tags:
1115 y_sorted = OrderedDict()
1116 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
1119 for suite, tags in y_tags_l.items():
1121 tag = tag.split(" ")[-1]
1122 if tag.lower() in tags:
1125 if tag.lower() not in tags:
1128 y_sorted[suite] = y_tmp_vals.pop(suite)
1130 logging.debug(suite)
1131 except KeyError as err:
1132 logging.error("Not found: {0}".format(repr(err)))
1136 y_sorted = y_tmp_vals
1138 logging.debug("y_sorted: {0}\n".format(y_sorted))
1143 nr_of_samples = list()
1144 for key, val in y_sorted.items():
1145 name = re.sub(REGEX_NIC, "", key.replace('-ndrpdr', '').
1146 replace('2n1l-', ''))
1147 x_vals.append(name) # dir 1
1148 y_vals.append(mean(val[1]) if val[1] else None)
1149 y_mins.append(mean(val[0]) if val[0] else None)
1150 y_maxs.append(mean(val[2]) if val[2] else None)
1151 nr_of_samples.append(len(val[1]) if val[1] else 0)
1152 x_vals.append(name) # dir 2
1153 y_vals.append(mean(val[4]) if val[4] else None)
1154 y_mins.append(mean(val[3]) if val[3] else None)
1155 y_maxs.append(mean(val[5]) if val[5] else None)
1156 nr_of_samples.append(len(val[3]) if val[3] else 0)
1158 logging.debug("x_vals :{0}\n".format(x_vals))
1159 logging.debug("y_vals :{0}\n".format(y_vals))
1160 logging.debug("y_mins :{0}\n".format(y_mins))
1161 logging.debug("y_maxs :{0}\n".format(y_maxs))
1162 logging.debug("nr_of_samples :{0}\n".format(nr_of_samples))
1164 annotations = list()
1166 for idx in range(len(x_vals)):
1167 if not bool(int(idx % 2)):
1168 direction = "West-East"
1170 direction = "East-West"
1171 hovertext = ("No. of Runs: {nr}<br>"
1173 "Direction: {dir}<br>".format(test=x_vals[idx],
1175 nr=nr_of_samples[idx]))
1176 if isinstance(y_maxs[idx], float):
1177 hovertext += "Max: {max:.2f}uSec<br>".format(max=y_maxs[idx])
1178 if isinstance(y_vals[idx], float):
1179 hovertext += "Mean: {avg:.2f}uSec<br>".format(avg=y_vals[idx])
1180 if isinstance(y_mins[idx], float):
1181 hovertext += "Min: {min:.2f}uSec".format(min=y_mins[idx])
1183 if isinstance(y_maxs[idx], float) and isinstance(y_vals[idx], float):
1184 array = [y_maxs[idx] - y_vals[idx], ]
1187 if isinstance(y_mins[idx], float) and isinstance(y_vals[idx], float):
1188 arrayminus = [y_vals[idx] - y_mins[idx], ]
1190 arrayminus = [None, ]
1191 logging.debug("y_vals[{1}] :{0}\n".format(y_vals[idx], idx))
1192 logging.debug("array :{0}\n".format(array))
1193 logging.debug("arrayminus :{0}\n".format(arrayminus))
1194 traces.append(plgo.Scatter(
1198 legendgroup=x_vals[idx],
1199 showlegend=bool(int(idx % 2)),
1205 arrayminus=arrayminus,
1206 color=COLORS[int(idx / 2)]
1210 color=COLORS[int(idx / 2)],
1215 annotations.append(dict(
1222 text="E-W" if bool(int(idx % 2)) else "W-E",
1232 logging.info(" Writing file '{0}{1}'.".
1233 format(plot["output-file"], plot["output-file-type"]))
1234 layout = deepcopy(plot["layout"])
1235 if layout.get("title", None):
1236 layout["title"] = "<b>Latency:</b> {0}".\
1237 format(layout["title"])
1238 layout["annotations"] = annotations
1239 plpl = plgo.Figure(data=traces, layout=layout)
1243 show_link=False, auto_open=False,
1244 filename='{0}{1}'.format(plot["output-file"],
1245 plot["output-file-type"]))
1246 except PlotlyError as err:
1247 logging.error(" Finished with error: {}".
1248 format(str(err).replace("\n", " ")))
1252 def plot_throughput_speedup_analysis(plot, input_data):
1253 """Generate the plot(s) with algorithm:
1254 plot_throughput_speedup_analysis
1255 specified in the specification file.
1257 TODO: Remove when not needed.
1259 :param plot: Plot to generate.
1260 :param input_data: Data to process.
1261 :type plot: pandas.Series
1262 :type input_data: InputData
1265 # Transform the data
1266 plot_title = plot.get("title", "")
1267 logging.info(" Creating the data set for the {0} '{1}'.".
1268 format(plot.get("type", ""), plot_title))
1269 data = input_data.filter_data(plot)
1271 logging.error("No data.")
1279 if y_vals.get(test["parent"], None) is None:
1280 y_vals[test["parent"]] = {"1": list(),
1283 y_tags[test["parent"]] = test.get("tags", None)
1285 if test["type"] in ("NDRPDR",):
1286 if "-pdr" in plot_title.lower():
1288 elif "-ndr" in plot_title.lower():
1292 if "1C" in test["tags"]:
1293 y_vals[test["parent"]]["1"]. \
1294 append(test["throughput"][ttype]["LOWER"])
1295 elif "2C" in test["tags"]:
1296 y_vals[test["parent"]]["2"]. \
1297 append(test["throughput"][ttype]["LOWER"])
1298 elif "4C" in test["tags"]:
1299 y_vals[test["parent"]]["4"]. \
1300 append(test["throughput"][ttype]["LOWER"])
1301 except (KeyError, TypeError):
1305 logging.warning("No data for the plot '{}'".
1306 format(plot.get("title", "")))
1310 for test_name, test_vals in y_vals.items():
1311 for key, test_val in test_vals.items():
1313 avg_val = sum(test_val) / len(test_val)
1314 y_vals[test_name][key] = (avg_val, len(test_val))
1315 ideal = avg_val / (int(key) * 1000000.0)
1316 if test_name not in y_1c_max or ideal > y_1c_max[test_name]:
1317 y_1c_max[test_name] = ideal
1323 pci_limit = plot["limits"]["pci"]["pci-g3-x8"]
1324 for test_name, test_vals in y_vals.items():
1326 if test_vals["1"][1]:
1327 name = re.sub(REGEX_NIC, "", test_name.replace('-ndrpdr', '').
1328 replace('2n1l-', ''))
1330 y_val_1 = test_vals["1"][0] / 1000000.0
1331 y_val_2 = test_vals["2"][0] / 1000000.0 if test_vals["2"][0] \
1333 y_val_4 = test_vals["4"][0] / 1000000.0 if test_vals["4"][0] \
1336 vals[name]["val"] = [y_val_1, y_val_2, y_val_4]
1337 vals[name]["rel"] = [1.0, None, None]
1338 vals[name]["ideal"] = [y_1c_max[test_name],
1339 y_1c_max[test_name] * 2,
1340 y_1c_max[test_name] * 4]
1341 vals[name]["diff"] = [(y_val_1 - y_1c_max[test_name]) * 100 /
1342 y_val_1, None, None]
1343 vals[name]["count"] = [test_vals["1"][1],
1348 # val_max = max(max(vals[name]["val"], vals[name]["ideal"]))
1349 val_max = max(vals[name]["val"])
1350 except ValueError as err:
1354 # y_max.append(int((val_max / 10) + 1) * 10)
1355 y_max.append(val_max)
1358 vals[name]["rel"][1] = round(y_val_2 / y_val_1, 2)
1359 vals[name]["diff"][1] = \
1360 (y_val_2 - vals[name]["ideal"][1]) * 100 / y_val_2
1362 vals[name]["rel"][2] = round(y_val_4 / y_val_1, 2)
1363 vals[name]["diff"][2] = \
1364 (y_val_4 - vals[name]["ideal"][2]) * 100 / y_val_4
1365 except IndexError as err:
1366 logging.warning("No data for '{0}'".format(test_name))
1367 logging.warning(repr(err))
1370 if "x520" in test_name:
1371 limit = plot["limits"]["nic"]["x520"]
1372 elif "x710" in test_name:
1373 limit = plot["limits"]["nic"]["x710"]
1374 elif "xxv710" in test_name:
1375 limit = plot["limits"]["nic"]["xxv710"]
1376 elif "xl710" in test_name:
1377 limit = plot["limits"]["nic"]["xl710"]
1378 elif "x553" in test_name:
1379 limit = plot["limits"]["nic"]["x553"]
1382 if limit > nic_limit:
1385 mul = 2 if "ge2p" in test_name else 1
1386 if "10ge" in test_name:
1387 limit = plot["limits"]["link"]["10ge"] * mul
1388 elif "25ge" in test_name:
1389 limit = plot["limits"]["link"]["25ge"] * mul
1390 elif "40ge" in test_name:
1391 limit = plot["limits"]["link"]["40ge"] * mul
1392 elif "100ge" in test_name:
1393 limit = plot["limits"]["link"]["100ge"] * mul
1396 if limit > lnk_limit:
1400 order = plot.get("sort", None)
1401 if order and y_tags:
1402 y_sorted = OrderedDict()
1403 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
1405 for test, tags in y_tags_l.items():
1406 if tag.lower() in tags:
1407 name = re.sub(REGEX_NIC, "",
1408 test.replace('-ndrpdr', '').
1409 replace('2n1l-', ''))
1411 y_sorted[name] = vals.pop(name)
1413 except KeyError as err:
1414 logging.error("Not found: {0}".format(err))
1421 annotations = list()
1426 threshold = 1.1 * max(y_max) # 10%
1427 except ValueError as err:
1430 nic_limit /= 1000000.0
1431 # if nic_limit < threshold:
1432 traces.append(plgo.Scatter(
1434 y=[nic_limit, ] * len(x_vals),
1435 name="NIC: {0:.2f}Mpps".format(nic_limit),
1444 annotations.append(dict(
1451 text="NIC: {0:.2f}Mpps".format(nic_limit),
1459 # y_max.append(int((nic_limit / 10) + 1) * 10)
1460 y_max.append(nic_limit)
1462 lnk_limit /= 1000000.0
1463 if lnk_limit < threshold:
1464 traces.append(plgo.Scatter(
1466 y=[lnk_limit, ] * len(x_vals),
1467 name="Link: {0:.2f}Mpps".format(lnk_limit),
1476 annotations.append(dict(
1483 text="Link: {0:.2f}Mpps".format(lnk_limit),
1491 # y_max.append(int((lnk_limit / 10) + 1) * 10)
1492 y_max.append(lnk_limit)
1494 pci_limit /= 1000000.0
1495 if (pci_limit < threshold and
1496 (pci_limit < lnk_limit * 0.95 or lnk_limit > lnk_limit * 1.05)):
1497 traces.append(plgo.Scatter(
1499 y=[pci_limit, ] * len(x_vals),
1500 name="PCIe: {0:.2f}Mpps".format(pci_limit),
1509 annotations.append(dict(
1516 text="PCIe: {0:.2f}Mpps".format(pci_limit),
1524 # y_max.append(int((pci_limit / 10) + 1) * 10)
1525 y_max.append(pci_limit)
1527 # Perfect and measured:
1529 for name, val in y_sorted.iteritems():
1532 for idx in range(len(val["val"])):
1534 if isinstance(val["val"][idx], float):
1535 htext += "No. of Runs: {1}<br>" \
1536 "Mean: {0:.2f}Mpps<br>".format(val["val"][idx],
1538 if isinstance(val["diff"][idx], float):
1539 htext += "Diff: {0:.0f}%<br>".format(round(val["diff"][idx]))
1540 if isinstance(val["rel"][idx], float):
1541 htext += "Speedup: {0:.2f}".format(val["rel"][idx])
1542 hovertext.append(htext)
1543 traces.append(plgo.Scatter(x=x_vals,
1547 mode="lines+markers",
1556 hoverinfo="text+name"
1558 traces.append(plgo.Scatter(x=x_vals,
1560 name="{0} perfect".format(name),
1568 text=["Perfect: {0:.2f}Mpps".format(y)
1569 for y in val["ideal"]],
1573 except (IndexError, ValueError, KeyError) as err:
1574 logging.warning("No data for '{0}'".format(name))
1575 logging.warning(repr(err))
1579 logging.info(" Writing file '{0}{1}'.".
1580 format(plot["output-file"], plot["output-file-type"]))
1581 layout = deepcopy(plot["layout"])
1582 if layout.get("title", None):
1583 layout["title"] = "<b>Speedup Multi-core:</b> {0}". \
1584 format(layout["title"])
1585 # layout["yaxis"]["range"] = [0, int((max(y_max) / 10) + 1) * 10]
1586 layout["yaxis"]["range"] = [0, int(max(y_max) * 1.1)]
1587 layout["annotations"].extend(annotations)
1588 plpl = plgo.Figure(data=traces, layout=layout)
1592 show_link=False, auto_open=False,
1593 filename='{0}{1}'.format(plot["output-file"],
1594 plot["output-file-type"]))
1595 except PlotlyError as err:
1596 logging.error(" Finished with error: {}".
1597 format(str(err).replace("\n", " ")))
1601 def plot_http_server_performance_box(plot, input_data):
1602 """Generate the plot(s) with algorithm: plot_http_server_performance_box
1603 specified in the specification file.
1605 :param plot: Plot to generate.
1606 :param input_data: Data to process.
1607 :type plot: pandas.Series
1608 :type input_data: InputData
1611 # Transform the data
1612 logging.info(" Creating the data set for the {0} '{1}'.".
1613 format(plot.get("type", ""), plot.get("title", "")))
1614 data = input_data.filter_data(plot)
1616 logging.error("No data.")
1619 # Prepare the data for the plot
1624 if y_vals.get(test["name"], None) is None:
1625 y_vals[test["name"]] = list()
1627 y_vals[test["name"]].append(test["result"])
1628 except (KeyError, TypeError):
1629 y_vals[test["name"]].append(None)
1631 # Add None to the lists with missing data
1633 nr_of_samples = list()
1634 for val in y_vals.values():
1635 if len(val) > max_len:
1637 nr_of_samples.append(len(val))
1638 for key, val in y_vals.items():
1639 if len(val) < max_len:
1640 val.extend([None for _ in range(max_len - len(val))])
1644 df = pd.DataFrame(y_vals)
1646 for i, col in enumerate(df.columns):
1647 name = "{nr}. ({samples:02d} run{plural}) {name}".\
1649 samples=nr_of_samples[i],
1650 plural='s' if nr_of_samples[i] > 1 else '',
1651 name=col.lower().replace('-ndrpdr', ''))
1653 name_lst = name.split('-')
1656 for segment in name_lst:
1657 if (len(name) + len(segment) + 1) > 50 and split_name:
1660 name += segment + '-'
1663 traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
1669 plpl = plgo.Figure(data=traces, layout=plot["layout"])
1672 logging.info(" Writing file '{0}{1}'.".
1673 format(plot["output-file"], plot["output-file-type"]))
1674 ploff.plot(plpl, show_link=False, auto_open=False,
1675 filename='{0}{1}'.format(plot["output-file"],
1676 plot["output-file-type"]))
1677 except PlotlyError as err:
1678 logging.error(" Finished with error: {}".
1679 format(str(err).replace("\n", " ")))
1683 def plot_service_density_heatmap(plot, input_data):
1684 """Generate the plot(s) with algorithm: plot_service_density_heatmap
1685 specified in the specification file.
1687 :param plot: Plot to generate.
1688 :param input_data: Data to process.
1689 :type plot: pandas.Series
1690 :type input_data: InputData
1693 REGEX_CN = re.compile(r'^(\d*)R(\d*)C$')
1694 REGEX_TEST_NAME = re.compile(r'^.*-(\d+vhost|\d+memif)-'
1695 r'(\d+chain|\d+pipe)-'
1696 r'(\d+vm|\d+dcr|\d+drc).*$')
1702 # Transform the data
1703 logging.info(" Creating the data set for the {0} '{1}'.".
1704 format(plot.get("type", ""), plot.get("title", "")))
1705 data = input_data.filter_data(plot, continue_on_error=True)
1706 if data is None or data.empty:
1707 logging.error("No data.")
1713 for tag in test['tags']:
1714 groups = re.search(REGEX_CN, tag)
1716 c = str(groups.group(1))
1717 n = str(groups.group(2))
1721 groups = re.search(REGEX_TEST_NAME, test["name"])
1722 if groups and len(groups.groups()) == 3:
1723 hover_name = "{vhost}-{chain}-{vm}".format(
1724 vhost=str(groups.group(1)),
1725 chain=str(groups.group(2)),
1726 vm=str(groups.group(3)))
1729 if vals.get(c, None) is None:
1731 if vals[c].get(n, None) is None:
1732 vals[c][n] = dict(name=hover_name,
1738 if plot["include-tests"] == "MRR":
1739 result = test["result"]["receive-rate"].avg
1740 elif plot["include-tests"] == "PDR":
1741 result = test["throughput"]["PDR"]["LOWER"]
1742 elif plot["include-tests"] == "NDR":
1743 result = test["throughput"]["NDR"]["LOWER"]
1750 vals[c][n]["vals"].append(result)
1753 logging.error("No data.")
1756 for key_c in vals.keys():
1757 txt_chains.append(key_c)
1758 for key_n in vals[key_c].keys():
1759 txt_nodes.append(key_n)
1760 if vals[key_c][key_n]["vals"]:
1761 vals[key_c][key_n]["nr"] = len(vals[key_c][key_n]["vals"])
1762 vals[key_c][key_n]["mean"] = \
1763 round(mean(vals[key_c][key_n]["vals"]) / 1000000, 1)
1764 vals[key_c][key_n]["stdev"] = \
1765 round(stdev(vals[key_c][key_n]["vals"]) / 1000000, 1)
1766 txt_nodes = list(set(txt_nodes))
1768 txt_chains = sorted(txt_chains, key=lambda chain: int(chain))
1769 txt_nodes = sorted(txt_nodes, key=lambda node: int(node))
1771 chains = [i + 1 for i in range(len(txt_chains))]
1772 nodes = [i + 1 for i in range(len(txt_nodes))]
1774 data = [list() for _ in range(len(chains))]
1778 val = vals[txt_chains[c - 1]][txt_nodes[n - 1]]["mean"]
1779 except (KeyError, IndexError):
1781 data[c - 1].append(val)
1784 my_green = [[0.0, 'rgb(235, 249, 242)'],
1785 [1.0, 'rgb(45, 134, 89)']]
1787 my_blue = [[0.0, 'rgb(236, 242, 248)'],
1788 [1.0, 'rgb(57, 115, 172)']]
1790 my_grey = [[0.0, 'rgb(230, 230, 230)'],
1791 [1.0, 'rgb(102, 102, 102)']]
1794 annotations = list()
1796 text = ("Test: {name}<br>"
1801 for c in range(len(txt_chains)):
1803 for n in range(len(txt_nodes)):
1804 if data[c][n] is not None:
1805 annotations.append(dict(
1812 text=str(data[c][n]),
1819 hover_line.append(text.format(
1820 name=vals[txt_chains[c]][txt_nodes[n]]["name"],
1821 nr=vals[txt_chains[c]][txt_nodes[n]]["nr"],
1823 stdev=vals[txt_chains[c]][txt_nodes[n]]["stdev"]))
1824 hovertext.append(hover_line)
1827 plgo.Heatmap(x=nodes,
1831 title=plot.get("z-axis", ""),
1845 colorscale=my_green,
1850 for idx, item in enumerate(txt_nodes):
1852 annotations.append(dict(
1866 for idx, item in enumerate(txt_chains):
1868 annotations.append(dict(
1883 annotations.append(dict(
1890 text=plot.get("x-axis", ""),
1898 annotations.append(dict(
1905 text=plot.get("y-axis", ""),
1913 updatemenus = list([
1922 args=[{"colorscale": [my_green, ], "reversescale": False}],
1927 args=[{"colorscale": [my_blue, ], "reversescale": False}],
1932 args=[{"colorscale": [my_grey, ], "reversescale": False}],
1941 layout = deepcopy(plot["layout"])
1942 except KeyError as err:
1943 logging.error("Finished with error: No layout defined")
1944 logging.error(repr(err))
1947 layout["annotations"] = annotations
1948 layout['updatemenus'] = updatemenus
1952 plpl = plgo.Figure(data=traces, layout=layout)
1955 logging.info(" Writing file '{0}{1}'.".
1956 format(plot["output-file"], plot["output-file-type"]))
1957 ploff.plot(plpl, show_link=False, auto_open=False,
1958 filename='{0}{1}'.format(plot["output-file"],
1959 plot["output-file-type"]))
1960 except PlotlyError as err:
1961 logging.error(" Finished with error: {}".
1962 format(str(err).replace("\n", " ")))
1966 def plot_service_density_heatmap_compare(plot, input_data):
1967 """Generate the plot(s) with algorithm: plot_service_density_heatmap_compare
1968 specified in the specification file.
1970 :param plot: Plot to generate.
1971 :param input_data: Data to process.
1972 :type plot: pandas.Series
1973 :type input_data: InputData
1976 REGEX_CN = re.compile(r'^(\d*)R(\d*)C$')
1977 REGEX_TEST_NAME = re.compile(r'^.*-(\d+ch|\d+pl)-'
1979 r'(\d+vm|\d+dcr).*$')
1980 REGEX_THREADS = re.compile(r'^(\d+)(VM|DCR)(\d+)T$')
1986 # Transform the data
1987 logging.info(" Creating the data set for the {0} '{1}'.".
1988 format(plot.get("type", ""), plot.get("title", "")))
1989 data = input_data.filter_data(plot, continue_on_error=True)
1990 if data is None or data.empty:
1991 logging.error("No data.")
1997 for tag in test['tags']:
1998 groups = re.search(REGEX_CN, tag)
2000 c = str(groups.group(1))
2001 n = str(groups.group(2))
2005 groups = re.search(REGEX_TEST_NAME, test["name"])
2006 if groups and len(groups.groups()) == 3:
2007 hover_name = "{chain}-{vhost}-{vm}".format(
2008 chain=str(groups.group(1)),
2009 vhost=str(groups.group(2)),
2010 vm=str(groups.group(3)))
2013 if vals.get(c, None) is None:
2015 if vals[c].get(n, None) is None:
2016 vals[c][n] = dict(name=hover_name,
2026 if plot["include-tests"] == "MRR":
2027 result = test["result"]["receive-rate"].avg
2028 elif plot["include-tests"] == "PDR":
2029 result = test["throughput"]["PDR"]["LOWER"]
2030 elif plot["include-tests"] == "NDR":
2031 result = test["throughput"]["NDR"]["LOWER"]
2038 for tag in test['tags']:
2039 groups = re.search(REGEX_THREADS, tag)
2040 if groups and len(groups.groups()) == 3:
2041 if str(groups.group(3)) == \
2042 plot["reference"]["include"]:
2043 vals[c][n]["vals_r"].append(result)
2044 elif str(groups.group(3)) == \
2045 plot["compare"]["include"]:
2046 vals[c][n]["vals_c"].append(result)
2049 logging.error("No data.")
2052 for key_c in vals.keys():
2053 txt_chains.append(key_c)
2054 for key_n in vals[key_c].keys():
2055 txt_nodes.append(key_n)
2056 if vals[key_c][key_n]["vals_r"]:
2057 vals[key_c][key_n]["nr_r"] = len(vals[key_c][key_n]["vals_r"])
2058 vals[key_c][key_n]["mean_r"] = \
2059 mean(vals[key_c][key_n]["vals_r"])
2060 vals[key_c][key_n]["stdev_r"] = \
2061 round(stdev(vals[key_c][key_n]["vals_r"]) / 1000000, 1)
2062 if vals[key_c][key_n]["vals_c"]:
2063 vals[key_c][key_n]["nr_c"] = len(vals[key_c][key_n]["vals_c"])
2064 vals[key_c][key_n]["mean_c"] = \
2065 mean(vals[key_c][key_n]["vals_c"])
2066 vals[key_c][key_n]["stdev_c"] = \
2067 round(stdev(vals[key_c][key_n]["vals_c"]) / 1000000, 1)
2069 txt_nodes = list(set(txt_nodes))
2071 txt_chains = sorted(txt_chains, key=lambda chain: int(chain))
2072 txt_nodes = sorted(txt_nodes, key=lambda node: int(node))
2074 chains = [i + 1 for i in range(len(txt_chains))]
2075 nodes = [i + 1 for i in range(len(txt_nodes))]
2077 data_r = [list() for _ in range(len(chains))]
2078 data_c = [list() for _ in range(len(chains))]
2079 diff = [list() for _ in range(len(chains))]
2083 val_r = vals[txt_chains[c - 1]][txt_nodes[n - 1]]["mean_r"]
2084 except (KeyError, IndexError):
2087 val_c = vals[txt_chains[c - 1]][txt_nodes[n - 1]]["mean_c"]
2088 except (KeyError, IndexError):
2090 if val_c is not None and val_r:
2091 val_d = (val_c - val_r) * 100 / val_r
2095 if val_r is not None:
2096 val_r = round(val_r / 1000000, 1)
2097 data_r[c - 1].append(val_r)
2098 if val_c is not None:
2099 val_c = round(val_c / 1000000, 1)
2100 data_c[c - 1].append(val_c)
2101 if val_d is not None:
2102 val_d = int(round(val_d, 0))
2103 diff[c - 1].append(val_d)
2106 my_green = [[0.0, 'rgb(235, 249, 242)'],
2107 [1.0, 'rgb(45, 134, 89)']]
2109 my_blue = [[0.0, 'rgb(236, 242, 248)'],
2110 [1.0, 'rgb(57, 115, 172)']]
2112 my_grey = [[0.0, 'rgb(230, 230, 230)'],
2113 [1.0, 'rgb(102, 102, 102)']]
2117 annotations = list()
2118 annotations_r = list()
2119 annotations_c = list()
2120 annotations_diff = list()
2122 text = ("Test: {name}"
2123 "<br>{title_r}: {text_r}"
2124 "<br>{title_c}: {text_c}{text_diff}")
2125 text_r = "Thput: {val_r}; StDev: {stdev_r}; Runs: {nr_r}"
2126 text_c = "Thput: {val_c}; StDev: {stdev_c}; Runs: {nr_c}"
2127 text_diff = "<br>Relative Difference {title_c} vs. {title_r}: {diff}%"
2129 for c in range(len(txt_chains)):
2131 for n in range(len(txt_nodes)):
2147 point_text_r = "Not present"
2148 point_text_c = "Not present"
2149 point_text_diff = ""
2151 point_r = data_r[c][n]
2152 if point_r is not None:
2153 point_text_r = text_r.format(
2155 stdev_r=vals[txt_chains[c]][txt_nodes[n]]["stdev_r"],
2156 nr_r=vals[txt_chains[c]][txt_nodes[n]]["nr_r"])
2159 point["text"] = "" if point_r is None else point_r
2160 annotations_r.append(deepcopy(point))
2163 point_c = data_c[c][n]
2164 if point_c is not None:
2165 point_text_c = text_c.format(
2167 stdev_c=vals[txt_chains[c]][txt_nodes[n]]["stdev_c"],
2168 nr_c=vals[txt_chains[c]][txt_nodes[n]]["nr_c"])
2171 point["text"] = "" if point_c is None else point_c
2172 annotations_c.append(deepcopy(point))
2175 point_d = diff[c][n]
2176 if point_d is not None:
2177 point_text_diff = text_diff.format(
2178 title_r=plot["reference"]["name"],
2179 title_c=plot["compare"]["name"],
2183 point["text"] = "" if point_d is None else point_d
2184 annotations_diff.append(deepcopy(point))
2187 name = vals[txt_chains[c]][txt_nodes[n]]["name"]
2191 hover_line.append(text.format(
2193 title_r=plot["reference"]["name"],
2194 text_r=point_text_r,
2195 title_c=plot["compare"]["name"],
2196 text_c=point_text_c,
2197 text_diff=point_text_diff
2200 hovertext.append(hover_line)
2203 plgo.Heatmap(x=nodes,
2208 title=plot.get("z-axis", ""),
2222 colorscale=my_green,
2226 plgo.Heatmap(x=nodes,
2231 title=plot.get("z-axis", ""),
2249 plgo.Heatmap(x=nodes,
2255 title="Relative Difference {name_c} vs. {name_r} [%]".
2256 format(name_c=plot["compare"]["name"],
2257 name_r=plot["reference"]["name"]),
2277 for idx, item in enumerate(txt_nodes):
2279 annotations.append(dict(
2293 for idx, item in enumerate(txt_chains):
2295 annotations.append(dict(
2310 annotations.append(dict(
2317 text=plot.get("x-axis", ""),
2325 annotations.append(dict(
2332 text=plot.get("y-axis", ""),
2340 updatemenus = list([
2350 label=plot["reference"]["name"],
2354 "visible": [True, False, False]
2357 "colorscale": [my_green, ],
2358 "reversescale": False,
2359 "annotations": annotations + annotations_r,
2364 label=plot["compare"]["name"],
2368 "visible": [False, True, False]
2371 "colorscale": [my_blue, ],
2372 "reversescale": False,
2373 "annotations": annotations + annotations_c,
2382 "visible": [False, False, True]
2385 "colorscale": [my_grey, ],
2386 "reversescale": False,
2387 "annotations": annotations + annotations_diff,
2396 layout = deepcopy(plot["layout"])
2397 except KeyError as err:
2398 logging.error("Finished with error: No layout defined")
2399 logging.error(repr(err))
2402 layout["annotations"] = annotations + annotations_r
2403 layout['updatemenus'] = updatemenus
2407 plpl = plgo.Figure(data=traces, layout=layout)
2410 logging.info(" Writing file '{0}{1}'.".
2411 format(plot["output-file"], plot["output-file-type"]))
2412 ploff.plot(plpl, show_link=False, auto_open=False,
2413 filename='{0}{1}'.format(plot["output-file"],
2414 plot["output-file-type"]))
2415 except PlotlyError as err:
2416 logging.error(" Finished with error: {}".
2417 format(str(err).replace("\n", " ")))