1 # Copyright (c) 2019 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
6 # http://www.apache.org/licenses/LICENSE-2.0
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
14 """Algorithms to generate plots.
21 import plotly.offline as ploff
22 import plotly.graph_objs as plgo
24 from plotly.exceptions import PlotlyError
25 from collections import OrderedDict
26 from copy import deepcopy
28 from utils import mean, stdev
31 COLORS = ["SkyBlue", "Olive", "Purple", "Coral", "Indigo", "Pink",
32 "Chocolate", "Brown", "Magenta", "Cyan", "Orange", "Black",
33 "Violet", "Blue", "Yellow", "BurlyWood", "CadetBlue", "Crimson",
34 "DarkBlue", "DarkCyan", "DarkGreen", "Green", "GoldenRod",
35 "LightGreen", "LightSeaGreen", "LightSkyBlue", "Maroon",
36 "MediumSeaGreen", "SeaGreen", "LightSlateGrey"]
38 REGEX_NIC = re.compile(r'\d*ge\dp\d\D*\d*-')
41 def generate_plots(spec, data):
42 """Generate all plots specified in the specification file.
44 :param spec: Specification read from the specification file.
45 :param data: Data to process.
46 :type spec: Specification
50 logging.info("Generating the plots ...")
51 for index, plot in enumerate(spec.plots):
53 logging.info(" Plot nr {0}: {1}".format(index + 1,
54 plot.get("title", "")))
55 plot["limits"] = spec.configuration["limits"]
56 eval(plot["algorithm"])(plot, data)
57 logging.info(" Done.")
58 except NameError as err:
59 logging.error("Probably algorithm '{alg}' is not defined: {err}".
60 format(alg=plot["algorithm"], err=repr(err)))
64 def plot_service_density_reconf_box_name(plot, input_data):
65 """Generate the plot(s) with algorithm: plot_service_density_reconf_box_name
66 specified in the specification file.
68 :param plot: Plot to generate.
69 :param input_data: Data to process.
70 :type plot: pandas.Series
71 :type input_data: InputData
75 plot_title = plot.get("title", "")
76 logging.info(" Creating the data set for the {0} '{1}'.".
77 format(plot.get("type", ""), plot_title))
78 data = input_data.filter_tests_by_name(
79 plot, params=["result", "parent", "tags", "type"])
81 logging.error("No data.")
84 # Prepare the data for the plot
85 y_vals = OrderedDict()
90 if y_vals.get(test["parent"], None) is None:
91 y_vals[test["parent"]] = list()
92 loss[test["parent"]] = list()
94 y_vals[test["parent"]].append(test["result"]["time"])
95 loss[test["parent"]].append(test["result"]["loss"])
96 except (KeyError, TypeError):
97 y_vals[test["parent"]].append(None)
99 # Add None to the lists with missing data
101 nr_of_samples = list()
102 for val in y_vals.values():
103 if len(val) > max_len:
105 nr_of_samples.append(len(val))
106 for key, val in y_vals.items():
107 if len(val) < max_len:
108 val.extend([None for _ in range(max_len - len(val))])
112 df = pd.DataFrame(y_vals)
114 for i, col in enumerate(df.columns):
115 tst_name = re.sub(REGEX_NIC, "",
116 col.lower().replace('-ndrpdr', '').
117 replace('2n1l-', ''))
118 tst_name = "-".join(tst_name.split("-")[3:-2])
119 name = "{nr}. ({samples:02d} run{plural}, avg pkt loss: {loss:.1f}, " \
120 "stdev: {stdev:.2f}) {name}".format(
122 samples=nr_of_samples[i],
123 plural='s' if nr_of_samples[i] > 1 else '',
125 loss=mean(loss[col]) / 1000000,
126 stdev=stdev(loss[col]) / 1000000)
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",
136 layout = deepcopy(plot["layout"])
137 layout["title"] = "<b>Time Lost:</b> {0}".format(layout["title"])
138 layout["yaxis"]["title"] = "<b>Implied Time Lost [s]</b>"
139 layout["legend"]["font"]["size"] = 14
140 layout["yaxis"].pop("range")
141 plpl = plgo.Figure(data=traces, layout=layout)
144 file_type = plot.get("output-file-type", ".html")
145 logging.info(" Writing file '{0}{1}'.".
146 format(plot["output-file"], file_type))
147 ploff.plot(plpl, show_link=False, auto_open=False,
148 filename='{0}{1}'.format(plot["output-file"], file_type))
149 except PlotlyError as err:
150 logging.error(" Finished with error: {}".
151 format(repr(err).replace("\n", " ")))
155 def plot_performance_box_name(plot, input_data):
156 """Generate the plot(s) with algorithm: plot_performance_box_name
157 specified in the specification file.
159 :param plot: Plot to generate.
160 :param input_data: Data to process.
161 :type plot: pandas.Series
162 :type input_data: InputData
166 plot_title = plot.get("title", "")
167 logging.info(" Creating the data set for the {0} '{1}'.".
168 format(plot.get("type", ""), plot_title))
169 data = input_data.filter_tests_by_name(
170 plot, params=["throughput", "parent", "tags", "type"])
172 logging.error("No data.")
175 # Prepare the data for the plot
176 y_vals = OrderedDict()
180 if y_vals.get(test["parent"], None) is None:
181 y_vals[test["parent"]] = list()
183 if test["type"] in ("NDRPDR", ):
184 if "-pdr" in plot_title.lower():
185 y_vals[test["parent"]].\
186 append(test["throughput"]["PDR"]["LOWER"])
187 elif "-ndr" in plot_title.lower():
188 y_vals[test["parent"]]. \
189 append(test["throughput"]["NDR"]["LOWER"])
192 elif test["type"] in ("SOAK", ):
193 y_vals[test["parent"]].\
194 append(test["throughput"]["LOWER"])
197 except (KeyError, TypeError):
198 y_vals[test["parent"]].append(None)
200 # Add None to the lists with missing data
202 nr_of_samples = list()
203 for val in y_vals.values():
204 if len(val) > max_len:
206 nr_of_samples.append(len(val))
207 for key, val in y_vals.items():
208 if len(val) < max_len:
209 val.extend([None for _ in range(max_len - len(val))])
213 df = pd.DataFrame(y_vals)
216 for i, col in enumerate(df.columns):
217 tst_name = re.sub(REGEX_NIC, "",
218 col.lower().replace('-ndrpdr', '').
219 replace('2n1l-', ''))
220 name = "{nr}. ({samples:02d} run{plural}) {name}".\
222 samples=nr_of_samples[i],
223 plural='s' if nr_of_samples[i] > 1 else '',
227 traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
228 y=[y / 1000000 if y else None for y in df[col]],
231 boxpoints="outliers",
234 val_max = max(df[col])
235 except ValueError as err:
236 logging.error(repr(err))
239 y_max.append(int(val_max / 1000000) + 2)
243 layout = deepcopy(plot["layout"])
244 if layout.get("title", None):
245 layout["title"] = "<b>Throughput:</b> {0}". \
246 format(layout["title"])
248 layout["yaxis"]["range"] = [0, max(y_max)]
249 plpl = plgo.Figure(data=traces, layout=layout)
252 file_type = plot.get("output-file-type", ".html")
253 logging.info(" Writing file '{0}{1}'.".
254 format(plot["output-file"], file_type))
255 ploff.plot(plpl, show_link=False, auto_open=False,
256 filename='{0}{1}'.format(plot["output-file"], file_type))
257 except PlotlyError as err:
258 logging.error(" Finished with error: {}".
259 format(repr(err).replace("\n", " ")))
263 def plot_latency_error_bars_name(plot, input_data):
264 """Generate the plot(s) with algorithm: plot_latency_error_bars_name
265 specified in the specification file.
267 :param plot: Plot to generate.
268 :param input_data: Data to process.
269 :type plot: pandas.Series
270 :type input_data: InputData
274 plot_title = plot.get("title", "")
275 logging.info(" Creating the data set for the {0} '{1}'.".
276 format(plot.get("type", ""), plot_title))
277 data = input_data.filter_tests_by_name(
278 plot, params=["latency", "parent", "tags", "type"])
280 logging.error("No data.")
283 # Prepare the data for the plot
284 y_tmp_vals = OrderedDict()
289 logging.debug("test['latency']: {0}\n".
290 format(test["latency"]))
291 except ValueError as err:
292 logging.warning(repr(err))
293 if y_tmp_vals.get(test["parent"], None) is None:
294 y_tmp_vals[test["parent"]] = [
295 list(), # direction1, min
296 list(), # direction1, avg
297 list(), # direction1, max
298 list(), # direction2, min
299 list(), # direction2, avg
300 list() # direction2, max
303 if test["type"] in ("NDRPDR", ):
304 if "-pdr" in plot_title.lower():
306 elif "-ndr" in plot_title.lower():
309 logging.warning("Invalid test type: {0}".
310 format(test["type"]))
312 y_tmp_vals[test["parent"]][0].append(
313 test["latency"][ttype]["direction1"]["min"])
314 y_tmp_vals[test["parent"]][1].append(
315 test["latency"][ttype]["direction1"]["avg"])
316 y_tmp_vals[test["parent"]][2].append(
317 test["latency"][ttype]["direction1"]["max"])
318 y_tmp_vals[test["parent"]][3].append(
319 test["latency"][ttype]["direction2"]["min"])
320 y_tmp_vals[test["parent"]][4].append(
321 test["latency"][ttype]["direction2"]["avg"])
322 y_tmp_vals[test["parent"]][5].append(
323 test["latency"][ttype]["direction2"]["max"])
325 logging.warning("Invalid test type: {0}".
326 format(test["type"]))
328 except (KeyError, TypeError) as err:
329 logging.warning(repr(err))
335 nr_of_samples = list()
336 for key, val in y_tmp_vals.items():
337 name = re.sub(REGEX_NIC, "", key.replace('-ndrpdr', '').
338 replace('2n1l-', ''))
339 x_vals.append(name) # dir 1
340 y_vals.append(mean(val[1]) if val[1] else None)
341 y_mins.append(mean(val[0]) if val[0] else None)
342 y_maxs.append(mean(val[2]) if val[2] else None)
343 nr_of_samples.append(len(val[1]) if val[1] else 0)
344 x_vals.append(name) # dir 2
345 y_vals.append(mean(val[4]) if val[4] else None)
346 y_mins.append(mean(val[3]) if val[3] else None)
347 y_maxs.append(mean(val[5]) if val[5] else None)
348 nr_of_samples.append(len(val[3]) if val[3] else 0)
353 for idx in range(len(x_vals)):
354 if not bool(int(idx % 2)):
355 direction = "West-East"
357 direction = "East-West"
358 hovertext = ("No. of Runs: {nr}<br>"
360 "Direction: {dir}<br>".format(test=x_vals[idx],
362 nr=nr_of_samples[idx]))
363 if isinstance(y_maxs[idx], float):
364 hovertext += "Max: {max:.2f}uSec<br>".format(max=y_maxs[idx])
365 if isinstance(y_vals[idx], float):
366 hovertext += "Mean: {avg:.2f}uSec<br>".format(avg=y_vals[idx])
367 if isinstance(y_mins[idx], float):
368 hovertext += "Min: {min:.2f}uSec".format(min=y_mins[idx])
370 if isinstance(y_maxs[idx], float) and isinstance(y_vals[idx], float):
371 array = [y_maxs[idx] - y_vals[idx], ]
374 if isinstance(y_mins[idx], float) and isinstance(y_vals[idx], float):
375 arrayminus = [y_vals[idx] - y_mins[idx], ]
377 arrayminus = [None, ]
378 traces.append(plgo.Scatter(
382 legendgroup=x_vals[idx],
383 showlegend=bool(int(idx % 2)),
389 arrayminus=arrayminus,
390 color=COLORS[int(idx / 2)]
394 color=COLORS[int(idx / 2)],
399 annotations.append(dict(
406 text="E-W" if bool(int(idx % 2)) else "W-E",
416 file_type = plot.get("output-file-type", ".html")
417 logging.info(" Writing file '{0}{1}'.".
418 format(plot["output-file"], file_type))
419 layout = deepcopy(plot["layout"])
420 if layout.get("title", None):
421 layout["title"] = "<b>Latency:</b> {0}".\
422 format(layout["title"])
423 layout["annotations"] = annotations
424 plpl = plgo.Figure(data=traces, layout=layout)
428 show_link=False, auto_open=False,
429 filename='{0}{1}'.format(plot["output-file"], file_type))
430 except PlotlyError as err:
431 logging.error(" Finished with error: {}".
432 format(str(err).replace("\n", " ")))
436 def plot_throughput_speedup_analysis_name(plot, input_data):
437 """Generate the plot(s) with algorithm:
438 plot_throughput_speedup_analysis_name
439 specified in the specification file.
441 :param plot: Plot to generate.
442 :param input_data: Data to process.
443 :type plot: pandas.Series
444 :type input_data: InputData
448 plot_title = plot.get("title", "")
449 logging.info(" Creating the data set for the {0} '{1}'.".
450 format(plot.get("type", ""), plot_title))
451 data = input_data.filter_tests_by_name(
452 plot, params=["throughput", "parent", "tags", "type"])
454 logging.error("No data.")
457 y_vals = OrderedDict()
461 if y_vals.get(test["parent"], None) is None:
462 y_vals[test["parent"]] = {"1": list(),
466 if test["type"] in ("NDRPDR",):
467 if "-pdr" in plot_title.lower():
469 elif "-ndr" in plot_title.lower():
473 if "1C" in test["tags"]:
474 y_vals[test["parent"]]["1"]. \
475 append(test["throughput"][ttype]["LOWER"])
476 elif "2C" in test["tags"]:
477 y_vals[test["parent"]]["2"]. \
478 append(test["throughput"][ttype]["LOWER"])
479 elif "4C" in test["tags"]:
480 y_vals[test["parent"]]["4"]. \
481 append(test["throughput"][ttype]["LOWER"])
482 except (KeyError, TypeError):
486 logging.warning("No data for the plot '{}'".
487 format(plot.get("title", "")))
491 for test_name, test_vals in y_vals.items():
492 for key, test_val in test_vals.items():
494 avg_val = sum(test_val) / len(test_val)
495 y_vals[test_name][key] = (avg_val, len(test_val))
496 ideal = avg_val / (int(key) * 1000000.0)
497 if test_name not in y_1c_max or ideal > y_1c_max[test_name]:
498 y_1c_max[test_name] = ideal
504 pci_limit = plot["limits"]["pci"]["pci-g3-x8"]
505 for test_name, test_vals in y_vals.items():
507 if test_vals["1"][1]:
508 name = re.sub(REGEX_NIC, "", test_name.replace('-ndrpdr', '').
509 replace('2n1l-', ''))
510 vals[name] = OrderedDict()
511 y_val_1 = test_vals["1"][0] / 1000000.0
512 y_val_2 = test_vals["2"][0] / 1000000.0 if test_vals["2"][0] \
514 y_val_4 = test_vals["4"][0] / 1000000.0 if test_vals["4"][0] \
517 vals[name]["val"] = [y_val_1, y_val_2, y_val_4]
518 vals[name]["rel"] = [1.0, None, None]
519 vals[name]["ideal"] = [y_1c_max[test_name],
520 y_1c_max[test_name] * 2,
521 y_1c_max[test_name] * 4]
522 vals[name]["diff"] = [(y_val_1 - y_1c_max[test_name]) * 100 /
524 vals[name]["count"] = [test_vals["1"][1],
529 val_max = max(vals[name]["val"])
530 except ValueError as err:
531 logging.error(repr(err))
534 y_max.append(val_max)
537 vals[name]["rel"][1] = round(y_val_2 / y_val_1, 2)
538 vals[name]["diff"][1] = \
539 (y_val_2 - vals[name]["ideal"][1]) * 100 / y_val_2
541 vals[name]["rel"][2] = round(y_val_4 / y_val_1, 2)
542 vals[name]["diff"][2] = \
543 (y_val_4 - vals[name]["ideal"][2]) * 100 / y_val_4
544 except IndexError as err:
545 logging.warning("No data for '{0}'".format(test_name))
546 logging.warning(repr(err))
549 if "x520" in test_name:
550 limit = plot["limits"]["nic"]["x520"]
551 elif "x710" in test_name:
552 limit = plot["limits"]["nic"]["x710"]
553 elif "xxv710" in test_name:
554 limit = plot["limits"]["nic"]["xxv710"]
555 elif "xl710" in test_name:
556 limit = plot["limits"]["nic"]["xl710"]
557 elif "x553" in test_name:
558 limit = plot["limits"]["nic"]["x553"]
561 if limit > nic_limit:
564 mul = 2 if "ge2p" in test_name else 1
565 if "10ge" in test_name:
566 limit = plot["limits"]["link"]["10ge"] * mul
567 elif "25ge" in test_name:
568 limit = plot["limits"]["link"]["25ge"] * mul
569 elif "40ge" in test_name:
570 limit = plot["limits"]["link"]["40ge"] * mul
571 elif "100ge" in test_name:
572 limit = plot["limits"]["link"]["100ge"] * mul
575 if limit > lnk_limit:
584 threshold = 1.1 * max(y_max) # 10%
585 except ValueError as err:
588 nic_limit /= 1000000.0
589 traces.append(plgo.Scatter(
591 y=[nic_limit, ] * len(x_vals),
592 name="NIC: {0:.2f}Mpps".format(nic_limit),
601 annotations.append(dict(
608 text="NIC: {0:.2f}Mpps".format(nic_limit),
616 y_max.append(nic_limit)
618 lnk_limit /= 1000000.0
619 if lnk_limit < threshold:
620 traces.append(plgo.Scatter(
622 y=[lnk_limit, ] * len(x_vals),
623 name="Link: {0:.2f}Mpps".format(lnk_limit),
632 annotations.append(dict(
639 text="Link: {0:.2f}Mpps".format(lnk_limit),
647 y_max.append(lnk_limit)
649 pci_limit /= 1000000.0
650 if (pci_limit < threshold and
651 (pci_limit < lnk_limit * 0.95 or lnk_limit > lnk_limit * 1.05)):
652 traces.append(plgo.Scatter(
654 y=[pci_limit, ] * len(x_vals),
655 name="PCIe: {0:.2f}Mpps".format(pci_limit),
664 annotations.append(dict(
671 text="PCIe: {0:.2f}Mpps".format(pci_limit),
679 y_max.append(pci_limit)
681 # Perfect and measured:
683 for name, val in vals.iteritems():
686 for idx in range(len(val["val"])):
688 if isinstance(val["val"][idx], float):
689 htext += "No. of Runs: {1}<br>" \
690 "Mean: {0:.2f}Mpps<br>".format(val["val"][idx],
692 if isinstance(val["diff"][idx], float):
693 htext += "Diff: {0:.0f}%<br>".format(
694 round(val["diff"][idx]))
695 if isinstance(val["rel"][idx], float):
696 htext += "Speedup: {0:.2f}".format(val["rel"][idx])
697 hovertext.append(htext)
698 traces.append(plgo.Scatter(x=x_vals,
702 mode="lines+markers",
711 hoverinfo="text+name"
713 traces.append(plgo.Scatter(x=x_vals,
715 name="{0} perfect".format(name),
723 text=["Perfect: {0:.2f}Mpps".format(y)
724 for y in val["ideal"]],
728 except (IndexError, ValueError, KeyError) as err:
729 logging.warning("No data for '{0}'".format(name))
730 logging.warning(repr(err))
734 file_type = plot.get("output-file-type", ".html")
735 logging.info(" Writing file '{0}{1}'.".
736 format(plot["output-file"], file_type))
737 layout = deepcopy(plot["layout"])
738 if layout.get("title", None):
739 layout["title"] = "<b>Speedup Multi-core:</b> {0}". \
740 format(layout["title"])
741 layout["yaxis"]["range"] = [0, int(max(y_max) * 1.1)]
742 layout["annotations"].extend(annotations)
743 plpl = plgo.Figure(data=traces, layout=layout)
747 show_link=False, auto_open=False,
748 filename='{0}{1}'.format(plot["output-file"], file_type))
749 except PlotlyError as err:
750 logging.error(" Finished with error: {}".
751 format(repr(err).replace("\n", " ")))
755 def plot_performance_box(plot, input_data):
756 """Generate the plot(s) with algorithm: plot_performance_box
757 specified in the specification file.
759 TODO: Remove when not needed.
761 :param plot: Plot to generate.
762 :param input_data: Data to process.
763 :type plot: pandas.Series
764 :type input_data: InputData
768 plot_title = plot.get("title", "")
769 logging.info(" Creating the data set for the {0} '{1}'.".
770 format(plot.get("type", ""), plot_title))
771 data = input_data.filter_data(plot)
773 logging.error("No data.")
776 # Prepare the data for the plot
782 if y_vals.get(test["parent"], None) is None:
783 y_vals[test["parent"]] = list()
784 y_tags[test["parent"]] = test.get("tags", None)
786 if test["type"] in ("NDRPDR", ):
787 if "-pdr" in plot_title.lower():
788 y_vals[test["parent"]].\
789 append(test["throughput"]["PDR"]["LOWER"])
790 elif "-ndr" in plot_title.lower():
791 y_vals[test["parent"]]. \
792 append(test["throughput"]["NDR"]["LOWER"])
795 elif test["type"] in ("SOAK", ):
796 y_vals[test["parent"]].\
797 append(test["throughput"]["LOWER"])
800 except (KeyError, TypeError):
801 y_vals[test["parent"]].append(None)
804 order = plot.get("sort", None)
806 y_sorted = OrderedDict()
807 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
810 for suite, tags in y_tags_l.items():
812 tag = tag.split(" ")[-1]
813 if tag.lower() in tags:
816 if tag.lower() not in tags:
819 y_sorted[suite] = y_vals.pop(suite)
822 except KeyError as err:
823 logging.error("Not found: {0}".format(repr(err)))
829 # Add None to the lists with missing data
831 nr_of_samples = list()
832 for val in y_sorted.values():
833 if len(val) > max_len:
835 nr_of_samples.append(len(val))
836 for key, val in y_sorted.items():
837 if len(val) < max_len:
838 val.extend([None for _ in range(max_len - len(val))])
842 df = pd.DataFrame(y_sorted)
845 for i, col in enumerate(df.columns):
846 tst_name = re.sub(REGEX_NIC, "",
847 col.lower().replace('-ndrpdr', '').
848 replace('2n1l-', ''))
849 name = "{nr}. ({samples:02d} run{plural}) {name}".\
851 samples=nr_of_samples[i],
852 plural='s' if nr_of_samples[i] > 1 else '',
856 traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
857 y=[y / 1000000 if y else None for y in df[col]],
861 val_max = max(df[col])
862 except ValueError as err:
863 logging.error(repr(err))
866 y_max.append(int(val_max / 1000000) + 2)
870 layout = deepcopy(plot["layout"])
871 if layout.get("title", None):
872 layout["title"] = "<b>Throughput:</b> {0}". \
873 format(layout["title"])
875 layout["yaxis"]["range"] = [0, max(y_max)]
876 plpl = plgo.Figure(data=traces, layout=layout)
879 logging.info(" Writing file '{0}{1}'.".
880 format(plot["output-file"], plot["output-file-type"]))
881 ploff.plot(plpl, show_link=False, auto_open=False,
882 filename='{0}{1}'.format(plot["output-file"],
883 plot["output-file-type"]))
884 except PlotlyError as err:
885 logging.error(" Finished with error: {}".
886 format(repr(err).replace("\n", " ")))
890 def plot_soak_bars(plot, input_data):
891 """Generate the plot(s) with algorithm: plot_soak_bars
892 specified in the specification file.
894 :param plot: Plot to generate.
895 :param input_data: Data to process.
896 :type plot: pandas.Series
897 :type input_data: InputData
901 plot_title = plot.get("title", "")
902 logging.info(" Creating the data set for the {0} '{1}'.".
903 format(plot.get("type", ""), plot_title))
904 data = input_data.filter_data(plot)
906 logging.error("No data.")
909 # Prepare the data for the plot
915 if y_vals.get(test["parent"], None) is None:
916 y_tags[test["parent"]] = test.get("tags", None)
918 if test["type"] in ("SOAK", ):
919 y_vals[test["parent"]] = test["throughput"]
922 except (KeyError, TypeError):
923 y_vals[test["parent"]] = dict()
926 order = plot.get("sort", None)
928 y_sorted = OrderedDict()
929 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
932 for suite, tags in y_tags_l.items():
934 tag = tag.split(" ")[-1]
935 if tag.lower() in tags:
938 if tag.lower() not in tags:
941 y_sorted[suite] = y_vals.pop(suite)
944 except KeyError as err:
945 logging.error("Not found: {0}".format(repr(err)))
954 for test_name, test_data in y_sorted.items():
956 name = "{nr}. {name}".\
957 format(nr=idx, name=test_name.lower().replace('-soak', ''))
959 name_lst = name.split('-')
962 for segment in name_lst:
963 if (len(name) + len(segment) + 1) > 50 and split_name:
966 name += segment + '-'
969 y_val = test_data.get("LOWER", None)
975 time = "No Information"
976 result = "No Information"
977 hovertext = ("{name}<br>"
978 "Packet Throughput: {val:.2f}Mpps<br>"
979 "Final Duration: {time}<br>"
980 "Result: {result}".format(name=name,
984 traces.append(plgo.Bar(x=[str(idx) + '.', ],
991 layout = deepcopy(plot["layout"])
992 if layout.get("title", None):
993 layout["title"] = "<b>Packet Throughput:</b> {0}". \
994 format(layout["title"])
996 layout["yaxis"]["range"] = [0, y_max + 1]
997 plpl = plgo.Figure(data=traces, layout=layout)
999 logging.info(" Writing file '{0}{1}'.".
1000 format(plot["output-file"], plot["output-file-type"]))
1001 ploff.plot(plpl, show_link=False, auto_open=False,
1002 filename='{0}{1}'.format(plot["output-file"],
1003 plot["output-file-type"]))
1004 except PlotlyError as err:
1005 logging.error(" Finished with error: {}".
1006 format(repr(err).replace("\n", " ")))
1010 def plot_soak_boxes(plot, input_data):
1011 """Generate the plot(s) with algorithm: plot_soak_boxes
1012 specified in the specification file.
1014 :param plot: Plot to generate.
1015 :param input_data: Data to process.
1016 :type plot: pandas.Series
1017 :type input_data: InputData
1020 # Transform the data
1021 plot_title = plot.get("title", "")
1022 logging.info(" Creating the data set for the {0} '{1}'.".
1023 format(plot.get("type", ""), plot_title))
1024 data = input_data.filter_data(plot)
1026 logging.error("No data.")
1029 # Prepare the data for the plot
1035 if y_vals.get(test["parent"], None) is None:
1036 y_tags[test["parent"]] = test.get("tags", None)
1038 if test["type"] in ("SOAK", ):
1039 y_vals[test["parent"]] = test["throughput"]
1042 except (KeyError, TypeError):
1043 y_vals[test["parent"]] = dict()
1046 order = plot.get("sort", None)
1047 if order and y_tags:
1048 y_sorted = OrderedDict()
1049 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
1052 for suite, tags in y_tags_l.items():
1054 tag = tag.split(" ")[-1]
1055 if tag.lower() in tags:
1058 if tag.lower() not in tags:
1061 y_sorted[suite] = y_vals.pop(suite)
1063 logging.debug(suite)
1064 except KeyError as err:
1065 logging.error("Not found: {0}".format(repr(err)))
1074 for test_name, test_data in y_sorted.items():
1076 name = "{nr}. {name}".\
1077 format(nr=idx, name=test_name.lower().replace('-soak', '').
1078 replace('2n1l-', ''))
1080 name_lst = name.split('-')
1083 for segment in name_lst:
1084 if (len(name) + len(segment) + 1) > 55 and split_name:
1087 name += segment + '-'
1090 y_val = test_data.get("UPPER", None)
1096 y_base = test_data.get("LOWER", None)
1100 hovertext = ("Upper bound: {upper:.2f}<br>"
1101 "Lower bound: {lower:.2f}".format(upper=y_val,
1103 traces.append(plgo.Bar(x=[str(idx) + '.', ],
1104 # +0.05 to see the value in case lower == upper
1105 y=[y_val - y_base + 0.05, ],
1112 layout = deepcopy(plot["layout"])
1113 if layout.get("title", None):
1114 layout["title"] = "<b>Throughput:</b> {0}". \
1115 format(layout["title"])
1117 layout["yaxis"]["range"] = [0, y_max + 1]
1118 plpl = plgo.Figure(data=traces, layout=layout)
1120 logging.info(" Writing file '{0}{1}'.".
1121 format(plot["output-file"], plot["output-file-type"]))
1122 ploff.plot(plpl, show_link=False, auto_open=False,
1123 filename='{0}{1}'.format(plot["output-file"],
1124 plot["output-file-type"]))
1125 except PlotlyError as err:
1126 logging.error(" Finished with error: {}".
1127 format(repr(err).replace("\n", " ")))
1131 def plot_latency_error_bars(plot, input_data):
1132 """Generate the plot(s) with algorithm: plot_latency_error_bars
1133 specified in the specification file.
1135 TODO: Remove when not needed.
1137 :param plot: Plot to generate.
1138 :param input_data: Data to process.
1139 :type plot: pandas.Series
1140 :type input_data: InputData
1143 # Transform the data
1144 plot_title = plot.get("title", "")
1145 logging.info(" Creating the data set for the {0} '{1}'.".
1146 format(plot.get("type", ""), plot_title))
1147 data = input_data.filter_data(plot)
1149 logging.error("No data.")
1152 # Prepare the data for the plot
1159 logging.debug("test['latency']: {0}\n".
1160 format(test["latency"]))
1161 except ValueError as err:
1162 logging.warning(repr(err))
1163 if y_tmp_vals.get(test["parent"], None) is None:
1164 y_tmp_vals[test["parent"]] = [
1165 list(), # direction1, min
1166 list(), # direction1, avg
1167 list(), # direction1, max
1168 list(), # direction2, min
1169 list(), # direction2, avg
1170 list() # direction2, max
1172 y_tags[test["parent"]] = test.get("tags", None)
1174 if test["type"] in ("NDRPDR", ):
1175 if "-pdr" in plot_title.lower():
1177 elif "-ndr" in plot_title.lower():
1180 logging.warning("Invalid test type: {0}".
1181 format(test["type"]))
1183 y_tmp_vals[test["parent"]][0].append(
1184 test["latency"][ttype]["direction1"]["min"])
1185 y_tmp_vals[test["parent"]][1].append(
1186 test["latency"][ttype]["direction1"]["avg"])
1187 y_tmp_vals[test["parent"]][2].append(
1188 test["latency"][ttype]["direction1"]["max"])
1189 y_tmp_vals[test["parent"]][3].append(
1190 test["latency"][ttype]["direction2"]["min"])
1191 y_tmp_vals[test["parent"]][4].append(
1192 test["latency"][ttype]["direction2"]["avg"])
1193 y_tmp_vals[test["parent"]][5].append(
1194 test["latency"][ttype]["direction2"]["max"])
1196 logging.warning("Invalid test type: {0}".
1197 format(test["type"]))
1199 except (KeyError, TypeError) as err:
1200 logging.warning(repr(err))
1201 logging.debug("y_tmp_vals: {0}\n".format(y_tmp_vals))
1204 order = plot.get("sort", None)
1205 if order and y_tags:
1206 y_sorted = OrderedDict()
1207 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
1210 for suite, tags in y_tags_l.items():
1212 tag = tag.split(" ")[-1]
1213 if tag.lower() in tags:
1216 if tag.lower() not in tags:
1219 y_sorted[suite] = y_tmp_vals.pop(suite)
1221 logging.debug(suite)
1222 except KeyError as err:
1223 logging.error("Not found: {0}".format(repr(err)))
1227 y_sorted = y_tmp_vals
1229 logging.debug("y_sorted: {0}\n".format(y_sorted))
1234 nr_of_samples = list()
1235 for key, val in y_sorted.items():
1236 name = re.sub(REGEX_NIC, "", key.replace('-ndrpdr', '').
1237 replace('2n1l-', ''))
1238 x_vals.append(name) # dir 1
1239 y_vals.append(mean(val[1]) if val[1] else None)
1240 y_mins.append(mean(val[0]) if val[0] else None)
1241 y_maxs.append(mean(val[2]) if val[2] else None)
1242 nr_of_samples.append(len(val[1]) if val[1] else 0)
1243 x_vals.append(name) # dir 2
1244 y_vals.append(mean(val[4]) if val[4] else None)
1245 y_mins.append(mean(val[3]) if val[3] else None)
1246 y_maxs.append(mean(val[5]) if val[5] else None)
1247 nr_of_samples.append(len(val[3]) if val[3] else 0)
1249 logging.debug("x_vals :{0}\n".format(x_vals))
1250 logging.debug("y_vals :{0}\n".format(y_vals))
1251 logging.debug("y_mins :{0}\n".format(y_mins))
1252 logging.debug("y_maxs :{0}\n".format(y_maxs))
1253 logging.debug("nr_of_samples :{0}\n".format(nr_of_samples))
1255 annotations = list()
1257 for idx in range(len(x_vals)):
1258 if not bool(int(idx % 2)):
1259 direction = "West-East"
1261 direction = "East-West"
1262 hovertext = ("No. of Runs: {nr}<br>"
1264 "Direction: {dir}<br>".format(test=x_vals[idx],
1266 nr=nr_of_samples[idx]))
1267 if isinstance(y_maxs[idx], float):
1268 hovertext += "Max: {max:.2f}uSec<br>".format(max=y_maxs[idx])
1269 if isinstance(y_vals[idx], float):
1270 hovertext += "Mean: {avg:.2f}uSec<br>".format(avg=y_vals[idx])
1271 if isinstance(y_mins[idx], float):
1272 hovertext += "Min: {min:.2f}uSec".format(min=y_mins[idx])
1274 if isinstance(y_maxs[idx], float) and isinstance(y_vals[idx], float):
1275 array = [y_maxs[idx] - y_vals[idx], ]
1278 if isinstance(y_mins[idx], float) and isinstance(y_vals[idx], float):
1279 arrayminus = [y_vals[idx] - y_mins[idx], ]
1281 arrayminus = [None, ]
1282 logging.debug("y_vals[{1}] :{0}\n".format(y_vals[idx], idx))
1283 logging.debug("array :{0}\n".format(array))
1284 logging.debug("arrayminus :{0}\n".format(arrayminus))
1285 traces.append(plgo.Scatter(
1289 legendgroup=x_vals[idx],
1290 showlegend=bool(int(idx % 2)),
1296 arrayminus=arrayminus,
1297 color=COLORS[int(idx / 2)]
1301 color=COLORS[int(idx / 2)],
1306 annotations.append(dict(
1313 text="E-W" if bool(int(idx % 2)) else "W-E",
1323 logging.info(" Writing file '{0}{1}'.".
1324 format(plot["output-file"], plot["output-file-type"]))
1325 layout = deepcopy(plot["layout"])
1326 if layout.get("title", None):
1327 layout["title"] = "<b>Latency:</b> {0}".\
1328 format(layout["title"])
1329 layout["annotations"] = annotations
1330 plpl = plgo.Figure(data=traces, layout=layout)
1334 show_link=False, auto_open=False,
1335 filename='{0}{1}'.format(plot["output-file"],
1336 plot["output-file-type"]))
1337 except PlotlyError as err:
1338 logging.error(" Finished with error: {}".
1339 format(str(err).replace("\n", " ")))
1343 def plot_throughput_speedup_analysis(plot, input_data):
1344 """Generate the plot(s) with algorithm:
1345 plot_throughput_speedup_analysis
1346 specified in the specification file.
1348 TODO: Remove when not needed.
1350 :param plot: Plot to generate.
1351 :param input_data: Data to process.
1352 :type plot: pandas.Series
1353 :type input_data: InputData
1356 # Transform the data
1357 plot_title = plot.get("title", "")
1358 logging.info(" Creating the data set for the {0} '{1}'.".
1359 format(plot.get("type", ""), plot_title))
1360 data = input_data.filter_data(plot)
1362 logging.error("No data.")
1370 if y_vals.get(test["parent"], None) is None:
1371 y_vals[test["parent"]] = {"1": list(),
1374 y_tags[test["parent"]] = test.get("tags", None)
1376 if test["type"] in ("NDRPDR",):
1377 if "-pdr" in plot_title.lower():
1379 elif "-ndr" in plot_title.lower():
1383 if "1C" in test["tags"]:
1384 y_vals[test["parent"]]["1"]. \
1385 append(test["throughput"][ttype]["LOWER"])
1386 elif "2C" in test["tags"]:
1387 y_vals[test["parent"]]["2"]. \
1388 append(test["throughput"][ttype]["LOWER"])
1389 elif "4C" in test["tags"]:
1390 y_vals[test["parent"]]["4"]. \
1391 append(test["throughput"][ttype]["LOWER"])
1392 except (KeyError, TypeError):
1396 logging.warning("No data for the plot '{}'".
1397 format(plot.get("title", "")))
1401 for test_name, test_vals in y_vals.items():
1402 for key, test_val in test_vals.items():
1404 avg_val = sum(test_val) / len(test_val)
1405 y_vals[test_name][key] = (avg_val, len(test_val))
1406 ideal = avg_val / (int(key) * 1000000.0)
1407 if test_name not in y_1c_max or ideal > y_1c_max[test_name]:
1408 y_1c_max[test_name] = ideal
1414 pci_limit = plot["limits"]["pci"]["pci-g3-x8"]
1415 for test_name, test_vals in y_vals.items():
1417 if test_vals["1"][1]:
1418 name = re.sub(REGEX_NIC, "", test_name.replace('-ndrpdr', '').
1419 replace('2n1l-', ''))
1421 y_val_1 = test_vals["1"][0] / 1000000.0
1422 y_val_2 = test_vals["2"][0] / 1000000.0 if test_vals["2"][0] \
1424 y_val_4 = test_vals["4"][0] / 1000000.0 if test_vals["4"][0] \
1427 vals[name]["val"] = [y_val_1, y_val_2, y_val_4]
1428 vals[name]["rel"] = [1.0, None, None]
1429 vals[name]["ideal"] = [y_1c_max[test_name],
1430 y_1c_max[test_name] * 2,
1431 y_1c_max[test_name] * 4]
1432 vals[name]["diff"] = [(y_val_1 - y_1c_max[test_name]) * 100 /
1433 y_val_1, None, None]
1434 vals[name]["count"] = [test_vals["1"][1],
1439 # val_max = max(max(vals[name]["val"], vals[name]["ideal"]))
1440 val_max = max(vals[name]["val"])
1441 except ValueError as err:
1445 # y_max.append(int((val_max / 10) + 1) * 10)
1446 y_max.append(val_max)
1449 vals[name]["rel"][1] = round(y_val_2 / y_val_1, 2)
1450 vals[name]["diff"][1] = \
1451 (y_val_2 - vals[name]["ideal"][1]) * 100 / y_val_2
1453 vals[name]["rel"][2] = round(y_val_4 / y_val_1, 2)
1454 vals[name]["diff"][2] = \
1455 (y_val_4 - vals[name]["ideal"][2]) * 100 / y_val_4
1456 except IndexError as err:
1457 logging.warning("No data for '{0}'".format(test_name))
1458 logging.warning(repr(err))
1461 if "x520" in test_name:
1462 limit = plot["limits"]["nic"]["x520"]
1463 elif "x710" in test_name:
1464 limit = plot["limits"]["nic"]["x710"]
1465 elif "xxv710" in test_name:
1466 limit = plot["limits"]["nic"]["xxv710"]
1467 elif "xl710" in test_name:
1468 limit = plot["limits"]["nic"]["xl710"]
1469 elif "x553" in test_name:
1470 limit = plot["limits"]["nic"]["x553"]
1473 if limit > nic_limit:
1476 mul = 2 if "ge2p" in test_name else 1
1477 if "10ge" in test_name:
1478 limit = plot["limits"]["link"]["10ge"] * mul
1479 elif "25ge" in test_name:
1480 limit = plot["limits"]["link"]["25ge"] * mul
1481 elif "40ge" in test_name:
1482 limit = plot["limits"]["link"]["40ge"] * mul
1483 elif "100ge" in test_name:
1484 limit = plot["limits"]["link"]["100ge"] * mul
1487 if limit > lnk_limit:
1491 order = plot.get("sort", None)
1492 if order and y_tags:
1493 y_sorted = OrderedDict()
1494 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
1496 for test, tags in y_tags_l.items():
1497 if tag.lower() in tags:
1498 name = re.sub(REGEX_NIC, "",
1499 test.replace('-ndrpdr', '').
1500 replace('2n1l-', ''))
1502 y_sorted[name] = vals.pop(name)
1504 except KeyError as err:
1505 logging.error("Not found: {0}".format(err))
1512 annotations = list()
1517 threshold = 1.1 * max(y_max) # 10%
1518 except ValueError as err:
1521 nic_limit /= 1000000.0
1522 # if nic_limit < threshold:
1523 traces.append(plgo.Scatter(
1525 y=[nic_limit, ] * len(x_vals),
1526 name="NIC: {0:.2f}Mpps".format(nic_limit),
1535 annotations.append(dict(
1542 text="NIC: {0:.2f}Mpps".format(nic_limit),
1550 # y_max.append(int((nic_limit / 10) + 1) * 10)
1551 y_max.append(nic_limit)
1553 lnk_limit /= 1000000.0
1554 if lnk_limit < threshold:
1555 traces.append(plgo.Scatter(
1557 y=[lnk_limit, ] * len(x_vals),
1558 name="Link: {0:.2f}Mpps".format(lnk_limit),
1567 annotations.append(dict(
1574 text="Link: {0:.2f}Mpps".format(lnk_limit),
1582 # y_max.append(int((lnk_limit / 10) + 1) * 10)
1583 y_max.append(lnk_limit)
1585 pci_limit /= 1000000.0
1586 if (pci_limit < threshold and
1587 (pci_limit < lnk_limit * 0.95 or lnk_limit > lnk_limit * 1.05)):
1588 traces.append(plgo.Scatter(
1590 y=[pci_limit, ] * len(x_vals),
1591 name="PCIe: {0:.2f}Mpps".format(pci_limit),
1600 annotations.append(dict(
1607 text="PCIe: {0:.2f}Mpps".format(pci_limit),
1615 # y_max.append(int((pci_limit / 10) + 1) * 10)
1616 y_max.append(pci_limit)
1618 # Perfect and measured:
1620 for name, val in y_sorted.iteritems():
1623 for idx in range(len(val["val"])):
1625 if isinstance(val["val"][idx], float):
1626 htext += "No. of Runs: {1}<br>" \
1627 "Mean: {0:.2f}Mpps<br>".format(val["val"][idx],
1629 if isinstance(val["diff"][idx], float):
1630 htext += "Diff: {0:.0f}%<br>".format(round(val["diff"][idx]))
1631 if isinstance(val["rel"][idx], float):
1632 htext += "Speedup: {0:.2f}".format(val["rel"][idx])
1633 hovertext.append(htext)
1634 traces.append(plgo.Scatter(x=x_vals,
1638 mode="lines+markers",
1647 hoverinfo="text+name"
1649 traces.append(plgo.Scatter(x=x_vals,
1651 name="{0} perfect".format(name),
1659 text=["Perfect: {0:.2f}Mpps".format(y)
1660 for y in val["ideal"]],
1664 except (IndexError, ValueError, KeyError) as err:
1665 logging.warning("No data for '{0}'".format(name))
1666 logging.warning(repr(err))
1670 logging.info(" Writing file '{0}{1}'.".
1671 format(plot["output-file"], plot["output-file-type"]))
1672 layout = deepcopy(plot["layout"])
1673 if layout.get("title", None):
1674 layout["title"] = "<b>Speedup Multi-core:</b> {0}". \
1675 format(layout["title"])
1676 # layout["yaxis"]["range"] = [0, int((max(y_max) / 10) + 1) * 10]
1677 layout["yaxis"]["range"] = [0, int(max(y_max) * 1.1)]
1678 layout["annotations"].extend(annotations)
1679 plpl = plgo.Figure(data=traces, layout=layout)
1683 show_link=False, auto_open=False,
1684 filename='{0}{1}'.format(plot["output-file"],
1685 plot["output-file-type"]))
1686 except PlotlyError as err:
1687 logging.error(" Finished with error: {}".
1688 format(str(err).replace("\n", " ")))
1692 def plot_http_server_performance_box(plot, input_data):
1693 """Generate the plot(s) with algorithm: plot_http_server_performance_box
1694 specified in the specification file.
1696 :param plot: Plot to generate.
1697 :param input_data: Data to process.
1698 :type plot: pandas.Series
1699 :type input_data: InputData
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)
1707 logging.error("No data.")
1710 # Prepare the data for the plot
1715 if y_vals.get(test["name"], None) is None:
1716 y_vals[test["name"]] = list()
1718 y_vals[test["name"]].append(test["result"])
1719 except (KeyError, TypeError):
1720 y_vals[test["name"]].append(None)
1722 # Add None to the lists with missing data
1724 nr_of_samples = list()
1725 for val in y_vals.values():
1726 if len(val) > max_len:
1728 nr_of_samples.append(len(val))
1729 for key, val in y_vals.items():
1730 if len(val) < max_len:
1731 val.extend([None for _ in range(max_len - len(val))])
1735 df = pd.DataFrame(y_vals)
1737 for i, col in enumerate(df.columns):
1738 name = "{nr}. ({samples:02d} run{plural}) {name}".\
1740 samples=nr_of_samples[i],
1741 plural='s' if nr_of_samples[i] > 1 else '',
1742 name=col.lower().replace('-ndrpdr', ''))
1744 name_lst = name.split('-')
1747 for segment in name_lst:
1748 if (len(name) + len(segment) + 1) > 50 and split_name:
1751 name += segment + '-'
1754 traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
1760 plpl = plgo.Figure(data=traces, layout=plot["layout"])
1763 logging.info(" Writing file '{0}{1}'.".
1764 format(plot["output-file"], plot["output-file-type"]))
1765 ploff.plot(plpl, show_link=False, auto_open=False,
1766 filename='{0}{1}'.format(plot["output-file"],
1767 plot["output-file-type"]))
1768 except PlotlyError as err:
1769 logging.error(" Finished with error: {}".
1770 format(str(err).replace("\n", " ")))
1774 def plot_service_density_heatmap(plot, input_data):
1775 """Generate the plot(s) with algorithm: plot_service_density_heatmap
1776 specified in the specification file.
1778 :param plot: Plot to generate.
1779 :param input_data: Data to process.
1780 :type plot: pandas.Series
1781 :type input_data: InputData
1784 REGEX_CN = re.compile(r'^(\d*)R(\d*)C$')
1785 REGEX_TEST_NAME = re.compile(r'^.*-(\d+ch|\d+pl)-'
1787 r'(\d+vm\d+t|\d+dcr\d+t).*$')
1793 # Transform the data
1794 logging.info(" Creating the data set for the {0} '{1}'.".
1795 format(plot.get("type", ""), plot.get("title", "")))
1796 data = input_data.filter_data(plot, continue_on_error=True)
1797 if data is None or data.empty:
1798 logging.error("No data.")
1804 for tag in test['tags']:
1805 groups = re.search(REGEX_CN, tag)
1807 c = str(groups.group(1))
1808 n = str(groups.group(2))
1812 groups = re.search(REGEX_TEST_NAME, test["name"])
1813 if groups and len(groups.groups()) == 3:
1814 hover_name = "{chain}-{vhost}-{vm}".format(
1815 chain=str(groups.group(1)),
1816 vhost=str(groups.group(2)),
1817 vm=str(groups.group(3)))
1820 if vals.get(c, None) is None:
1822 if vals[c].get(n, None) is None:
1823 vals[c][n] = dict(name=hover_name,
1829 if plot["include-tests"] == "MRR":
1830 result = test["result"]["receive-rate"].avg
1831 elif plot["include-tests"] == "PDR":
1832 result = test["throughput"]["PDR"]["LOWER"]
1833 elif plot["include-tests"] == "NDR":
1834 result = test["throughput"]["NDR"]["LOWER"]
1841 vals[c][n]["vals"].append(result)
1844 logging.error("No data.")
1847 for key_c in vals.keys():
1848 txt_chains.append(key_c)
1849 for key_n in vals[key_c].keys():
1850 txt_nodes.append(key_n)
1851 if vals[key_c][key_n]["vals"]:
1852 vals[key_c][key_n]["nr"] = len(vals[key_c][key_n]["vals"])
1853 vals[key_c][key_n]["mean"] = \
1854 round(mean(vals[key_c][key_n]["vals"]) / 1000000, 1)
1855 vals[key_c][key_n]["stdev"] = \
1856 round(stdev(vals[key_c][key_n]["vals"]) / 1000000, 1)
1857 txt_nodes = list(set(txt_nodes))
1859 txt_chains = sorted(txt_chains, key=lambda chain: int(chain))
1860 txt_nodes = sorted(txt_nodes, key=lambda node: int(node))
1862 chains = [i + 1 for i in range(len(txt_chains))]
1863 nodes = [i + 1 for i in range(len(txt_nodes))]
1865 data = [list() for _ in range(len(chains))]
1869 val = vals[txt_chains[c - 1]][txt_nodes[n - 1]]["mean"]
1870 except (KeyError, IndexError):
1872 data[c - 1].append(val)
1875 my_green = [[0.0, 'rgb(235, 249, 242)'],
1876 [1.0, 'rgb(45, 134, 89)']]
1878 my_blue = [[0.0, 'rgb(236, 242, 248)'],
1879 [1.0, 'rgb(57, 115, 172)']]
1881 my_grey = [[0.0, 'rgb(230, 230, 230)'],
1882 [1.0, 'rgb(102, 102, 102)']]
1885 annotations = list()
1887 text = ("Test: {name}<br>"
1892 for c in range(len(txt_chains)):
1894 for n in range(len(txt_nodes)):
1895 if data[c][n] is not None:
1896 annotations.append(dict(
1903 text=str(data[c][n]),
1910 hover_line.append(text.format(
1911 name=vals[txt_chains[c]][txt_nodes[n]]["name"],
1912 nr=vals[txt_chains[c]][txt_nodes[n]]["nr"],
1914 stdev=vals[txt_chains[c]][txt_nodes[n]]["stdev"]))
1915 hovertext.append(hover_line)
1918 plgo.Heatmap(x=nodes,
1922 title=plot.get("z-axis", ""),
1936 colorscale=my_green,
1941 for idx, item in enumerate(txt_nodes):
1943 annotations.append(dict(
1957 for idx, item in enumerate(txt_chains):
1959 annotations.append(dict(
1974 annotations.append(dict(
1981 text=plot.get("x-axis", ""),
1989 annotations.append(dict(
1996 text=plot.get("y-axis", ""),
2004 updatemenus = list([
2013 args=[{"colorscale": [my_green, ], "reversescale": False}],
2018 args=[{"colorscale": [my_blue, ], "reversescale": False}],
2023 args=[{"colorscale": [my_grey, ], "reversescale": False}],
2032 layout = deepcopy(plot["layout"])
2033 except KeyError as err:
2034 logging.error("Finished with error: No layout defined")
2035 logging.error(repr(err))
2038 layout["annotations"] = annotations
2039 layout['updatemenus'] = updatemenus
2043 plpl = plgo.Figure(data=traces, layout=layout)
2046 logging.info(" Writing file '{0}{1}'.".
2047 format(plot["output-file"], plot["output-file-type"]))
2048 ploff.plot(plpl, show_link=False, auto_open=False,
2049 filename='{0}{1}'.format(plot["output-file"],
2050 plot["output-file-type"]))
2051 except PlotlyError as err:
2052 logging.error(" Finished with error: {}".
2053 format(str(err).replace("\n", " ")))
2057 def plot_service_density_heatmap_compare(plot, input_data):
2058 """Generate the plot(s) with algorithm: plot_service_density_heatmap_compare
2059 specified in the specification file.
2061 :param plot: Plot to generate.
2062 :param input_data: Data to process.
2063 :type plot: pandas.Series
2064 :type input_data: InputData
2067 REGEX_CN = re.compile(r'^(\d*)R(\d*)C$')
2068 REGEX_TEST_NAME = re.compile(r'^.*-(\d+ch|\d+pl)-'
2070 r'(\d+vm\d+t|\d+dcr\d+t).*$')
2071 REGEX_THREADS = re.compile(r'^(\d+)(VM|DCR)(\d+)T$')
2077 # Transform the data
2078 logging.info(" Creating the data set for the {0} '{1}'.".
2079 format(plot.get("type", ""), plot.get("title", "")))
2080 data = input_data.filter_data(plot, continue_on_error=True)
2081 if data is None or data.empty:
2082 logging.error("No data.")
2088 for tag in test['tags']:
2089 groups = re.search(REGEX_CN, tag)
2091 c = str(groups.group(1))
2092 n = str(groups.group(2))
2096 groups = re.search(REGEX_TEST_NAME, test["name"])
2097 if groups and len(groups.groups()) == 3:
2098 hover_name = "{chain}-{vhost}-{vm}".format(
2099 chain=str(groups.group(1)),
2100 vhost=str(groups.group(2)),
2101 vm=str(groups.group(3)))
2104 if vals.get(c, None) is None:
2106 if vals[c].get(n, None) is None:
2107 vals[c][n] = dict(name=hover_name,
2117 if plot["include-tests"] == "MRR":
2118 result = test["result"]["receive-rate"].avg
2119 elif plot["include-tests"] == "PDR":
2120 result = test["throughput"]["PDR"]["LOWER"]
2121 elif plot["include-tests"] == "NDR":
2122 result = test["throughput"]["NDR"]["LOWER"]
2129 for tag in test['tags']:
2130 groups = re.search(REGEX_THREADS, tag)
2131 if groups and len(groups.groups()) == 3:
2132 if str(groups.group(3)) == \
2133 plot["reference"]["include"]:
2134 vals[c][n]["vals_r"].append(result)
2135 elif str(groups.group(3)) == \
2136 plot["compare"]["include"]:
2137 vals[c][n]["vals_c"].append(result)
2140 logging.error("No data.")
2143 for key_c in vals.keys():
2144 txt_chains.append(key_c)
2145 for key_n in vals[key_c].keys():
2146 txt_nodes.append(key_n)
2147 if vals[key_c][key_n]["vals_r"]:
2148 vals[key_c][key_n]["nr_r"] = len(vals[key_c][key_n]["vals_r"])
2149 vals[key_c][key_n]["mean_r"] = \
2150 mean(vals[key_c][key_n]["vals_r"])
2151 vals[key_c][key_n]["stdev_r"] = \
2152 round(stdev(vals[key_c][key_n]["vals_r"]) / 1000000, 1)
2153 if vals[key_c][key_n]["vals_c"]:
2154 vals[key_c][key_n]["nr_c"] = len(vals[key_c][key_n]["vals_c"])
2155 vals[key_c][key_n]["mean_c"] = \
2156 mean(vals[key_c][key_n]["vals_c"])
2157 vals[key_c][key_n]["stdev_c"] = \
2158 round(stdev(vals[key_c][key_n]["vals_c"]) / 1000000, 1)
2160 txt_nodes = list(set(txt_nodes))
2162 txt_chains = sorted(txt_chains, key=lambda chain: int(chain))
2163 txt_nodes = sorted(txt_nodes, key=lambda node: int(node))
2165 chains = [i + 1 for i in range(len(txt_chains))]
2166 nodes = [i + 1 for i in range(len(txt_nodes))]
2168 data_r = [list() for _ in range(len(chains))]
2169 data_c = [list() for _ in range(len(chains))]
2170 diff = [list() for _ in range(len(chains))]
2174 val_r = vals[txt_chains[c - 1]][txt_nodes[n - 1]]["mean_r"]
2175 except (KeyError, IndexError):
2178 val_c = vals[txt_chains[c - 1]][txt_nodes[n - 1]]["mean_c"]
2179 except (KeyError, IndexError):
2181 if val_c is not None and val_r:
2182 val_d = (val_c - val_r) * 100 / val_r
2186 if val_r is not None:
2187 val_r = round(val_r / 1000000, 1)
2188 data_r[c - 1].append(val_r)
2189 if val_c is not None:
2190 val_c = round(val_c / 1000000, 1)
2191 data_c[c - 1].append(val_c)
2192 if val_d is not None:
2193 val_d = int(round(val_d, 0))
2194 diff[c - 1].append(val_d)
2197 my_green = [[0.0, 'rgb(235, 249, 242)'],
2198 [1.0, 'rgb(45, 134, 89)']]
2200 my_blue = [[0.0, 'rgb(236, 242, 248)'],
2201 [1.0, 'rgb(57, 115, 172)']]
2203 my_grey = [[0.0, 'rgb(230, 230, 230)'],
2204 [1.0, 'rgb(102, 102, 102)']]
2208 annotations = list()
2209 annotations_r = list()
2210 annotations_c = list()
2211 annotations_diff = list()
2213 text = ("Test: {name}"
2214 "<br>{title_r}: {text_r}"
2215 "<br>{title_c}: {text_c}{text_diff}")
2216 text_r = "Thput: {val_r}; StDev: {stdev_r}; Runs: {nr_r}"
2217 text_c = "Thput: {val_c}; StDev: {stdev_c}; Runs: {nr_c}"
2218 text_diff = "<br>Relative Difference {title_c} vs. {title_r}: {diff}%"
2220 for c in range(len(txt_chains)):
2222 for n in range(len(txt_nodes)):
2238 point_text_r = "Not present"
2239 point_text_c = "Not present"
2240 point_text_diff = ""
2242 point_r = data_r[c][n]
2243 if point_r is not None:
2244 point_text_r = text_r.format(
2246 stdev_r=vals[txt_chains[c]][txt_nodes[n]]["stdev_r"],
2247 nr_r=vals[txt_chains[c]][txt_nodes[n]]["nr_r"])
2250 point["text"] = "" if point_r is None else point_r
2251 annotations_r.append(deepcopy(point))
2254 point_c = data_c[c][n]
2255 if point_c is not None:
2256 point_text_c = text_c.format(
2258 stdev_c=vals[txt_chains[c]][txt_nodes[n]]["stdev_c"],
2259 nr_c=vals[txt_chains[c]][txt_nodes[n]]["nr_c"])
2262 point["text"] = "" if point_c is None else point_c
2263 annotations_c.append(deepcopy(point))
2266 point_d = diff[c][n]
2267 if point_d is not None:
2268 point_text_diff = text_diff.format(
2269 title_r=plot["reference"]["name"],
2270 title_c=plot["compare"]["name"],
2274 point["text"] = "" if point_d is None else point_d
2275 annotations_diff.append(deepcopy(point))
2278 name = vals[txt_chains[c]][txt_nodes[n]]["name"]
2282 hover_line.append(text.format(
2284 title_r=plot["reference"]["name"],
2285 text_r=point_text_r,
2286 title_c=plot["compare"]["name"],
2287 text_c=point_text_c,
2288 text_diff=point_text_diff
2291 hovertext.append(hover_line)
2294 plgo.Heatmap(x=nodes,
2299 title=plot.get("z-axis", ""),
2313 colorscale=my_green,
2317 plgo.Heatmap(x=nodes,
2322 title=plot.get("z-axis", ""),
2340 plgo.Heatmap(x=nodes,
2346 title="Relative Difference {name_c} vs. {name_r} [%]".
2347 format(name_c=plot["compare"]["name"],
2348 name_r=plot["reference"]["name"]),
2368 for idx, item in enumerate(txt_nodes):
2370 annotations.append(dict(
2384 for idx, item in enumerate(txt_chains):
2386 annotations.append(dict(
2401 annotations.append(dict(
2408 text=plot.get("x-axis", ""),
2416 annotations.append(dict(
2423 text=plot.get("y-axis", ""),
2431 updatemenus = list([
2441 label=plot["reference"]["name"],
2445 "visible": [True, False, False]
2448 "colorscale": [my_green, ],
2449 "reversescale": False,
2450 "annotations": annotations + annotations_r,
2455 label=plot["compare"]["name"],
2459 "visible": [False, True, False]
2462 "colorscale": [my_blue, ],
2463 "reversescale": False,
2464 "annotations": annotations + annotations_c,
2473 "visible": [False, False, True]
2476 "colorscale": [my_grey, ],
2477 "reversescale": False,
2478 "annotations": annotations + annotations_diff,
2487 layout = deepcopy(plot["layout"])
2488 except KeyError as err:
2489 logging.error("Finished with error: No layout defined")
2490 logging.error(repr(err))
2493 layout["annotations"] = annotations + annotations_r
2494 layout['updatemenus'] = updatemenus
2498 plpl = plgo.Figure(data=traces, layout=layout)
2501 logging.info(" Writing file '{0}{1}'.".
2502 format(plot["output-file"], plot["output-file-type"]))
2503 ploff.plot(plpl, show_link=False, auto_open=False,
2504 filename='{0}{1}'.format(plot["output-file"],
2505 plot["output-file-type"]))
2506 except PlotlyError as err:
2507 logging.error(" Finished with error: {}".
2508 format(str(err).replace("\n", " ")))