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)
115 for i, col in enumerate(df.columns):
116 tst_name = re.sub(REGEX_NIC, "",
117 col.lower().replace('-ndrpdr', '').
118 replace('2n1l-', ''))
119 tst_name = "-".join(tst_name.split("-")[3:-2])
120 name = "{nr}. ({samples:02d} run{plural}, packets lost average: " \
121 "{loss:.1f}, {name}".format(
123 samples=nr_of_samples[i],
124 plural='s' if nr_of_samples[i] > 1 else '',
126 loss=mean(loss[col]))
128 traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
129 y=[y if y else None for y in df[col]],
132 boxpoints="outliers",
135 val_max = max(df[col])
136 except ValueError as err:
137 logging.error(repr(err))
140 y_max.append(int(val_max) + 1)
144 layout = deepcopy(plot["layout"])
145 layout["title"] = "<b>Time Lost:</b> {0}".format(layout["title"])
146 layout["yaxis"]["title"] = "<b>Implied Time Lost [s]</b>"
147 layout["legend"]["font"]["size"] = 14
149 layout["yaxis"]["range"] = [0, max(y_max)]
150 plpl = plgo.Figure(data=traces, layout=layout)
153 file_type = plot.get("output-file-type", ".html")
154 logging.info(" Writing file '{0}{1}'.".
155 format(plot["output-file"], file_type))
156 ploff.plot(plpl, show_link=False, auto_open=False,
157 filename='{0}{1}'.format(plot["output-file"], file_type))
158 except PlotlyError as err:
159 logging.error(" Finished with error: {}".
160 format(repr(err).replace("\n", " ")))
164 def plot_performance_box_name(plot, input_data):
165 """Generate the plot(s) with algorithm: plot_performance_box_name
166 specified in the specification file.
168 :param plot: Plot to generate.
169 :param input_data: Data to process.
170 :type plot: pandas.Series
171 :type input_data: InputData
175 plot_title = plot.get("title", "")
176 logging.info(" Creating the data set for the {0} '{1}'.".
177 format(plot.get("type", ""), plot_title))
178 data = input_data.filter_tests_by_name(
179 plot, params=["throughput", "parent", "tags", "type"])
181 logging.error("No data.")
184 # Prepare the data for the plot
185 y_vals = OrderedDict()
189 if y_vals.get(test["parent"], None) is None:
190 y_vals[test["parent"]] = list()
192 if test["type"] in ("NDRPDR", ):
193 if "-pdr" in plot_title.lower():
194 y_vals[test["parent"]].\
195 append(test["throughput"]["PDR"]["LOWER"])
196 elif "-ndr" in plot_title.lower():
197 y_vals[test["parent"]]. \
198 append(test["throughput"]["NDR"]["LOWER"])
201 elif test["type"] in ("SOAK", ):
202 y_vals[test["parent"]].\
203 append(test["throughput"]["LOWER"])
206 except (KeyError, TypeError):
207 y_vals[test["parent"]].append(None)
209 # Add None to the lists with missing data
211 nr_of_samples = list()
212 for val in y_vals.values():
213 if len(val) > max_len:
215 nr_of_samples.append(len(val))
216 for key, val in y_vals.items():
217 if len(val) < max_len:
218 val.extend([None for _ in range(max_len - len(val))])
222 df = pd.DataFrame(y_vals)
225 for i, col in enumerate(df.columns):
226 tst_name = re.sub(REGEX_NIC, "",
227 col.lower().replace('-ndrpdr', '').
228 replace('2n1l-', ''))
229 name = "{nr}. ({samples:02d} run{plural}) {name}".\
231 samples=nr_of_samples[i],
232 plural='s' if nr_of_samples[i] > 1 else '',
236 traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
237 y=[y / 1000000 if y else None for y in df[col]],
240 boxpoints="outliers",
243 val_max = max(df[col])
244 except ValueError as err:
245 logging.error(repr(err))
248 y_max.append(int(val_max / 1000000) + 2)
252 layout = deepcopy(plot["layout"])
253 if layout.get("title", None):
254 layout["title"] = "<b>Throughput:</b> {0}". \
255 format(layout["title"])
257 layout["yaxis"]["range"] = [0, max(y_max)]
258 plpl = plgo.Figure(data=traces, layout=layout)
261 file_type = plot.get("output-file-type", ".html")
262 logging.info(" Writing file '{0}{1}'.".
263 format(plot["output-file"], file_type))
264 ploff.plot(plpl, show_link=False, auto_open=False,
265 filename='{0}{1}'.format(plot["output-file"], file_type))
266 except PlotlyError as err:
267 logging.error(" Finished with error: {}".
268 format(repr(err).replace("\n", " ")))
272 def plot_latency_error_bars_name(plot, input_data):
273 """Generate the plot(s) with algorithm: plot_latency_error_bars_name
274 specified in the specification file.
276 :param plot: Plot to generate.
277 :param input_data: Data to process.
278 :type plot: pandas.Series
279 :type input_data: InputData
283 plot_title = plot.get("title", "")
284 logging.info(" Creating the data set for the {0} '{1}'.".
285 format(plot.get("type", ""), plot_title))
286 data = input_data.filter_tests_by_name(
287 plot, params=["latency", "parent", "tags", "type"])
289 logging.error("No data.")
292 # Prepare the data for the plot
293 y_tmp_vals = OrderedDict()
298 logging.debug("test['latency']: {0}\n".
299 format(test["latency"]))
300 except ValueError as err:
301 logging.warning(repr(err))
302 if y_tmp_vals.get(test["parent"], None) is None:
303 y_tmp_vals[test["parent"]] = [
304 list(), # direction1, min
305 list(), # direction1, avg
306 list(), # direction1, max
307 list(), # direction2, min
308 list(), # direction2, avg
309 list() # direction2, max
312 if test["type"] in ("NDRPDR", ):
313 if "-pdr" in plot_title.lower():
315 elif "-ndr" in plot_title.lower():
318 logging.warning("Invalid test type: {0}".
319 format(test["type"]))
321 y_tmp_vals[test["parent"]][0].append(
322 test["latency"][ttype]["direction1"]["min"])
323 y_tmp_vals[test["parent"]][1].append(
324 test["latency"][ttype]["direction1"]["avg"])
325 y_tmp_vals[test["parent"]][2].append(
326 test["latency"][ttype]["direction1"]["max"])
327 y_tmp_vals[test["parent"]][3].append(
328 test["latency"][ttype]["direction2"]["min"])
329 y_tmp_vals[test["parent"]][4].append(
330 test["latency"][ttype]["direction2"]["avg"])
331 y_tmp_vals[test["parent"]][5].append(
332 test["latency"][ttype]["direction2"]["max"])
334 logging.warning("Invalid test type: {0}".
335 format(test["type"]))
337 except (KeyError, TypeError) as err:
338 logging.warning(repr(err))
344 nr_of_samples = list()
345 for key, val in y_tmp_vals.items():
346 name = re.sub(REGEX_NIC, "", key.replace('-ndrpdr', '').
347 replace('2n1l-', ''))
348 x_vals.append(name) # dir 1
349 y_vals.append(mean(val[1]) if val[1] else None)
350 y_mins.append(mean(val[0]) if val[0] else None)
351 y_maxs.append(mean(val[2]) if val[2] else None)
352 nr_of_samples.append(len(val[1]) if val[1] else 0)
353 x_vals.append(name) # dir 2
354 y_vals.append(mean(val[4]) if val[4] else None)
355 y_mins.append(mean(val[3]) if val[3] else None)
356 y_maxs.append(mean(val[5]) if val[5] else None)
357 nr_of_samples.append(len(val[3]) if val[3] else 0)
362 for idx in range(len(x_vals)):
363 if not bool(int(idx % 2)):
364 direction = "West-East"
366 direction = "East-West"
367 hovertext = ("No. of Runs: {nr}<br>"
369 "Direction: {dir}<br>".format(test=x_vals[idx],
371 nr=nr_of_samples[idx]))
372 if isinstance(y_maxs[idx], float):
373 hovertext += "Max: {max:.2f}uSec<br>".format(max=y_maxs[idx])
374 if isinstance(y_vals[idx], float):
375 hovertext += "Mean: {avg:.2f}uSec<br>".format(avg=y_vals[idx])
376 if isinstance(y_mins[idx], float):
377 hovertext += "Min: {min:.2f}uSec".format(min=y_mins[idx])
379 if isinstance(y_maxs[idx], float) and isinstance(y_vals[idx], float):
380 array = [y_maxs[idx] - y_vals[idx], ]
383 if isinstance(y_mins[idx], float) and isinstance(y_vals[idx], float):
384 arrayminus = [y_vals[idx] - y_mins[idx], ]
386 arrayminus = [None, ]
387 traces.append(plgo.Scatter(
391 legendgroup=x_vals[idx],
392 showlegend=bool(int(idx % 2)),
398 arrayminus=arrayminus,
399 color=COLORS[int(idx / 2)]
403 color=COLORS[int(idx / 2)],
408 annotations.append(dict(
415 text="E-W" if bool(int(idx % 2)) else "W-E",
425 file_type = plot.get("output-file-type", ".html")
426 logging.info(" Writing file '{0}{1}'.".
427 format(plot["output-file"], file_type))
428 layout = deepcopy(plot["layout"])
429 if layout.get("title", None):
430 layout["title"] = "<b>Latency:</b> {0}".\
431 format(layout["title"])
432 layout["annotations"] = annotations
433 plpl = plgo.Figure(data=traces, layout=layout)
437 show_link=False, auto_open=False,
438 filename='{0}{1}'.format(plot["output-file"], file_type))
439 except PlotlyError as err:
440 logging.error(" Finished with error: {}".
441 format(str(err).replace("\n", " ")))
445 def plot_throughput_speedup_analysis_name(plot, input_data):
446 """Generate the plot(s) with algorithm:
447 plot_throughput_speedup_analysis_name
448 specified in the specification file.
450 :param plot: Plot to generate.
451 :param input_data: Data to process.
452 :type plot: pandas.Series
453 :type input_data: InputData
457 plot_title = plot.get("title", "")
458 logging.info(" Creating the data set for the {0} '{1}'.".
459 format(plot.get("type", ""), plot_title))
460 data = input_data.filter_tests_by_name(
461 plot, params=["throughput", "parent", "tags", "type"])
463 logging.error("No data.")
466 y_vals = OrderedDict()
470 if y_vals.get(test["parent"], None) is None:
471 y_vals[test["parent"]] = {"1": list(),
475 if test["type"] in ("NDRPDR",):
476 if "-pdr" in plot_title.lower():
478 elif "-ndr" in plot_title.lower():
482 if "1C" in test["tags"]:
483 y_vals[test["parent"]]["1"]. \
484 append(test["throughput"][ttype]["LOWER"])
485 elif "2C" in test["tags"]:
486 y_vals[test["parent"]]["2"]. \
487 append(test["throughput"][ttype]["LOWER"])
488 elif "4C" in test["tags"]:
489 y_vals[test["parent"]]["4"]. \
490 append(test["throughput"][ttype]["LOWER"])
491 except (KeyError, TypeError):
495 logging.warning("No data for the plot '{}'".
496 format(plot.get("title", "")))
500 for test_name, test_vals in y_vals.items():
501 for key, test_val in test_vals.items():
503 avg_val = sum(test_val) / len(test_val)
504 y_vals[test_name][key] = (avg_val, len(test_val))
505 ideal = avg_val / (int(key) * 1000000.0)
506 if test_name not in y_1c_max or ideal > y_1c_max[test_name]:
507 y_1c_max[test_name] = ideal
513 pci_limit = plot["limits"]["pci"]["pci-g3-x8"]
514 for test_name, test_vals in y_vals.items():
516 if test_vals["1"][1]:
517 name = re.sub(REGEX_NIC, "", test_name.replace('-ndrpdr', '').
518 replace('2n1l-', ''))
519 vals[name] = OrderedDict()
520 y_val_1 = test_vals["1"][0] / 1000000.0
521 y_val_2 = test_vals["2"][0] / 1000000.0 if test_vals["2"][0] \
523 y_val_4 = test_vals["4"][0] / 1000000.0 if test_vals["4"][0] \
526 vals[name]["val"] = [y_val_1, y_val_2, y_val_4]
527 vals[name]["rel"] = [1.0, None, None]
528 vals[name]["ideal"] = [y_1c_max[test_name],
529 y_1c_max[test_name] * 2,
530 y_1c_max[test_name] * 4]
531 vals[name]["diff"] = [(y_val_1 - y_1c_max[test_name]) * 100 /
533 vals[name]["count"] = [test_vals["1"][1],
538 val_max = max(vals[name]["val"])
539 except ValueError as err:
540 logging.error(repr(err))
543 y_max.append(val_max)
546 vals[name]["rel"][1] = round(y_val_2 / y_val_1, 2)
547 vals[name]["diff"][1] = \
548 (y_val_2 - vals[name]["ideal"][1]) * 100 / y_val_2
550 vals[name]["rel"][2] = round(y_val_4 / y_val_1, 2)
551 vals[name]["diff"][2] = \
552 (y_val_4 - vals[name]["ideal"][2]) * 100 / y_val_4
553 except IndexError as err:
554 logging.warning("No data for '{0}'".format(test_name))
555 logging.warning(repr(err))
558 if "x520" in test_name:
559 limit = plot["limits"]["nic"]["x520"]
560 elif "x710" in test_name:
561 limit = plot["limits"]["nic"]["x710"]
562 elif "xxv710" in test_name:
563 limit = plot["limits"]["nic"]["xxv710"]
564 elif "xl710" in test_name:
565 limit = plot["limits"]["nic"]["xl710"]
566 elif "x553" in test_name:
567 limit = plot["limits"]["nic"]["x553"]
570 if limit > nic_limit:
573 mul = 2 if "ge2p" in test_name else 1
574 if "10ge" in test_name:
575 limit = plot["limits"]["link"]["10ge"] * mul
576 elif "25ge" in test_name:
577 limit = plot["limits"]["link"]["25ge"] * mul
578 elif "40ge" in test_name:
579 limit = plot["limits"]["link"]["40ge"] * mul
580 elif "100ge" in test_name:
581 limit = plot["limits"]["link"]["100ge"] * mul
584 if limit > lnk_limit:
593 threshold = 1.1 * max(y_max) # 10%
594 except ValueError as err:
597 nic_limit /= 1000000.0
598 traces.append(plgo.Scatter(
600 y=[nic_limit, ] * len(x_vals),
601 name="NIC: {0:.2f}Mpps".format(nic_limit),
610 annotations.append(dict(
617 text="NIC: {0:.2f}Mpps".format(nic_limit),
625 y_max.append(nic_limit)
627 lnk_limit /= 1000000.0
628 if lnk_limit < threshold:
629 traces.append(plgo.Scatter(
631 y=[lnk_limit, ] * len(x_vals),
632 name="Link: {0:.2f}Mpps".format(lnk_limit),
641 annotations.append(dict(
648 text="Link: {0:.2f}Mpps".format(lnk_limit),
656 y_max.append(lnk_limit)
658 pci_limit /= 1000000.0
659 if (pci_limit < threshold and
660 (pci_limit < lnk_limit * 0.95 or lnk_limit > lnk_limit * 1.05)):
661 traces.append(plgo.Scatter(
663 y=[pci_limit, ] * len(x_vals),
664 name="PCIe: {0:.2f}Mpps".format(pci_limit),
673 annotations.append(dict(
680 text="PCIe: {0:.2f}Mpps".format(pci_limit),
688 y_max.append(pci_limit)
690 # Perfect and measured:
692 for name, val in vals.iteritems():
695 for idx in range(len(val["val"])):
697 if isinstance(val["val"][idx], float):
698 htext += "No. of Runs: {1}<br>" \
699 "Mean: {0:.2f}Mpps<br>".format(val["val"][idx],
701 if isinstance(val["diff"][idx], float):
702 htext += "Diff: {0:.0f}%<br>".format(
703 round(val["diff"][idx]))
704 if isinstance(val["rel"][idx], float):
705 htext += "Speedup: {0:.2f}".format(val["rel"][idx])
706 hovertext.append(htext)
707 traces.append(plgo.Scatter(x=x_vals,
711 mode="lines+markers",
720 hoverinfo="text+name"
722 traces.append(plgo.Scatter(x=x_vals,
724 name="{0} perfect".format(name),
732 text=["Perfect: {0:.2f}Mpps".format(y)
733 for y in val["ideal"]],
737 except (IndexError, ValueError, KeyError) as err:
738 logging.warning("No data for '{0}'".format(name))
739 logging.warning(repr(err))
743 file_type = plot.get("output-file-type", ".html")
744 logging.info(" Writing file '{0}{1}'.".
745 format(plot["output-file"], file_type))
746 layout = deepcopy(plot["layout"])
747 if layout.get("title", None):
748 layout["title"] = "<b>Speedup Multi-core:</b> {0}". \
749 format(layout["title"])
750 layout["yaxis"]["range"] = [0, int(max(y_max) * 1.1)]
751 layout["annotations"].extend(annotations)
752 plpl = plgo.Figure(data=traces, layout=layout)
756 show_link=False, auto_open=False,
757 filename='{0}{1}'.format(plot["output-file"], file_type))
758 except PlotlyError as err:
759 logging.error(" Finished with error: {}".
760 format(repr(err).replace("\n", " ")))
764 def plot_performance_box(plot, input_data):
765 """Generate the plot(s) with algorithm: plot_performance_box
766 specified in the specification file.
768 TODO: Remove when not needed.
770 :param plot: Plot to generate.
771 :param input_data: Data to process.
772 :type plot: pandas.Series
773 :type input_data: InputData
777 plot_title = plot.get("title", "")
778 logging.info(" Creating the data set for the {0} '{1}'.".
779 format(plot.get("type", ""), plot_title))
780 data = input_data.filter_data(plot)
782 logging.error("No data.")
785 # Prepare the data for the plot
791 if y_vals.get(test["parent"], None) is None:
792 y_vals[test["parent"]] = list()
793 y_tags[test["parent"]] = test.get("tags", None)
795 if test["type"] in ("NDRPDR", ):
796 if "-pdr" in plot_title.lower():
797 y_vals[test["parent"]].\
798 append(test["throughput"]["PDR"]["LOWER"])
799 elif "-ndr" in plot_title.lower():
800 y_vals[test["parent"]]. \
801 append(test["throughput"]["NDR"]["LOWER"])
804 elif test["type"] in ("SOAK", ):
805 y_vals[test["parent"]].\
806 append(test["throughput"]["LOWER"])
809 except (KeyError, TypeError):
810 y_vals[test["parent"]].append(None)
813 order = plot.get("sort", None)
815 y_sorted = OrderedDict()
816 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
819 for suite, tags in y_tags_l.items():
821 tag = tag.split(" ")[-1]
822 if tag.lower() in tags:
825 if tag.lower() not in tags:
828 y_sorted[suite] = y_vals.pop(suite)
831 except KeyError as err:
832 logging.error("Not found: {0}".format(repr(err)))
838 # Add None to the lists with missing data
840 nr_of_samples = list()
841 for val in y_sorted.values():
842 if len(val) > max_len:
844 nr_of_samples.append(len(val))
845 for key, val in y_sorted.items():
846 if len(val) < max_len:
847 val.extend([None for _ in range(max_len - len(val))])
851 df = pd.DataFrame(y_sorted)
854 for i, col in enumerate(df.columns):
855 tst_name = re.sub(REGEX_NIC, "",
856 col.lower().replace('-ndrpdr', '').
857 replace('2n1l-', ''))
858 name = "{nr}. ({samples:02d} run{plural}) {name}".\
860 samples=nr_of_samples[i],
861 plural='s' if nr_of_samples[i] > 1 else '',
865 traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
866 y=[y / 1000000 if y else None for y in df[col]],
870 val_max = max(df[col])
871 except ValueError as err:
872 logging.error(repr(err))
875 y_max.append(int(val_max / 1000000) + 2)
879 layout = deepcopy(plot["layout"])
880 if layout.get("title", None):
881 layout["title"] = "<b>Throughput:</b> {0}". \
882 format(layout["title"])
884 layout["yaxis"]["range"] = [0, max(y_max)]
885 plpl = plgo.Figure(data=traces, layout=layout)
888 logging.info(" Writing file '{0}{1}'.".
889 format(plot["output-file"], plot["output-file-type"]))
890 ploff.plot(plpl, show_link=False, auto_open=False,
891 filename='{0}{1}'.format(plot["output-file"],
892 plot["output-file-type"]))
893 except PlotlyError as err:
894 logging.error(" Finished with error: {}".
895 format(repr(err).replace("\n", " ")))
899 def plot_soak_bars(plot, input_data):
900 """Generate the plot(s) with algorithm: plot_soak_bars
901 specified in the specification file.
903 :param plot: Plot to generate.
904 :param input_data: Data to process.
905 :type plot: pandas.Series
906 :type input_data: InputData
910 plot_title = plot.get("title", "")
911 logging.info(" Creating the data set for the {0} '{1}'.".
912 format(plot.get("type", ""), plot_title))
913 data = input_data.filter_data(plot)
915 logging.error("No data.")
918 # Prepare the data for the plot
924 if y_vals.get(test["parent"], None) is None:
925 y_tags[test["parent"]] = test.get("tags", None)
927 if test["type"] in ("SOAK", ):
928 y_vals[test["parent"]] = test["throughput"]
931 except (KeyError, TypeError):
932 y_vals[test["parent"]] = dict()
935 order = plot.get("sort", None)
937 y_sorted = OrderedDict()
938 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
941 for suite, tags in y_tags_l.items():
943 tag = tag.split(" ")[-1]
944 if tag.lower() in tags:
947 if tag.lower() not in tags:
950 y_sorted[suite] = y_vals.pop(suite)
953 except KeyError as err:
954 logging.error("Not found: {0}".format(repr(err)))
963 for test_name, test_data in y_sorted.items():
965 name = "{nr}. {name}".\
966 format(nr=idx, name=test_name.lower().replace('-soak', ''))
968 name_lst = name.split('-')
971 for segment in name_lst:
972 if (len(name) + len(segment) + 1) > 50 and split_name:
975 name += segment + '-'
978 y_val = test_data.get("LOWER", None)
984 time = "No Information"
985 result = "No Information"
986 hovertext = ("{name}<br>"
987 "Packet Throughput: {val:.2f}Mpps<br>"
988 "Final Duration: {time}<br>"
989 "Result: {result}".format(name=name,
993 traces.append(plgo.Bar(x=[str(idx) + '.', ],
1000 layout = deepcopy(plot["layout"])
1001 if layout.get("title", None):
1002 layout["title"] = "<b>Packet Throughput:</b> {0}". \
1003 format(layout["title"])
1005 layout["yaxis"]["range"] = [0, y_max + 1]
1006 plpl = plgo.Figure(data=traces, layout=layout)
1008 logging.info(" Writing file '{0}{1}'.".
1009 format(plot["output-file"], plot["output-file-type"]))
1010 ploff.plot(plpl, show_link=False, auto_open=False,
1011 filename='{0}{1}'.format(plot["output-file"],
1012 plot["output-file-type"]))
1013 except PlotlyError as err:
1014 logging.error(" Finished with error: {}".
1015 format(repr(err).replace("\n", " ")))
1019 def plot_soak_boxes(plot, input_data):
1020 """Generate the plot(s) with algorithm: plot_soak_boxes
1021 specified in the specification file.
1023 :param plot: Plot to generate.
1024 :param input_data: Data to process.
1025 :type plot: pandas.Series
1026 :type input_data: InputData
1029 # Transform the data
1030 plot_title = plot.get("title", "")
1031 logging.info(" Creating the data set for the {0} '{1}'.".
1032 format(plot.get("type", ""), plot_title))
1033 data = input_data.filter_data(plot)
1035 logging.error("No data.")
1038 # Prepare the data for the plot
1044 if y_vals.get(test["parent"], None) is None:
1045 y_tags[test["parent"]] = test.get("tags", None)
1047 if test["type"] in ("SOAK", ):
1048 y_vals[test["parent"]] = test["throughput"]
1051 except (KeyError, TypeError):
1052 y_vals[test["parent"]] = dict()
1055 order = plot.get("sort", None)
1056 if order and y_tags:
1057 y_sorted = OrderedDict()
1058 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
1061 for suite, tags in y_tags_l.items():
1063 tag = tag.split(" ")[-1]
1064 if tag.lower() in tags:
1067 if tag.lower() not in tags:
1070 y_sorted[suite] = y_vals.pop(suite)
1072 logging.debug(suite)
1073 except KeyError as err:
1074 logging.error("Not found: {0}".format(repr(err)))
1083 for test_name, test_data in y_sorted.items():
1085 name = "{nr}. {name}".\
1086 format(nr=idx, name=test_name.lower().replace('-soak', '').
1087 replace('2n1l-', ''))
1089 name_lst = name.split('-')
1092 for segment in name_lst:
1093 if (len(name) + len(segment) + 1) > 55 and split_name:
1096 name += segment + '-'
1099 y_val = test_data.get("UPPER", None)
1105 y_base = test_data.get("LOWER", None)
1109 hovertext = ("Upper bound: {upper:.2f}<br>"
1110 "Lower bound: {lower:.2f}".format(upper=y_val,
1112 traces.append(plgo.Bar(x=[str(idx) + '.', ],
1113 # +0.05 to see the value in case lower == upper
1114 y=[y_val - y_base + 0.05, ],
1121 layout = deepcopy(plot["layout"])
1122 if layout.get("title", None):
1123 layout["title"] = "<b>Throughput:</b> {0}". \
1124 format(layout["title"])
1126 layout["yaxis"]["range"] = [0, y_max + 1]
1127 plpl = plgo.Figure(data=traces, layout=layout)
1129 logging.info(" Writing file '{0}{1}'.".
1130 format(plot["output-file"], plot["output-file-type"]))
1131 ploff.plot(plpl, show_link=False, auto_open=False,
1132 filename='{0}{1}'.format(plot["output-file"],
1133 plot["output-file-type"]))
1134 except PlotlyError as err:
1135 logging.error(" Finished with error: {}".
1136 format(repr(err).replace("\n", " ")))
1140 def plot_latency_error_bars(plot, input_data):
1141 """Generate the plot(s) with algorithm: plot_latency_error_bars
1142 specified in the specification file.
1144 TODO: Remove when not needed.
1146 :param plot: Plot to generate.
1147 :param input_data: Data to process.
1148 :type plot: pandas.Series
1149 :type input_data: InputData
1152 # Transform the data
1153 plot_title = plot.get("title", "")
1154 logging.info(" Creating the data set for the {0} '{1}'.".
1155 format(plot.get("type", ""), plot_title))
1156 data = input_data.filter_data(plot)
1158 logging.error("No data.")
1161 # Prepare the data for the plot
1168 logging.debug("test['latency']: {0}\n".
1169 format(test["latency"]))
1170 except ValueError as err:
1171 logging.warning(repr(err))
1172 if y_tmp_vals.get(test["parent"], None) is None:
1173 y_tmp_vals[test["parent"]] = [
1174 list(), # direction1, min
1175 list(), # direction1, avg
1176 list(), # direction1, max
1177 list(), # direction2, min
1178 list(), # direction2, avg
1179 list() # direction2, max
1181 y_tags[test["parent"]] = test.get("tags", None)
1183 if test["type"] in ("NDRPDR", ):
1184 if "-pdr" in plot_title.lower():
1186 elif "-ndr" in plot_title.lower():
1189 logging.warning("Invalid test type: {0}".
1190 format(test["type"]))
1192 y_tmp_vals[test["parent"]][0].append(
1193 test["latency"][ttype]["direction1"]["min"])
1194 y_tmp_vals[test["parent"]][1].append(
1195 test["latency"][ttype]["direction1"]["avg"])
1196 y_tmp_vals[test["parent"]][2].append(
1197 test["latency"][ttype]["direction1"]["max"])
1198 y_tmp_vals[test["parent"]][3].append(
1199 test["latency"][ttype]["direction2"]["min"])
1200 y_tmp_vals[test["parent"]][4].append(
1201 test["latency"][ttype]["direction2"]["avg"])
1202 y_tmp_vals[test["parent"]][5].append(
1203 test["latency"][ttype]["direction2"]["max"])
1205 logging.warning("Invalid test type: {0}".
1206 format(test["type"]))
1208 except (KeyError, TypeError) as err:
1209 logging.warning(repr(err))
1210 logging.debug("y_tmp_vals: {0}\n".format(y_tmp_vals))
1213 order = plot.get("sort", None)
1214 if order and y_tags:
1215 y_sorted = OrderedDict()
1216 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
1219 for suite, tags in y_tags_l.items():
1221 tag = tag.split(" ")[-1]
1222 if tag.lower() in tags:
1225 if tag.lower() not in tags:
1228 y_sorted[suite] = y_tmp_vals.pop(suite)
1230 logging.debug(suite)
1231 except KeyError as err:
1232 logging.error("Not found: {0}".format(repr(err)))
1236 y_sorted = y_tmp_vals
1238 logging.debug("y_sorted: {0}\n".format(y_sorted))
1243 nr_of_samples = list()
1244 for key, val in y_sorted.items():
1245 name = re.sub(REGEX_NIC, "", key.replace('-ndrpdr', '').
1246 replace('2n1l-', ''))
1247 x_vals.append(name) # dir 1
1248 y_vals.append(mean(val[1]) if val[1] else None)
1249 y_mins.append(mean(val[0]) if val[0] else None)
1250 y_maxs.append(mean(val[2]) if val[2] else None)
1251 nr_of_samples.append(len(val[1]) if val[1] else 0)
1252 x_vals.append(name) # dir 2
1253 y_vals.append(mean(val[4]) if val[4] else None)
1254 y_mins.append(mean(val[3]) if val[3] else None)
1255 y_maxs.append(mean(val[5]) if val[5] else None)
1256 nr_of_samples.append(len(val[3]) if val[3] else 0)
1258 logging.debug("x_vals :{0}\n".format(x_vals))
1259 logging.debug("y_vals :{0}\n".format(y_vals))
1260 logging.debug("y_mins :{0}\n".format(y_mins))
1261 logging.debug("y_maxs :{0}\n".format(y_maxs))
1262 logging.debug("nr_of_samples :{0}\n".format(nr_of_samples))
1264 annotations = list()
1266 for idx in range(len(x_vals)):
1267 if not bool(int(idx % 2)):
1268 direction = "West-East"
1270 direction = "East-West"
1271 hovertext = ("No. of Runs: {nr}<br>"
1273 "Direction: {dir}<br>".format(test=x_vals[idx],
1275 nr=nr_of_samples[idx]))
1276 if isinstance(y_maxs[idx], float):
1277 hovertext += "Max: {max:.2f}uSec<br>".format(max=y_maxs[idx])
1278 if isinstance(y_vals[idx], float):
1279 hovertext += "Mean: {avg:.2f}uSec<br>".format(avg=y_vals[idx])
1280 if isinstance(y_mins[idx], float):
1281 hovertext += "Min: {min:.2f}uSec".format(min=y_mins[idx])
1283 if isinstance(y_maxs[idx], float) and isinstance(y_vals[idx], float):
1284 array = [y_maxs[idx] - y_vals[idx], ]
1287 if isinstance(y_mins[idx], float) and isinstance(y_vals[idx], float):
1288 arrayminus = [y_vals[idx] - y_mins[idx], ]
1290 arrayminus = [None, ]
1291 logging.debug("y_vals[{1}] :{0}\n".format(y_vals[idx], idx))
1292 logging.debug("array :{0}\n".format(array))
1293 logging.debug("arrayminus :{0}\n".format(arrayminus))
1294 traces.append(plgo.Scatter(
1298 legendgroup=x_vals[idx],
1299 showlegend=bool(int(idx % 2)),
1305 arrayminus=arrayminus,
1306 color=COLORS[int(idx / 2)]
1310 color=COLORS[int(idx / 2)],
1315 annotations.append(dict(
1322 text="E-W" if bool(int(idx % 2)) else "W-E",
1332 logging.info(" Writing file '{0}{1}'.".
1333 format(plot["output-file"], plot["output-file-type"]))
1334 layout = deepcopy(plot["layout"])
1335 if layout.get("title", None):
1336 layout["title"] = "<b>Latency:</b> {0}".\
1337 format(layout["title"])
1338 layout["annotations"] = annotations
1339 plpl = plgo.Figure(data=traces, layout=layout)
1343 show_link=False, auto_open=False,
1344 filename='{0}{1}'.format(plot["output-file"],
1345 plot["output-file-type"]))
1346 except PlotlyError as err:
1347 logging.error(" Finished with error: {}".
1348 format(str(err).replace("\n", " ")))
1352 def plot_throughput_speedup_analysis(plot, input_data):
1353 """Generate the plot(s) with algorithm:
1354 plot_throughput_speedup_analysis
1355 specified in the specification file.
1357 TODO: Remove when not needed.
1359 :param plot: Plot to generate.
1360 :param input_data: Data to process.
1361 :type plot: pandas.Series
1362 :type input_data: InputData
1365 # Transform the data
1366 plot_title = plot.get("title", "")
1367 logging.info(" Creating the data set for the {0} '{1}'.".
1368 format(plot.get("type", ""), plot_title))
1369 data = input_data.filter_data(plot)
1371 logging.error("No data.")
1379 if y_vals.get(test["parent"], None) is None:
1380 y_vals[test["parent"]] = {"1": list(),
1383 y_tags[test["parent"]] = test.get("tags", None)
1385 if test["type"] in ("NDRPDR",):
1386 if "-pdr" in plot_title.lower():
1388 elif "-ndr" in plot_title.lower():
1392 if "1C" in test["tags"]:
1393 y_vals[test["parent"]]["1"]. \
1394 append(test["throughput"][ttype]["LOWER"])
1395 elif "2C" in test["tags"]:
1396 y_vals[test["parent"]]["2"]. \
1397 append(test["throughput"][ttype]["LOWER"])
1398 elif "4C" in test["tags"]:
1399 y_vals[test["parent"]]["4"]. \
1400 append(test["throughput"][ttype]["LOWER"])
1401 except (KeyError, TypeError):
1405 logging.warning("No data for the plot '{}'".
1406 format(plot.get("title", "")))
1410 for test_name, test_vals in y_vals.items():
1411 for key, test_val in test_vals.items():
1413 avg_val = sum(test_val) / len(test_val)
1414 y_vals[test_name][key] = (avg_val, len(test_val))
1415 ideal = avg_val / (int(key) * 1000000.0)
1416 if test_name not in y_1c_max or ideal > y_1c_max[test_name]:
1417 y_1c_max[test_name] = ideal
1423 pci_limit = plot["limits"]["pci"]["pci-g3-x8"]
1424 for test_name, test_vals in y_vals.items():
1426 if test_vals["1"][1]:
1427 name = re.sub(REGEX_NIC, "", test_name.replace('-ndrpdr', '').
1428 replace('2n1l-', ''))
1430 y_val_1 = test_vals["1"][0] / 1000000.0
1431 y_val_2 = test_vals["2"][0] / 1000000.0 if test_vals["2"][0] \
1433 y_val_4 = test_vals["4"][0] / 1000000.0 if test_vals["4"][0] \
1436 vals[name]["val"] = [y_val_1, y_val_2, y_val_4]
1437 vals[name]["rel"] = [1.0, None, None]
1438 vals[name]["ideal"] = [y_1c_max[test_name],
1439 y_1c_max[test_name] * 2,
1440 y_1c_max[test_name] * 4]
1441 vals[name]["diff"] = [(y_val_1 - y_1c_max[test_name]) * 100 /
1442 y_val_1, None, None]
1443 vals[name]["count"] = [test_vals["1"][1],
1448 # val_max = max(max(vals[name]["val"], vals[name]["ideal"]))
1449 val_max = max(vals[name]["val"])
1450 except ValueError as err:
1454 # y_max.append(int((val_max / 10) + 1) * 10)
1455 y_max.append(val_max)
1458 vals[name]["rel"][1] = round(y_val_2 / y_val_1, 2)
1459 vals[name]["diff"][1] = \
1460 (y_val_2 - vals[name]["ideal"][1]) * 100 / y_val_2
1462 vals[name]["rel"][2] = round(y_val_4 / y_val_1, 2)
1463 vals[name]["diff"][2] = \
1464 (y_val_4 - vals[name]["ideal"][2]) * 100 / y_val_4
1465 except IndexError as err:
1466 logging.warning("No data for '{0}'".format(test_name))
1467 logging.warning(repr(err))
1470 if "x520" in test_name:
1471 limit = plot["limits"]["nic"]["x520"]
1472 elif "x710" in test_name:
1473 limit = plot["limits"]["nic"]["x710"]
1474 elif "xxv710" in test_name:
1475 limit = plot["limits"]["nic"]["xxv710"]
1476 elif "xl710" in test_name:
1477 limit = plot["limits"]["nic"]["xl710"]
1478 elif "x553" in test_name:
1479 limit = plot["limits"]["nic"]["x553"]
1482 if limit > nic_limit:
1485 mul = 2 if "ge2p" in test_name else 1
1486 if "10ge" in test_name:
1487 limit = plot["limits"]["link"]["10ge"] * mul
1488 elif "25ge" in test_name:
1489 limit = plot["limits"]["link"]["25ge"] * mul
1490 elif "40ge" in test_name:
1491 limit = plot["limits"]["link"]["40ge"] * mul
1492 elif "100ge" in test_name:
1493 limit = plot["limits"]["link"]["100ge"] * mul
1496 if limit > lnk_limit:
1500 order = plot.get("sort", None)
1501 if order and y_tags:
1502 y_sorted = OrderedDict()
1503 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
1505 for test, tags in y_tags_l.items():
1506 if tag.lower() in tags:
1507 name = re.sub(REGEX_NIC, "",
1508 test.replace('-ndrpdr', '').
1509 replace('2n1l-', ''))
1511 y_sorted[name] = vals.pop(name)
1513 except KeyError as err:
1514 logging.error("Not found: {0}".format(err))
1521 annotations = list()
1526 threshold = 1.1 * max(y_max) # 10%
1527 except ValueError as err:
1530 nic_limit /= 1000000.0
1531 # if nic_limit < threshold:
1532 traces.append(plgo.Scatter(
1534 y=[nic_limit, ] * len(x_vals),
1535 name="NIC: {0:.2f}Mpps".format(nic_limit),
1544 annotations.append(dict(
1551 text="NIC: {0:.2f}Mpps".format(nic_limit),
1559 # y_max.append(int((nic_limit / 10) + 1) * 10)
1560 y_max.append(nic_limit)
1562 lnk_limit /= 1000000.0
1563 if lnk_limit < threshold:
1564 traces.append(plgo.Scatter(
1566 y=[lnk_limit, ] * len(x_vals),
1567 name="Link: {0:.2f}Mpps".format(lnk_limit),
1576 annotations.append(dict(
1583 text="Link: {0:.2f}Mpps".format(lnk_limit),
1591 # y_max.append(int((lnk_limit / 10) + 1) * 10)
1592 y_max.append(lnk_limit)
1594 pci_limit /= 1000000.0
1595 if (pci_limit < threshold and
1596 (pci_limit < lnk_limit * 0.95 or lnk_limit > lnk_limit * 1.05)):
1597 traces.append(plgo.Scatter(
1599 y=[pci_limit, ] * len(x_vals),
1600 name="PCIe: {0:.2f}Mpps".format(pci_limit),
1609 annotations.append(dict(
1616 text="PCIe: {0:.2f}Mpps".format(pci_limit),
1624 # y_max.append(int((pci_limit / 10) + 1) * 10)
1625 y_max.append(pci_limit)
1627 # Perfect and measured:
1629 for name, val in y_sorted.iteritems():
1632 for idx in range(len(val["val"])):
1634 if isinstance(val["val"][idx], float):
1635 htext += "No. of Runs: {1}<br>" \
1636 "Mean: {0:.2f}Mpps<br>".format(val["val"][idx],
1638 if isinstance(val["diff"][idx], float):
1639 htext += "Diff: {0:.0f}%<br>".format(round(val["diff"][idx]))
1640 if isinstance(val["rel"][idx], float):
1641 htext += "Speedup: {0:.2f}".format(val["rel"][idx])
1642 hovertext.append(htext)
1643 traces.append(plgo.Scatter(x=x_vals,
1647 mode="lines+markers",
1656 hoverinfo="text+name"
1658 traces.append(plgo.Scatter(x=x_vals,
1660 name="{0} perfect".format(name),
1668 text=["Perfect: {0:.2f}Mpps".format(y)
1669 for y in val["ideal"]],
1673 except (IndexError, ValueError, KeyError) as err:
1674 logging.warning("No data for '{0}'".format(name))
1675 logging.warning(repr(err))
1679 logging.info(" Writing file '{0}{1}'.".
1680 format(plot["output-file"], plot["output-file-type"]))
1681 layout = deepcopy(plot["layout"])
1682 if layout.get("title", None):
1683 layout["title"] = "<b>Speedup Multi-core:</b> {0}". \
1684 format(layout["title"])
1685 # layout["yaxis"]["range"] = [0, int((max(y_max) / 10) + 1) * 10]
1686 layout["yaxis"]["range"] = [0, int(max(y_max) * 1.1)]
1687 layout["annotations"].extend(annotations)
1688 plpl = plgo.Figure(data=traces, layout=layout)
1692 show_link=False, auto_open=False,
1693 filename='{0}{1}'.format(plot["output-file"],
1694 plot["output-file-type"]))
1695 except PlotlyError as err:
1696 logging.error(" Finished with error: {}".
1697 format(str(err).replace("\n", " ")))
1701 def plot_http_server_performance_box(plot, input_data):
1702 """Generate the plot(s) with algorithm: plot_http_server_performance_box
1703 specified in the specification file.
1705 :param plot: Plot to generate.
1706 :param input_data: Data to process.
1707 :type plot: pandas.Series
1708 :type input_data: InputData
1711 # Transform the data
1712 logging.info(" Creating the data set for the {0} '{1}'.".
1713 format(plot.get("type", ""), plot.get("title", "")))
1714 data = input_data.filter_data(plot)
1716 logging.error("No data.")
1719 # Prepare the data for the plot
1724 if y_vals.get(test["name"], None) is None:
1725 y_vals[test["name"]] = list()
1727 y_vals[test["name"]].append(test["result"])
1728 except (KeyError, TypeError):
1729 y_vals[test["name"]].append(None)
1731 # Add None to the lists with missing data
1733 nr_of_samples = list()
1734 for val in y_vals.values():
1735 if len(val) > max_len:
1737 nr_of_samples.append(len(val))
1738 for key, val in y_vals.items():
1739 if len(val) < max_len:
1740 val.extend([None for _ in range(max_len - len(val))])
1744 df = pd.DataFrame(y_vals)
1746 for i, col in enumerate(df.columns):
1747 name = "{nr}. ({samples:02d} run{plural}) {name}".\
1749 samples=nr_of_samples[i],
1750 plural='s' if nr_of_samples[i] > 1 else '',
1751 name=col.lower().replace('-ndrpdr', ''))
1753 name_lst = name.split('-')
1756 for segment in name_lst:
1757 if (len(name) + len(segment) + 1) > 50 and split_name:
1760 name += segment + '-'
1763 traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
1769 plpl = plgo.Figure(data=traces, layout=plot["layout"])
1772 logging.info(" Writing file '{0}{1}'.".
1773 format(plot["output-file"], plot["output-file-type"]))
1774 ploff.plot(plpl, show_link=False, auto_open=False,
1775 filename='{0}{1}'.format(plot["output-file"],
1776 plot["output-file-type"]))
1777 except PlotlyError as err:
1778 logging.error(" Finished with error: {}".
1779 format(str(err).replace("\n", " ")))
1783 def plot_service_density_heatmap(plot, input_data):
1784 """Generate the plot(s) with algorithm: plot_service_density_heatmap
1785 specified in the specification file.
1787 :param plot: Plot to generate.
1788 :param input_data: Data to process.
1789 :type plot: pandas.Series
1790 :type input_data: InputData
1793 REGEX_CN = re.compile(r'^(\d*)R(\d*)C$')
1794 REGEX_TEST_NAME = re.compile(r'^.*-(\d+ch|\d+pl)-'
1796 r'(\d+vm\d+t|\d+dcr\d+t).*$')
1802 # Transform the data
1803 logging.info(" Creating the data set for the {0} '{1}'.".
1804 format(plot.get("type", ""), plot.get("title", "")))
1805 data = input_data.filter_data(plot, continue_on_error=True)
1806 if data is None or data.empty:
1807 logging.error("No data.")
1813 for tag in test['tags']:
1814 groups = re.search(REGEX_CN, tag)
1816 c = str(groups.group(1))
1817 n = str(groups.group(2))
1821 groups = re.search(REGEX_TEST_NAME, test["name"])
1822 if groups and len(groups.groups()) == 3:
1823 hover_name = "{chain}-{vhost}-{vm}".format(
1824 chain=str(groups.group(1)),
1825 vhost=str(groups.group(2)),
1826 vm=str(groups.group(3)))
1829 if vals.get(c, None) is None:
1831 if vals[c].get(n, None) is None:
1832 vals[c][n] = dict(name=hover_name,
1838 if plot["include-tests"] == "MRR":
1839 result = test["result"]["receive-rate"].avg
1840 elif plot["include-tests"] == "PDR":
1841 result = test["throughput"]["PDR"]["LOWER"]
1842 elif plot["include-tests"] == "NDR":
1843 result = test["throughput"]["NDR"]["LOWER"]
1850 vals[c][n]["vals"].append(result)
1853 logging.error("No data.")
1856 for key_c in vals.keys():
1857 txt_chains.append(key_c)
1858 for key_n in vals[key_c].keys():
1859 txt_nodes.append(key_n)
1860 if vals[key_c][key_n]["vals"]:
1861 vals[key_c][key_n]["nr"] = len(vals[key_c][key_n]["vals"])
1862 vals[key_c][key_n]["mean"] = \
1863 round(mean(vals[key_c][key_n]["vals"]) / 1000000, 1)
1864 vals[key_c][key_n]["stdev"] = \
1865 round(stdev(vals[key_c][key_n]["vals"]) / 1000000, 1)
1866 txt_nodes = list(set(txt_nodes))
1868 txt_chains = sorted(txt_chains, key=lambda chain: int(chain))
1869 txt_nodes = sorted(txt_nodes, key=lambda node: int(node))
1871 chains = [i + 1 for i in range(len(txt_chains))]
1872 nodes = [i + 1 for i in range(len(txt_nodes))]
1874 data = [list() for _ in range(len(chains))]
1878 val = vals[txt_chains[c - 1]][txt_nodes[n - 1]]["mean"]
1879 except (KeyError, IndexError):
1881 data[c - 1].append(val)
1884 my_green = [[0.0, 'rgb(235, 249, 242)'],
1885 [1.0, 'rgb(45, 134, 89)']]
1887 my_blue = [[0.0, 'rgb(236, 242, 248)'],
1888 [1.0, 'rgb(57, 115, 172)']]
1890 my_grey = [[0.0, 'rgb(230, 230, 230)'],
1891 [1.0, 'rgb(102, 102, 102)']]
1894 annotations = list()
1896 text = ("Test: {name}<br>"
1901 for c in range(len(txt_chains)):
1903 for n in range(len(txt_nodes)):
1904 if data[c][n] is not None:
1905 annotations.append(dict(
1912 text=str(data[c][n]),
1919 hover_line.append(text.format(
1920 name=vals[txt_chains[c]][txt_nodes[n]]["name"],
1921 nr=vals[txt_chains[c]][txt_nodes[n]]["nr"],
1923 stdev=vals[txt_chains[c]][txt_nodes[n]]["stdev"]))
1924 hovertext.append(hover_line)
1927 plgo.Heatmap(x=nodes,
1931 title=plot.get("z-axis", ""),
1945 colorscale=my_green,
1950 for idx, item in enumerate(txt_nodes):
1952 annotations.append(dict(
1966 for idx, item in enumerate(txt_chains):
1968 annotations.append(dict(
1983 annotations.append(dict(
1990 text=plot.get("x-axis", ""),
1998 annotations.append(dict(
2005 text=plot.get("y-axis", ""),
2013 updatemenus = list([
2022 args=[{"colorscale": [my_green, ], "reversescale": False}],
2027 args=[{"colorscale": [my_blue, ], "reversescale": False}],
2032 args=[{"colorscale": [my_grey, ], "reversescale": False}],
2041 layout = deepcopy(plot["layout"])
2042 except KeyError as err:
2043 logging.error("Finished with error: No layout defined")
2044 logging.error(repr(err))
2047 layout["annotations"] = annotations
2048 layout['updatemenus'] = updatemenus
2052 plpl = plgo.Figure(data=traces, layout=layout)
2055 logging.info(" Writing file '{0}{1}'.".
2056 format(plot["output-file"], plot["output-file-type"]))
2057 ploff.plot(plpl, show_link=False, auto_open=False,
2058 filename='{0}{1}'.format(plot["output-file"],
2059 plot["output-file-type"]))
2060 except PlotlyError as err:
2061 logging.error(" Finished with error: {}".
2062 format(str(err).replace("\n", " ")))
2066 def plot_service_density_heatmap_compare(plot, input_data):
2067 """Generate the plot(s) with algorithm: plot_service_density_heatmap_compare
2068 specified in the specification file.
2070 :param plot: Plot to generate.
2071 :param input_data: Data to process.
2072 :type plot: pandas.Series
2073 :type input_data: InputData
2076 REGEX_CN = re.compile(r'^(\d*)R(\d*)C$')
2077 REGEX_TEST_NAME = re.compile(r'^.*-(\d+ch|\d+pl)-'
2079 r'(\d+vm\d+t|\d+dcr\d+t).*$')
2080 REGEX_THREADS = re.compile(r'^(\d+)(VM|DCR)(\d+)T$')
2086 # Transform the data
2087 logging.info(" Creating the data set for the {0} '{1}'.".
2088 format(plot.get("type", ""), plot.get("title", "")))
2089 data = input_data.filter_data(plot, continue_on_error=True)
2090 if data is None or data.empty:
2091 logging.error("No data.")
2097 for tag in test['tags']:
2098 groups = re.search(REGEX_CN, tag)
2100 c = str(groups.group(1))
2101 n = str(groups.group(2))
2105 groups = re.search(REGEX_TEST_NAME, test["name"])
2106 if groups and len(groups.groups()) == 3:
2107 hover_name = "{chain}-{vhost}-{vm}".format(
2108 chain=str(groups.group(1)),
2109 vhost=str(groups.group(2)),
2110 vm=str(groups.group(3)))
2113 if vals.get(c, None) is None:
2115 if vals[c].get(n, None) is None:
2116 vals[c][n] = dict(name=hover_name,
2126 if plot["include-tests"] == "MRR":
2127 result = test["result"]["receive-rate"].avg
2128 elif plot["include-tests"] == "PDR":
2129 result = test["throughput"]["PDR"]["LOWER"]
2130 elif plot["include-tests"] == "NDR":
2131 result = test["throughput"]["NDR"]["LOWER"]
2138 for tag in test['tags']:
2139 groups = re.search(REGEX_THREADS, tag)
2140 if groups and len(groups.groups()) == 3:
2141 if str(groups.group(3)) == \
2142 plot["reference"]["include"]:
2143 vals[c][n]["vals_r"].append(result)
2144 elif str(groups.group(3)) == \
2145 plot["compare"]["include"]:
2146 vals[c][n]["vals_c"].append(result)
2149 logging.error("No data.")
2152 for key_c in vals.keys():
2153 txt_chains.append(key_c)
2154 for key_n in vals[key_c].keys():
2155 txt_nodes.append(key_n)
2156 if vals[key_c][key_n]["vals_r"]:
2157 vals[key_c][key_n]["nr_r"] = len(vals[key_c][key_n]["vals_r"])
2158 vals[key_c][key_n]["mean_r"] = \
2159 mean(vals[key_c][key_n]["vals_r"])
2160 vals[key_c][key_n]["stdev_r"] = \
2161 round(stdev(vals[key_c][key_n]["vals_r"]) / 1000000, 1)
2162 if vals[key_c][key_n]["vals_c"]:
2163 vals[key_c][key_n]["nr_c"] = len(vals[key_c][key_n]["vals_c"])
2164 vals[key_c][key_n]["mean_c"] = \
2165 mean(vals[key_c][key_n]["vals_c"])
2166 vals[key_c][key_n]["stdev_c"] = \
2167 round(stdev(vals[key_c][key_n]["vals_c"]) / 1000000, 1)
2169 txt_nodes = list(set(txt_nodes))
2171 txt_chains = sorted(txt_chains, key=lambda chain: int(chain))
2172 txt_nodes = sorted(txt_nodes, key=lambda node: int(node))
2174 chains = [i + 1 for i in range(len(txt_chains))]
2175 nodes = [i + 1 for i in range(len(txt_nodes))]
2177 data_r = [list() for _ in range(len(chains))]
2178 data_c = [list() for _ in range(len(chains))]
2179 diff = [list() for _ in range(len(chains))]
2183 val_r = vals[txt_chains[c - 1]][txt_nodes[n - 1]]["mean_r"]
2184 except (KeyError, IndexError):
2187 val_c = vals[txt_chains[c - 1]][txt_nodes[n - 1]]["mean_c"]
2188 except (KeyError, IndexError):
2190 if val_c is not None and val_r:
2191 val_d = (val_c - val_r) * 100 / val_r
2195 if val_r is not None:
2196 val_r = round(val_r / 1000000, 1)
2197 data_r[c - 1].append(val_r)
2198 if val_c is not None:
2199 val_c = round(val_c / 1000000, 1)
2200 data_c[c - 1].append(val_c)
2201 if val_d is not None:
2202 val_d = int(round(val_d, 0))
2203 diff[c - 1].append(val_d)
2206 my_green = [[0.0, 'rgb(235, 249, 242)'],
2207 [1.0, 'rgb(45, 134, 89)']]
2209 my_blue = [[0.0, 'rgb(236, 242, 248)'],
2210 [1.0, 'rgb(57, 115, 172)']]
2212 my_grey = [[0.0, 'rgb(230, 230, 230)'],
2213 [1.0, 'rgb(102, 102, 102)']]
2217 annotations = list()
2218 annotations_r = list()
2219 annotations_c = list()
2220 annotations_diff = list()
2222 text = ("Test: {name}"
2223 "<br>{title_r}: {text_r}"
2224 "<br>{title_c}: {text_c}{text_diff}")
2225 text_r = "Thput: {val_r}; StDev: {stdev_r}; Runs: {nr_r}"
2226 text_c = "Thput: {val_c}; StDev: {stdev_c}; Runs: {nr_c}"
2227 text_diff = "<br>Relative Difference {title_c} vs. {title_r}: {diff}%"
2229 for c in range(len(txt_chains)):
2231 for n in range(len(txt_nodes)):
2247 point_text_r = "Not present"
2248 point_text_c = "Not present"
2249 point_text_diff = ""
2251 point_r = data_r[c][n]
2252 if point_r is not None:
2253 point_text_r = text_r.format(
2255 stdev_r=vals[txt_chains[c]][txt_nodes[n]]["stdev_r"],
2256 nr_r=vals[txt_chains[c]][txt_nodes[n]]["nr_r"])
2259 point["text"] = "" if point_r is None else point_r
2260 annotations_r.append(deepcopy(point))
2263 point_c = data_c[c][n]
2264 if point_c is not None:
2265 point_text_c = text_c.format(
2267 stdev_c=vals[txt_chains[c]][txt_nodes[n]]["stdev_c"],
2268 nr_c=vals[txt_chains[c]][txt_nodes[n]]["nr_c"])
2271 point["text"] = "" if point_c is None else point_c
2272 annotations_c.append(deepcopy(point))
2275 point_d = diff[c][n]
2276 if point_d is not None:
2277 point_text_diff = text_diff.format(
2278 title_r=plot["reference"]["name"],
2279 title_c=plot["compare"]["name"],
2283 point["text"] = "" if point_d is None else point_d
2284 annotations_diff.append(deepcopy(point))
2287 name = vals[txt_chains[c]][txt_nodes[n]]["name"]
2291 hover_line.append(text.format(
2293 title_r=plot["reference"]["name"],
2294 text_r=point_text_r,
2295 title_c=plot["compare"]["name"],
2296 text_c=point_text_c,
2297 text_diff=point_text_diff
2300 hovertext.append(hover_line)
2303 plgo.Heatmap(x=nodes,
2308 title=plot.get("z-axis", ""),
2322 colorscale=my_green,
2326 plgo.Heatmap(x=nodes,
2331 title=plot.get("z-axis", ""),
2349 plgo.Heatmap(x=nodes,
2355 title="Relative Difference {name_c} vs. {name_r} [%]".
2356 format(name_c=plot["compare"]["name"],
2357 name_r=plot["reference"]["name"]),
2377 for idx, item in enumerate(txt_nodes):
2379 annotations.append(dict(
2393 for idx, item in enumerate(txt_chains):
2395 annotations.append(dict(
2410 annotations.append(dict(
2417 text=plot.get("x-axis", ""),
2425 annotations.append(dict(
2432 text=plot.get("y-axis", ""),
2440 updatemenus = list([
2450 label=plot["reference"]["name"],
2454 "visible": [True, False, False]
2457 "colorscale": [my_green, ],
2458 "reversescale": False,
2459 "annotations": annotations + annotations_r,
2464 label=plot["compare"]["name"],
2468 "visible": [False, True, False]
2471 "colorscale": [my_blue, ],
2472 "reversescale": False,
2473 "annotations": annotations + annotations_c,
2482 "visible": [False, False, True]
2485 "colorscale": [my_grey, ],
2486 "reversescale": False,
2487 "annotations": annotations + annotations_diff,
2496 layout = deepcopy(plot["layout"])
2497 except KeyError as err:
2498 logging.error("Finished with error: No layout defined")
2499 logging.error(repr(err))
2502 layout["annotations"] = annotations + annotations_r
2503 layout['updatemenus'] = updatemenus
2507 plpl = plgo.Figure(data=traces, layout=layout)
2510 logging.info(" Writing file '{0}{1}'.".
2511 format(plot["output-file"], plot["output-file-type"]))
2512 ploff.plot(plpl, show_link=False, auto_open=False,
2513 filename='{0}{1}'.format(plot["output-file"],
2514 plot["output-file-type"]))
2515 except PlotlyError as err:
2516 logging.error(" Finished with error: {}".
2517 format(str(err).replace("\n", " ")))