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}, avg pkt loss: {loss:.1f}, " \
121 "stdev: {stdev:.2f}) {name}".format(
123 samples=nr_of_samples[i],
124 plural='s' if nr_of_samples[i] > 1 else '',
126 loss=mean(loss[col]) / 1000000,
127 stdev=stdev(loss[col]) / 1000000)
129 traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
130 y=[y if y else None for y in df[col]],
133 boxpoints="outliers",
136 val_max = max(df[col])
137 except ValueError as err:
138 logging.error(repr(err))
141 y_max.append(int(val_max) + 1)
145 layout = deepcopy(plot["layout"])
146 layout["title"] = "<b>Time Lost:</b> {0}".format(layout["title"])
147 layout["yaxis"]["title"] = "<b>Implied Time Lost [s]</b>"
148 layout["legend"]["font"]["size"] = 14
150 layout["yaxis"]["range"] = [0, max(y_max)]
151 plpl = plgo.Figure(data=traces, layout=layout)
154 file_type = plot.get("output-file-type", ".html")
155 logging.info(" Writing file '{0}{1}'.".
156 format(plot["output-file"], file_type))
157 ploff.plot(plpl, show_link=False, auto_open=False,
158 filename='{0}{1}'.format(plot["output-file"], file_type))
159 except PlotlyError as err:
160 logging.error(" Finished with error: {}".
161 format(repr(err).replace("\n", " ")))
165 def plot_performance_box_name(plot, input_data):
166 """Generate the plot(s) with algorithm: plot_performance_box_name
167 specified in the specification file.
169 :param plot: Plot to generate.
170 :param input_data: Data to process.
171 :type plot: pandas.Series
172 :type input_data: InputData
176 plot_title = plot.get("title", "")
177 logging.info(" Creating the data set for the {0} '{1}'.".
178 format(plot.get("type", ""), plot_title))
179 data = input_data.filter_tests_by_name(
180 plot, params=["throughput", "parent", "tags", "type"])
182 logging.error("No data.")
185 # Prepare the data for the plot
186 y_vals = OrderedDict()
190 if y_vals.get(test["parent"], None) is None:
191 y_vals[test["parent"]] = list()
193 if test["type"] in ("NDRPDR", ):
194 if "-pdr" in plot_title.lower():
195 y_vals[test["parent"]].\
196 append(test["throughput"]["PDR"]["LOWER"])
197 elif "-ndr" in plot_title.lower():
198 y_vals[test["parent"]]. \
199 append(test["throughput"]["NDR"]["LOWER"])
202 elif test["type"] in ("SOAK", ):
203 y_vals[test["parent"]].\
204 append(test["throughput"]["LOWER"])
207 except (KeyError, TypeError):
208 y_vals[test["parent"]].append(None)
210 # Add None to the lists with missing data
212 nr_of_samples = list()
213 for val in y_vals.values():
214 if len(val) > max_len:
216 nr_of_samples.append(len(val))
217 for key, val in y_vals.items():
218 if len(val) < max_len:
219 val.extend([None for _ in range(max_len - len(val))])
223 df = pd.DataFrame(y_vals)
226 for i, col in enumerate(df.columns):
227 tst_name = re.sub(REGEX_NIC, "",
228 col.lower().replace('-ndrpdr', '').
229 replace('2n1l-', ''))
230 name = "{nr}. ({samples:02d} run{plural}) {name}".\
232 samples=nr_of_samples[i],
233 plural='s' if nr_of_samples[i] > 1 else '',
237 traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
238 y=[y / 1000000 if y else None for y in df[col]],
241 boxpoints="outliers",
244 val_max = max(df[col])
245 except ValueError as err:
246 logging.error(repr(err))
249 y_max.append(int(val_max / 1000000) + 2)
253 layout = deepcopy(plot["layout"])
254 if layout.get("title", None):
255 layout["title"] = "<b>Throughput:</b> {0}". \
256 format(layout["title"])
258 layout["yaxis"]["range"] = [0, max(y_max)]
259 plpl = plgo.Figure(data=traces, layout=layout)
262 file_type = plot.get("output-file-type", ".html")
263 logging.info(" Writing file '{0}{1}'.".
264 format(plot["output-file"], file_type))
265 ploff.plot(plpl, show_link=False, auto_open=False,
266 filename='{0}{1}'.format(plot["output-file"], file_type))
267 except PlotlyError as err:
268 logging.error(" Finished with error: {}".
269 format(repr(err).replace("\n", " ")))
273 def plot_latency_error_bars_name(plot, input_data):
274 """Generate the plot(s) with algorithm: plot_latency_error_bars_name
275 specified in the specification file.
277 :param plot: Plot to generate.
278 :param input_data: Data to process.
279 :type plot: pandas.Series
280 :type input_data: InputData
284 plot_title = plot.get("title", "")
285 logging.info(" Creating the data set for the {0} '{1}'.".
286 format(plot.get("type", ""), plot_title))
287 data = input_data.filter_tests_by_name(
288 plot, params=["latency", "parent", "tags", "type"])
290 logging.error("No data.")
293 # Prepare the data for the plot
294 y_tmp_vals = OrderedDict()
299 logging.debug("test['latency']: {0}\n".
300 format(test["latency"]))
301 except ValueError as err:
302 logging.warning(repr(err))
303 if y_tmp_vals.get(test["parent"], None) is None:
304 y_tmp_vals[test["parent"]] = [
305 list(), # direction1, min
306 list(), # direction1, avg
307 list(), # direction1, max
308 list(), # direction2, min
309 list(), # direction2, avg
310 list() # direction2, max
313 if test["type"] in ("NDRPDR", ):
314 if "-pdr" in plot_title.lower():
316 elif "-ndr" in plot_title.lower():
319 logging.warning("Invalid test type: {0}".
320 format(test["type"]))
322 y_tmp_vals[test["parent"]][0].append(
323 test["latency"][ttype]["direction1"]["min"])
324 y_tmp_vals[test["parent"]][1].append(
325 test["latency"][ttype]["direction1"]["avg"])
326 y_tmp_vals[test["parent"]][2].append(
327 test["latency"][ttype]["direction1"]["max"])
328 y_tmp_vals[test["parent"]][3].append(
329 test["latency"][ttype]["direction2"]["min"])
330 y_tmp_vals[test["parent"]][4].append(
331 test["latency"][ttype]["direction2"]["avg"])
332 y_tmp_vals[test["parent"]][5].append(
333 test["latency"][ttype]["direction2"]["max"])
335 logging.warning("Invalid test type: {0}".
336 format(test["type"]))
338 except (KeyError, TypeError) as err:
339 logging.warning(repr(err))
345 nr_of_samples = list()
346 for key, val in y_tmp_vals.items():
347 name = re.sub(REGEX_NIC, "", key.replace('-ndrpdr', '').
348 replace('2n1l-', ''))
349 x_vals.append(name) # dir 1
350 y_vals.append(mean(val[1]) if val[1] else None)
351 y_mins.append(mean(val[0]) if val[0] else None)
352 y_maxs.append(mean(val[2]) if val[2] else None)
353 nr_of_samples.append(len(val[1]) if val[1] else 0)
354 x_vals.append(name) # dir 2
355 y_vals.append(mean(val[4]) if val[4] else None)
356 y_mins.append(mean(val[3]) if val[3] else None)
357 y_maxs.append(mean(val[5]) if val[5] else None)
358 nr_of_samples.append(len(val[3]) if val[3] else 0)
363 for idx in range(len(x_vals)):
364 if not bool(int(idx % 2)):
365 direction = "West-East"
367 direction = "East-West"
368 hovertext = ("No. of Runs: {nr}<br>"
370 "Direction: {dir}<br>".format(test=x_vals[idx],
372 nr=nr_of_samples[idx]))
373 if isinstance(y_maxs[idx], float):
374 hovertext += "Max: {max:.2f}uSec<br>".format(max=y_maxs[idx])
375 if isinstance(y_vals[idx], float):
376 hovertext += "Mean: {avg:.2f}uSec<br>".format(avg=y_vals[idx])
377 if isinstance(y_mins[idx], float):
378 hovertext += "Min: {min:.2f}uSec".format(min=y_mins[idx])
380 if isinstance(y_maxs[idx], float) and isinstance(y_vals[idx], float):
381 array = [y_maxs[idx] - y_vals[idx], ]
384 if isinstance(y_mins[idx], float) and isinstance(y_vals[idx], float):
385 arrayminus = [y_vals[idx] - y_mins[idx], ]
387 arrayminus = [None, ]
388 traces.append(plgo.Scatter(
392 legendgroup=x_vals[idx],
393 showlegend=bool(int(idx % 2)),
399 arrayminus=arrayminus,
400 color=COLORS[int(idx / 2)]
404 color=COLORS[int(idx / 2)],
409 annotations.append(dict(
416 text="E-W" if bool(int(idx % 2)) else "W-E",
426 file_type = plot.get("output-file-type", ".html")
427 logging.info(" Writing file '{0}{1}'.".
428 format(plot["output-file"], file_type))
429 layout = deepcopy(plot["layout"])
430 if layout.get("title", None):
431 layout["title"] = "<b>Latency:</b> {0}".\
432 format(layout["title"])
433 layout["annotations"] = annotations
434 plpl = plgo.Figure(data=traces, layout=layout)
438 show_link=False, auto_open=False,
439 filename='{0}{1}'.format(plot["output-file"], file_type))
440 except PlotlyError as err:
441 logging.error(" Finished with error: {}".
442 format(str(err).replace("\n", " ")))
446 def plot_throughput_speedup_analysis_name(plot, input_data):
447 """Generate the plot(s) with algorithm:
448 plot_throughput_speedup_analysis_name
449 specified in the specification file.
451 :param plot: Plot to generate.
452 :param input_data: Data to process.
453 :type plot: pandas.Series
454 :type input_data: InputData
458 plot_title = plot.get("title", "")
459 logging.info(" Creating the data set for the {0} '{1}'.".
460 format(plot.get("type", ""), plot_title))
461 data = input_data.filter_tests_by_name(
462 plot, params=["throughput", "parent", "tags", "type"])
464 logging.error("No data.")
467 y_vals = OrderedDict()
471 if y_vals.get(test["parent"], None) is None:
472 y_vals[test["parent"]] = {"1": list(),
476 if test["type"] in ("NDRPDR",):
477 if "-pdr" in plot_title.lower():
479 elif "-ndr" in plot_title.lower():
483 if "1C" in test["tags"]:
484 y_vals[test["parent"]]["1"]. \
485 append(test["throughput"][ttype]["LOWER"])
486 elif "2C" in test["tags"]:
487 y_vals[test["parent"]]["2"]. \
488 append(test["throughput"][ttype]["LOWER"])
489 elif "4C" in test["tags"]:
490 y_vals[test["parent"]]["4"]. \
491 append(test["throughput"][ttype]["LOWER"])
492 except (KeyError, TypeError):
496 logging.warning("No data for the plot '{}'".
497 format(plot.get("title", "")))
501 for test_name, test_vals in y_vals.items():
502 for key, test_val in test_vals.items():
504 avg_val = sum(test_val) / len(test_val)
505 y_vals[test_name][key] = (avg_val, len(test_val))
506 ideal = avg_val / (int(key) * 1000000.0)
507 if test_name not in y_1c_max or ideal > y_1c_max[test_name]:
508 y_1c_max[test_name] = ideal
514 pci_limit = plot["limits"]["pci"]["pci-g3-x8"]
515 for test_name, test_vals in y_vals.items():
517 if test_vals["1"][1]:
518 name = re.sub(REGEX_NIC, "", test_name.replace('-ndrpdr', '').
519 replace('2n1l-', ''))
520 vals[name] = OrderedDict()
521 y_val_1 = test_vals["1"][0] / 1000000.0
522 y_val_2 = test_vals["2"][0] / 1000000.0 if test_vals["2"][0] \
524 y_val_4 = test_vals["4"][0] / 1000000.0 if test_vals["4"][0] \
527 vals[name]["val"] = [y_val_1, y_val_2, y_val_4]
528 vals[name]["rel"] = [1.0, None, None]
529 vals[name]["ideal"] = [y_1c_max[test_name],
530 y_1c_max[test_name] * 2,
531 y_1c_max[test_name] * 4]
532 vals[name]["diff"] = [(y_val_1 - y_1c_max[test_name]) * 100 /
534 vals[name]["count"] = [test_vals["1"][1],
539 val_max = max(vals[name]["val"])
540 except ValueError as err:
541 logging.error(repr(err))
544 y_max.append(val_max)
547 vals[name]["rel"][1] = round(y_val_2 / y_val_1, 2)
548 vals[name]["diff"][1] = \
549 (y_val_2 - vals[name]["ideal"][1]) * 100 / y_val_2
551 vals[name]["rel"][2] = round(y_val_4 / y_val_1, 2)
552 vals[name]["diff"][2] = \
553 (y_val_4 - vals[name]["ideal"][2]) * 100 / y_val_4
554 except IndexError as err:
555 logging.warning("No data for '{0}'".format(test_name))
556 logging.warning(repr(err))
559 if "x520" in test_name:
560 limit = plot["limits"]["nic"]["x520"]
561 elif "x710" in test_name:
562 limit = plot["limits"]["nic"]["x710"]
563 elif "xxv710" in test_name:
564 limit = plot["limits"]["nic"]["xxv710"]
565 elif "xl710" in test_name:
566 limit = plot["limits"]["nic"]["xl710"]
567 elif "x553" in test_name:
568 limit = plot["limits"]["nic"]["x553"]
571 if limit > nic_limit:
574 mul = 2 if "ge2p" in test_name else 1
575 if "10ge" in test_name:
576 limit = plot["limits"]["link"]["10ge"] * mul
577 elif "25ge" in test_name:
578 limit = plot["limits"]["link"]["25ge"] * mul
579 elif "40ge" in test_name:
580 limit = plot["limits"]["link"]["40ge"] * mul
581 elif "100ge" in test_name:
582 limit = plot["limits"]["link"]["100ge"] * mul
585 if limit > lnk_limit:
594 threshold = 1.1 * max(y_max) # 10%
595 except ValueError as err:
598 nic_limit /= 1000000.0
599 traces.append(plgo.Scatter(
601 y=[nic_limit, ] * len(x_vals),
602 name="NIC: {0:.2f}Mpps".format(nic_limit),
611 annotations.append(dict(
618 text="NIC: {0:.2f}Mpps".format(nic_limit),
626 y_max.append(nic_limit)
628 lnk_limit /= 1000000.0
629 if lnk_limit < threshold:
630 traces.append(plgo.Scatter(
632 y=[lnk_limit, ] * len(x_vals),
633 name="Link: {0:.2f}Mpps".format(lnk_limit),
642 annotations.append(dict(
649 text="Link: {0:.2f}Mpps".format(lnk_limit),
657 y_max.append(lnk_limit)
659 pci_limit /= 1000000.0
660 if (pci_limit < threshold and
661 (pci_limit < lnk_limit * 0.95 or lnk_limit > lnk_limit * 1.05)):
662 traces.append(plgo.Scatter(
664 y=[pci_limit, ] * len(x_vals),
665 name="PCIe: {0:.2f}Mpps".format(pci_limit),
674 annotations.append(dict(
681 text="PCIe: {0:.2f}Mpps".format(pci_limit),
689 y_max.append(pci_limit)
691 # Perfect and measured:
693 for name, val in vals.iteritems():
696 for idx in range(len(val["val"])):
698 if isinstance(val["val"][idx], float):
699 htext += "No. of Runs: {1}<br>" \
700 "Mean: {0:.2f}Mpps<br>".format(val["val"][idx],
702 if isinstance(val["diff"][idx], float):
703 htext += "Diff: {0:.0f}%<br>".format(
704 round(val["diff"][idx]))
705 if isinstance(val["rel"][idx], float):
706 htext += "Speedup: {0:.2f}".format(val["rel"][idx])
707 hovertext.append(htext)
708 traces.append(plgo.Scatter(x=x_vals,
712 mode="lines+markers",
721 hoverinfo="text+name"
723 traces.append(plgo.Scatter(x=x_vals,
725 name="{0} perfect".format(name),
733 text=["Perfect: {0:.2f}Mpps".format(y)
734 for y in val["ideal"]],
738 except (IndexError, ValueError, KeyError) as err:
739 logging.warning("No data for '{0}'".format(name))
740 logging.warning(repr(err))
744 file_type = plot.get("output-file-type", ".html")
745 logging.info(" Writing file '{0}{1}'.".
746 format(plot["output-file"], file_type))
747 layout = deepcopy(plot["layout"])
748 if layout.get("title", None):
749 layout["title"] = "<b>Speedup Multi-core:</b> {0}". \
750 format(layout["title"])
751 layout["yaxis"]["range"] = [0, int(max(y_max) * 1.1)]
752 layout["annotations"].extend(annotations)
753 plpl = plgo.Figure(data=traces, layout=layout)
757 show_link=False, auto_open=False,
758 filename='{0}{1}'.format(plot["output-file"], file_type))
759 except PlotlyError as err:
760 logging.error(" Finished with error: {}".
761 format(repr(err).replace("\n", " ")))
765 def plot_performance_box(plot, input_data):
766 """Generate the plot(s) with algorithm: plot_performance_box
767 specified in the specification file.
769 TODO: Remove when not needed.
771 :param plot: Plot to generate.
772 :param input_data: Data to process.
773 :type plot: pandas.Series
774 :type input_data: InputData
778 plot_title = plot.get("title", "")
779 logging.info(" Creating the data set for the {0} '{1}'.".
780 format(plot.get("type", ""), plot_title))
781 data = input_data.filter_data(plot)
783 logging.error("No data.")
786 # Prepare the data for the plot
792 if y_vals.get(test["parent"], None) is None:
793 y_vals[test["parent"]] = list()
794 y_tags[test["parent"]] = test.get("tags", None)
796 if test["type"] in ("NDRPDR", ):
797 if "-pdr" in plot_title.lower():
798 y_vals[test["parent"]].\
799 append(test["throughput"]["PDR"]["LOWER"])
800 elif "-ndr" in plot_title.lower():
801 y_vals[test["parent"]]. \
802 append(test["throughput"]["NDR"]["LOWER"])
805 elif test["type"] in ("SOAK", ):
806 y_vals[test["parent"]].\
807 append(test["throughput"]["LOWER"])
810 except (KeyError, TypeError):
811 y_vals[test["parent"]].append(None)
814 order = plot.get("sort", None)
816 y_sorted = OrderedDict()
817 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
820 for suite, tags in y_tags_l.items():
822 tag = tag.split(" ")[-1]
823 if tag.lower() in tags:
826 if tag.lower() not in tags:
829 y_sorted[suite] = y_vals.pop(suite)
832 except KeyError as err:
833 logging.error("Not found: {0}".format(repr(err)))
839 # Add None to the lists with missing data
841 nr_of_samples = list()
842 for val in y_sorted.values():
843 if len(val) > max_len:
845 nr_of_samples.append(len(val))
846 for key, val in y_sorted.items():
847 if len(val) < max_len:
848 val.extend([None for _ in range(max_len - len(val))])
852 df = pd.DataFrame(y_sorted)
855 for i, col in enumerate(df.columns):
856 tst_name = re.sub(REGEX_NIC, "",
857 col.lower().replace('-ndrpdr', '').
858 replace('2n1l-', ''))
859 name = "{nr}. ({samples:02d} run{plural}) {name}".\
861 samples=nr_of_samples[i],
862 plural='s' if nr_of_samples[i] > 1 else '',
866 traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
867 y=[y / 1000000 if y else None for y in df[col]],
871 val_max = max(df[col])
872 except ValueError as err:
873 logging.error(repr(err))
876 y_max.append(int(val_max / 1000000) + 2)
880 layout = deepcopy(plot["layout"])
881 if layout.get("title", None):
882 layout["title"] = "<b>Throughput:</b> {0}". \
883 format(layout["title"])
885 layout["yaxis"]["range"] = [0, max(y_max)]
886 plpl = plgo.Figure(data=traces, layout=layout)
889 logging.info(" Writing file '{0}{1}'.".
890 format(plot["output-file"], plot["output-file-type"]))
891 ploff.plot(plpl, show_link=False, auto_open=False,
892 filename='{0}{1}'.format(plot["output-file"],
893 plot["output-file-type"]))
894 except PlotlyError as err:
895 logging.error(" Finished with error: {}".
896 format(repr(err).replace("\n", " ")))
900 def plot_soak_bars(plot, input_data):
901 """Generate the plot(s) with algorithm: plot_soak_bars
902 specified in the specification file.
904 :param plot: Plot to generate.
905 :param input_data: Data to process.
906 :type plot: pandas.Series
907 :type input_data: InputData
911 plot_title = plot.get("title", "")
912 logging.info(" Creating the data set for the {0} '{1}'.".
913 format(plot.get("type", ""), plot_title))
914 data = input_data.filter_data(plot)
916 logging.error("No data.")
919 # Prepare the data for the plot
925 if y_vals.get(test["parent"], None) is None:
926 y_tags[test["parent"]] = test.get("tags", None)
928 if test["type"] in ("SOAK", ):
929 y_vals[test["parent"]] = test["throughput"]
932 except (KeyError, TypeError):
933 y_vals[test["parent"]] = dict()
936 order = plot.get("sort", None)
938 y_sorted = OrderedDict()
939 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
942 for suite, tags in y_tags_l.items():
944 tag = tag.split(" ")[-1]
945 if tag.lower() in tags:
948 if tag.lower() not in tags:
951 y_sorted[suite] = y_vals.pop(suite)
954 except KeyError as err:
955 logging.error("Not found: {0}".format(repr(err)))
964 for test_name, test_data in y_sorted.items():
966 name = "{nr}. {name}".\
967 format(nr=idx, name=test_name.lower().replace('-soak', ''))
969 name_lst = name.split('-')
972 for segment in name_lst:
973 if (len(name) + len(segment) + 1) > 50 and split_name:
976 name += segment + '-'
979 y_val = test_data.get("LOWER", None)
985 time = "No Information"
986 result = "No Information"
987 hovertext = ("{name}<br>"
988 "Packet Throughput: {val:.2f}Mpps<br>"
989 "Final Duration: {time}<br>"
990 "Result: {result}".format(name=name,
994 traces.append(plgo.Bar(x=[str(idx) + '.', ],
1001 layout = deepcopy(plot["layout"])
1002 if layout.get("title", None):
1003 layout["title"] = "<b>Packet Throughput:</b> {0}". \
1004 format(layout["title"])
1006 layout["yaxis"]["range"] = [0, y_max + 1]
1007 plpl = plgo.Figure(data=traces, layout=layout)
1009 logging.info(" Writing file '{0}{1}'.".
1010 format(plot["output-file"], plot["output-file-type"]))
1011 ploff.plot(plpl, show_link=False, auto_open=False,
1012 filename='{0}{1}'.format(plot["output-file"],
1013 plot["output-file-type"]))
1014 except PlotlyError as err:
1015 logging.error(" Finished with error: {}".
1016 format(repr(err).replace("\n", " ")))
1020 def plot_soak_boxes(plot, input_data):
1021 """Generate the plot(s) with algorithm: plot_soak_boxes
1022 specified in the specification file.
1024 :param plot: Plot to generate.
1025 :param input_data: Data to process.
1026 :type plot: pandas.Series
1027 :type input_data: InputData
1030 # Transform the data
1031 plot_title = plot.get("title", "")
1032 logging.info(" Creating the data set for the {0} '{1}'.".
1033 format(plot.get("type", ""), plot_title))
1034 data = input_data.filter_data(plot)
1036 logging.error("No data.")
1039 # Prepare the data for the plot
1045 if y_vals.get(test["parent"], None) is None:
1046 y_tags[test["parent"]] = test.get("tags", None)
1048 if test["type"] in ("SOAK", ):
1049 y_vals[test["parent"]] = test["throughput"]
1052 except (KeyError, TypeError):
1053 y_vals[test["parent"]] = dict()
1056 order = plot.get("sort", None)
1057 if order and y_tags:
1058 y_sorted = OrderedDict()
1059 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
1062 for suite, tags in y_tags_l.items():
1064 tag = tag.split(" ")[-1]
1065 if tag.lower() in tags:
1068 if tag.lower() not in tags:
1071 y_sorted[suite] = y_vals.pop(suite)
1073 logging.debug(suite)
1074 except KeyError as err:
1075 logging.error("Not found: {0}".format(repr(err)))
1084 for test_name, test_data in y_sorted.items():
1086 name = "{nr}. {name}".\
1087 format(nr=idx, name=test_name.lower().replace('-soak', '').
1088 replace('2n1l-', ''))
1090 name_lst = name.split('-')
1093 for segment in name_lst:
1094 if (len(name) + len(segment) + 1) > 55 and split_name:
1097 name += segment + '-'
1100 y_val = test_data.get("UPPER", None)
1106 y_base = test_data.get("LOWER", None)
1110 hovertext = ("Upper bound: {upper:.2f}<br>"
1111 "Lower bound: {lower:.2f}".format(upper=y_val,
1113 traces.append(plgo.Bar(x=[str(idx) + '.', ],
1114 # +0.05 to see the value in case lower == upper
1115 y=[y_val - y_base + 0.05, ],
1122 layout = deepcopy(plot["layout"])
1123 if layout.get("title", None):
1124 layout["title"] = "<b>Throughput:</b> {0}". \
1125 format(layout["title"])
1127 layout["yaxis"]["range"] = [0, y_max + 1]
1128 plpl = plgo.Figure(data=traces, layout=layout)
1130 logging.info(" Writing file '{0}{1}'.".
1131 format(plot["output-file"], plot["output-file-type"]))
1132 ploff.plot(plpl, show_link=False, auto_open=False,
1133 filename='{0}{1}'.format(plot["output-file"],
1134 plot["output-file-type"]))
1135 except PlotlyError as err:
1136 logging.error(" Finished with error: {}".
1137 format(repr(err).replace("\n", " ")))
1141 def plot_latency_error_bars(plot, input_data):
1142 """Generate the plot(s) with algorithm: plot_latency_error_bars
1143 specified in the specification file.
1145 TODO: Remove when not needed.
1147 :param plot: Plot to generate.
1148 :param input_data: Data to process.
1149 :type plot: pandas.Series
1150 :type input_data: InputData
1153 # Transform the data
1154 plot_title = plot.get("title", "")
1155 logging.info(" Creating the data set for the {0} '{1}'.".
1156 format(plot.get("type", ""), plot_title))
1157 data = input_data.filter_data(plot)
1159 logging.error("No data.")
1162 # Prepare the data for the plot
1169 logging.debug("test['latency']: {0}\n".
1170 format(test["latency"]))
1171 except ValueError as err:
1172 logging.warning(repr(err))
1173 if y_tmp_vals.get(test["parent"], None) is None:
1174 y_tmp_vals[test["parent"]] = [
1175 list(), # direction1, min
1176 list(), # direction1, avg
1177 list(), # direction1, max
1178 list(), # direction2, min
1179 list(), # direction2, avg
1180 list() # direction2, max
1182 y_tags[test["parent"]] = test.get("tags", None)
1184 if test["type"] in ("NDRPDR", ):
1185 if "-pdr" in plot_title.lower():
1187 elif "-ndr" in plot_title.lower():
1190 logging.warning("Invalid test type: {0}".
1191 format(test["type"]))
1193 y_tmp_vals[test["parent"]][0].append(
1194 test["latency"][ttype]["direction1"]["min"])
1195 y_tmp_vals[test["parent"]][1].append(
1196 test["latency"][ttype]["direction1"]["avg"])
1197 y_tmp_vals[test["parent"]][2].append(
1198 test["latency"][ttype]["direction1"]["max"])
1199 y_tmp_vals[test["parent"]][3].append(
1200 test["latency"][ttype]["direction2"]["min"])
1201 y_tmp_vals[test["parent"]][4].append(
1202 test["latency"][ttype]["direction2"]["avg"])
1203 y_tmp_vals[test["parent"]][5].append(
1204 test["latency"][ttype]["direction2"]["max"])
1206 logging.warning("Invalid test type: {0}".
1207 format(test["type"]))
1209 except (KeyError, TypeError) as err:
1210 logging.warning(repr(err))
1211 logging.debug("y_tmp_vals: {0}\n".format(y_tmp_vals))
1214 order = plot.get("sort", None)
1215 if order and y_tags:
1216 y_sorted = OrderedDict()
1217 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
1220 for suite, tags in y_tags_l.items():
1222 tag = tag.split(" ")[-1]
1223 if tag.lower() in tags:
1226 if tag.lower() not in tags:
1229 y_sorted[suite] = y_tmp_vals.pop(suite)
1231 logging.debug(suite)
1232 except KeyError as err:
1233 logging.error("Not found: {0}".format(repr(err)))
1237 y_sorted = y_tmp_vals
1239 logging.debug("y_sorted: {0}\n".format(y_sorted))
1244 nr_of_samples = list()
1245 for key, val in y_sorted.items():
1246 name = re.sub(REGEX_NIC, "", key.replace('-ndrpdr', '').
1247 replace('2n1l-', ''))
1248 x_vals.append(name) # dir 1
1249 y_vals.append(mean(val[1]) if val[1] else None)
1250 y_mins.append(mean(val[0]) if val[0] else None)
1251 y_maxs.append(mean(val[2]) if val[2] else None)
1252 nr_of_samples.append(len(val[1]) if val[1] else 0)
1253 x_vals.append(name) # dir 2
1254 y_vals.append(mean(val[4]) if val[4] else None)
1255 y_mins.append(mean(val[3]) if val[3] else None)
1256 y_maxs.append(mean(val[5]) if val[5] else None)
1257 nr_of_samples.append(len(val[3]) if val[3] else 0)
1259 logging.debug("x_vals :{0}\n".format(x_vals))
1260 logging.debug("y_vals :{0}\n".format(y_vals))
1261 logging.debug("y_mins :{0}\n".format(y_mins))
1262 logging.debug("y_maxs :{0}\n".format(y_maxs))
1263 logging.debug("nr_of_samples :{0}\n".format(nr_of_samples))
1265 annotations = list()
1267 for idx in range(len(x_vals)):
1268 if not bool(int(idx % 2)):
1269 direction = "West-East"
1271 direction = "East-West"
1272 hovertext = ("No. of Runs: {nr}<br>"
1274 "Direction: {dir}<br>".format(test=x_vals[idx],
1276 nr=nr_of_samples[idx]))
1277 if isinstance(y_maxs[idx], float):
1278 hovertext += "Max: {max:.2f}uSec<br>".format(max=y_maxs[idx])
1279 if isinstance(y_vals[idx], float):
1280 hovertext += "Mean: {avg:.2f}uSec<br>".format(avg=y_vals[idx])
1281 if isinstance(y_mins[idx], float):
1282 hovertext += "Min: {min:.2f}uSec".format(min=y_mins[idx])
1284 if isinstance(y_maxs[idx], float) and isinstance(y_vals[idx], float):
1285 array = [y_maxs[idx] - y_vals[idx], ]
1288 if isinstance(y_mins[idx], float) and isinstance(y_vals[idx], float):
1289 arrayminus = [y_vals[idx] - y_mins[idx], ]
1291 arrayminus = [None, ]
1292 logging.debug("y_vals[{1}] :{0}\n".format(y_vals[idx], idx))
1293 logging.debug("array :{0}\n".format(array))
1294 logging.debug("arrayminus :{0}\n".format(arrayminus))
1295 traces.append(plgo.Scatter(
1299 legendgroup=x_vals[idx],
1300 showlegend=bool(int(idx % 2)),
1306 arrayminus=arrayminus,
1307 color=COLORS[int(idx / 2)]
1311 color=COLORS[int(idx / 2)],
1316 annotations.append(dict(
1323 text="E-W" if bool(int(idx % 2)) else "W-E",
1333 logging.info(" Writing file '{0}{1}'.".
1334 format(plot["output-file"], plot["output-file-type"]))
1335 layout = deepcopy(plot["layout"])
1336 if layout.get("title", None):
1337 layout["title"] = "<b>Latency:</b> {0}".\
1338 format(layout["title"])
1339 layout["annotations"] = annotations
1340 plpl = plgo.Figure(data=traces, layout=layout)
1344 show_link=False, auto_open=False,
1345 filename='{0}{1}'.format(plot["output-file"],
1346 plot["output-file-type"]))
1347 except PlotlyError as err:
1348 logging.error(" Finished with error: {}".
1349 format(str(err).replace("\n", " ")))
1353 def plot_throughput_speedup_analysis(plot, input_data):
1354 """Generate the plot(s) with algorithm:
1355 plot_throughput_speedup_analysis
1356 specified in the specification file.
1358 TODO: Remove when not needed.
1360 :param plot: Plot to generate.
1361 :param input_data: Data to process.
1362 :type plot: pandas.Series
1363 :type input_data: InputData
1366 # Transform the data
1367 plot_title = plot.get("title", "")
1368 logging.info(" Creating the data set for the {0} '{1}'.".
1369 format(plot.get("type", ""), plot_title))
1370 data = input_data.filter_data(plot)
1372 logging.error("No data.")
1380 if y_vals.get(test["parent"], None) is None:
1381 y_vals[test["parent"]] = {"1": list(),
1384 y_tags[test["parent"]] = test.get("tags", None)
1386 if test["type"] in ("NDRPDR",):
1387 if "-pdr" in plot_title.lower():
1389 elif "-ndr" in plot_title.lower():
1393 if "1C" in test["tags"]:
1394 y_vals[test["parent"]]["1"]. \
1395 append(test["throughput"][ttype]["LOWER"])
1396 elif "2C" in test["tags"]:
1397 y_vals[test["parent"]]["2"]. \
1398 append(test["throughput"][ttype]["LOWER"])
1399 elif "4C" in test["tags"]:
1400 y_vals[test["parent"]]["4"]. \
1401 append(test["throughput"][ttype]["LOWER"])
1402 except (KeyError, TypeError):
1406 logging.warning("No data for the plot '{}'".
1407 format(plot.get("title", "")))
1411 for test_name, test_vals in y_vals.items():
1412 for key, test_val in test_vals.items():
1414 avg_val = sum(test_val) / len(test_val)
1415 y_vals[test_name][key] = (avg_val, len(test_val))
1416 ideal = avg_val / (int(key) * 1000000.0)
1417 if test_name not in y_1c_max or ideal > y_1c_max[test_name]:
1418 y_1c_max[test_name] = ideal
1424 pci_limit = plot["limits"]["pci"]["pci-g3-x8"]
1425 for test_name, test_vals in y_vals.items():
1427 if test_vals["1"][1]:
1428 name = re.sub(REGEX_NIC, "", test_name.replace('-ndrpdr', '').
1429 replace('2n1l-', ''))
1431 y_val_1 = test_vals["1"][0] / 1000000.0
1432 y_val_2 = test_vals["2"][0] / 1000000.0 if test_vals["2"][0] \
1434 y_val_4 = test_vals["4"][0] / 1000000.0 if test_vals["4"][0] \
1437 vals[name]["val"] = [y_val_1, y_val_2, y_val_4]
1438 vals[name]["rel"] = [1.0, None, None]
1439 vals[name]["ideal"] = [y_1c_max[test_name],
1440 y_1c_max[test_name] * 2,
1441 y_1c_max[test_name] * 4]
1442 vals[name]["diff"] = [(y_val_1 - y_1c_max[test_name]) * 100 /
1443 y_val_1, None, None]
1444 vals[name]["count"] = [test_vals["1"][1],
1449 # val_max = max(max(vals[name]["val"], vals[name]["ideal"]))
1450 val_max = max(vals[name]["val"])
1451 except ValueError as err:
1455 # y_max.append(int((val_max / 10) + 1) * 10)
1456 y_max.append(val_max)
1459 vals[name]["rel"][1] = round(y_val_2 / y_val_1, 2)
1460 vals[name]["diff"][1] = \
1461 (y_val_2 - vals[name]["ideal"][1]) * 100 / y_val_2
1463 vals[name]["rel"][2] = round(y_val_4 / y_val_1, 2)
1464 vals[name]["diff"][2] = \
1465 (y_val_4 - vals[name]["ideal"][2]) * 100 / y_val_4
1466 except IndexError as err:
1467 logging.warning("No data for '{0}'".format(test_name))
1468 logging.warning(repr(err))
1471 if "x520" in test_name:
1472 limit = plot["limits"]["nic"]["x520"]
1473 elif "x710" in test_name:
1474 limit = plot["limits"]["nic"]["x710"]
1475 elif "xxv710" in test_name:
1476 limit = plot["limits"]["nic"]["xxv710"]
1477 elif "xl710" in test_name:
1478 limit = plot["limits"]["nic"]["xl710"]
1479 elif "x553" in test_name:
1480 limit = plot["limits"]["nic"]["x553"]
1483 if limit > nic_limit:
1486 mul = 2 if "ge2p" in test_name else 1
1487 if "10ge" in test_name:
1488 limit = plot["limits"]["link"]["10ge"] * mul
1489 elif "25ge" in test_name:
1490 limit = plot["limits"]["link"]["25ge"] * mul
1491 elif "40ge" in test_name:
1492 limit = plot["limits"]["link"]["40ge"] * mul
1493 elif "100ge" in test_name:
1494 limit = plot["limits"]["link"]["100ge"] * mul
1497 if limit > lnk_limit:
1501 order = plot.get("sort", None)
1502 if order and y_tags:
1503 y_sorted = OrderedDict()
1504 y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
1506 for test, tags in y_tags_l.items():
1507 if tag.lower() in tags:
1508 name = re.sub(REGEX_NIC, "",
1509 test.replace('-ndrpdr', '').
1510 replace('2n1l-', ''))
1512 y_sorted[name] = vals.pop(name)
1514 except KeyError as err:
1515 logging.error("Not found: {0}".format(err))
1522 annotations = list()
1527 threshold = 1.1 * max(y_max) # 10%
1528 except ValueError as err:
1531 nic_limit /= 1000000.0
1532 # if nic_limit < threshold:
1533 traces.append(plgo.Scatter(
1535 y=[nic_limit, ] * len(x_vals),
1536 name="NIC: {0:.2f}Mpps".format(nic_limit),
1545 annotations.append(dict(
1552 text="NIC: {0:.2f}Mpps".format(nic_limit),
1560 # y_max.append(int((nic_limit / 10) + 1) * 10)
1561 y_max.append(nic_limit)
1563 lnk_limit /= 1000000.0
1564 if lnk_limit < threshold:
1565 traces.append(plgo.Scatter(
1567 y=[lnk_limit, ] * len(x_vals),
1568 name="Link: {0:.2f}Mpps".format(lnk_limit),
1577 annotations.append(dict(
1584 text="Link: {0:.2f}Mpps".format(lnk_limit),
1592 # y_max.append(int((lnk_limit / 10) + 1) * 10)
1593 y_max.append(lnk_limit)
1595 pci_limit /= 1000000.0
1596 if (pci_limit < threshold and
1597 (pci_limit < lnk_limit * 0.95 or lnk_limit > lnk_limit * 1.05)):
1598 traces.append(plgo.Scatter(
1600 y=[pci_limit, ] * len(x_vals),
1601 name="PCIe: {0:.2f}Mpps".format(pci_limit),
1610 annotations.append(dict(
1617 text="PCIe: {0:.2f}Mpps".format(pci_limit),
1625 # y_max.append(int((pci_limit / 10) + 1) * 10)
1626 y_max.append(pci_limit)
1628 # Perfect and measured:
1630 for name, val in y_sorted.iteritems():
1633 for idx in range(len(val["val"])):
1635 if isinstance(val["val"][idx], float):
1636 htext += "No. of Runs: {1}<br>" \
1637 "Mean: {0:.2f}Mpps<br>".format(val["val"][idx],
1639 if isinstance(val["diff"][idx], float):
1640 htext += "Diff: {0:.0f}%<br>".format(round(val["diff"][idx]))
1641 if isinstance(val["rel"][idx], float):
1642 htext += "Speedup: {0:.2f}".format(val["rel"][idx])
1643 hovertext.append(htext)
1644 traces.append(plgo.Scatter(x=x_vals,
1648 mode="lines+markers",
1657 hoverinfo="text+name"
1659 traces.append(plgo.Scatter(x=x_vals,
1661 name="{0} perfect".format(name),
1669 text=["Perfect: {0:.2f}Mpps".format(y)
1670 for y in val["ideal"]],
1674 except (IndexError, ValueError, KeyError) as err:
1675 logging.warning("No data for '{0}'".format(name))
1676 logging.warning(repr(err))
1680 logging.info(" Writing file '{0}{1}'.".
1681 format(plot["output-file"], plot["output-file-type"]))
1682 layout = deepcopy(plot["layout"])
1683 if layout.get("title", None):
1684 layout["title"] = "<b>Speedup Multi-core:</b> {0}". \
1685 format(layout["title"])
1686 # layout["yaxis"]["range"] = [0, int((max(y_max) / 10) + 1) * 10]
1687 layout["yaxis"]["range"] = [0, int(max(y_max) * 1.1)]
1688 layout["annotations"].extend(annotations)
1689 plpl = plgo.Figure(data=traces, layout=layout)
1693 show_link=False, auto_open=False,
1694 filename='{0}{1}'.format(plot["output-file"],
1695 plot["output-file-type"]))
1696 except PlotlyError as err:
1697 logging.error(" Finished with error: {}".
1698 format(str(err).replace("\n", " ")))
1702 def plot_http_server_performance_box(plot, input_data):
1703 """Generate the plot(s) with algorithm: plot_http_server_performance_box
1704 specified in the specification file.
1706 :param plot: Plot to generate.
1707 :param input_data: Data to process.
1708 :type plot: pandas.Series
1709 :type input_data: InputData
1712 # Transform the data
1713 logging.info(" Creating the data set for the {0} '{1}'.".
1714 format(plot.get("type", ""), plot.get("title", "")))
1715 data = input_data.filter_data(plot)
1717 logging.error("No data.")
1720 # Prepare the data for the plot
1725 if y_vals.get(test["name"], None) is None:
1726 y_vals[test["name"]] = list()
1728 y_vals[test["name"]].append(test["result"])
1729 except (KeyError, TypeError):
1730 y_vals[test["name"]].append(None)
1732 # Add None to the lists with missing data
1734 nr_of_samples = list()
1735 for val in y_vals.values():
1736 if len(val) > max_len:
1738 nr_of_samples.append(len(val))
1739 for key, val in y_vals.items():
1740 if len(val) < max_len:
1741 val.extend([None for _ in range(max_len - len(val))])
1745 df = pd.DataFrame(y_vals)
1747 for i, col in enumerate(df.columns):
1748 name = "{nr}. ({samples:02d} run{plural}) {name}".\
1750 samples=nr_of_samples[i],
1751 plural='s' if nr_of_samples[i] > 1 else '',
1752 name=col.lower().replace('-ndrpdr', ''))
1754 name_lst = name.split('-')
1757 for segment in name_lst:
1758 if (len(name) + len(segment) + 1) > 50 and split_name:
1761 name += segment + '-'
1764 traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
1770 plpl = plgo.Figure(data=traces, layout=plot["layout"])
1773 logging.info(" Writing file '{0}{1}'.".
1774 format(plot["output-file"], plot["output-file-type"]))
1775 ploff.plot(plpl, show_link=False, auto_open=False,
1776 filename='{0}{1}'.format(plot["output-file"],
1777 plot["output-file-type"]))
1778 except PlotlyError as err:
1779 logging.error(" Finished with error: {}".
1780 format(str(err).replace("\n", " ")))
1784 def plot_service_density_heatmap(plot, input_data):
1785 """Generate the plot(s) with algorithm: plot_service_density_heatmap
1786 specified in the specification file.
1788 :param plot: Plot to generate.
1789 :param input_data: Data to process.
1790 :type plot: pandas.Series
1791 :type input_data: InputData
1794 REGEX_CN = re.compile(r'^(\d*)R(\d*)C$')
1795 REGEX_TEST_NAME = re.compile(r'^.*-(\d+vhost|\d+memif)-'
1796 r'(\d+chain|\d+pipe)-'
1797 r'(\d+vm|\d+dcr|\d+drc).*$')
1803 # Transform the data
1804 logging.info(" Creating the data set for the {0} '{1}'.".
1805 format(plot.get("type", ""), plot.get("title", "")))
1806 data = input_data.filter_data(plot, continue_on_error=True)
1807 if data is None or data.empty:
1808 logging.error("No data.")
1814 for tag in test['tags']:
1815 groups = re.search(REGEX_CN, tag)
1817 c = str(groups.group(1))
1818 n = str(groups.group(2))
1822 groups = re.search(REGEX_TEST_NAME, test["name"])
1823 if groups and len(groups.groups()) == 3:
1824 hover_name = "{vhost}-{chain}-{vm}".format(
1825 vhost=str(groups.group(1)),
1826 chain=str(groups.group(2)),
1827 vm=str(groups.group(3)))
1830 if vals.get(c, None) is None:
1832 if vals[c].get(n, None) is None:
1833 vals[c][n] = dict(name=hover_name,
1839 if plot["include-tests"] == "MRR":
1840 result = test["result"]["receive-rate"].avg
1841 elif plot["include-tests"] == "PDR":
1842 result = test["throughput"]["PDR"]["LOWER"]
1843 elif plot["include-tests"] == "NDR":
1844 result = test["throughput"]["NDR"]["LOWER"]
1851 vals[c][n]["vals"].append(result)
1854 logging.error("No data.")
1857 for key_c in vals.keys():
1858 txt_chains.append(key_c)
1859 for key_n in vals[key_c].keys():
1860 txt_nodes.append(key_n)
1861 if vals[key_c][key_n]["vals"]:
1862 vals[key_c][key_n]["nr"] = len(vals[key_c][key_n]["vals"])
1863 vals[key_c][key_n]["mean"] = \
1864 round(mean(vals[key_c][key_n]["vals"]) / 1000000, 1)
1865 vals[key_c][key_n]["stdev"] = \
1866 round(stdev(vals[key_c][key_n]["vals"]) / 1000000, 1)
1867 txt_nodes = list(set(txt_nodes))
1869 txt_chains = sorted(txt_chains, key=lambda chain: int(chain))
1870 txt_nodes = sorted(txt_nodes, key=lambda node: int(node))
1872 chains = [i + 1 for i in range(len(txt_chains))]
1873 nodes = [i + 1 for i in range(len(txt_nodes))]
1875 data = [list() for _ in range(len(chains))]
1879 val = vals[txt_chains[c - 1]][txt_nodes[n - 1]]["mean"]
1880 except (KeyError, IndexError):
1882 data[c - 1].append(val)
1885 my_green = [[0.0, 'rgb(235, 249, 242)'],
1886 [1.0, 'rgb(45, 134, 89)']]
1888 my_blue = [[0.0, 'rgb(236, 242, 248)'],
1889 [1.0, 'rgb(57, 115, 172)']]
1891 my_grey = [[0.0, 'rgb(230, 230, 230)'],
1892 [1.0, 'rgb(102, 102, 102)']]
1895 annotations = list()
1897 text = ("Test: {name}<br>"
1902 for c in range(len(txt_chains)):
1904 for n in range(len(txt_nodes)):
1905 if data[c][n] is not None:
1906 annotations.append(dict(
1913 text=str(data[c][n]),
1920 hover_line.append(text.format(
1921 name=vals[txt_chains[c]][txt_nodes[n]]["name"],
1922 nr=vals[txt_chains[c]][txt_nodes[n]]["nr"],
1924 stdev=vals[txt_chains[c]][txt_nodes[n]]["stdev"]))
1925 hovertext.append(hover_line)
1928 plgo.Heatmap(x=nodes,
1932 title=plot.get("z-axis", ""),
1946 colorscale=my_green,
1951 for idx, item in enumerate(txt_nodes):
1953 annotations.append(dict(
1967 for idx, item in enumerate(txt_chains):
1969 annotations.append(dict(
1984 annotations.append(dict(
1991 text=plot.get("x-axis", ""),
1999 annotations.append(dict(
2006 text=plot.get("y-axis", ""),
2014 updatemenus = list([
2023 args=[{"colorscale": [my_green, ], "reversescale": False}],
2028 args=[{"colorscale": [my_blue, ], "reversescale": False}],
2033 args=[{"colorscale": [my_grey, ], "reversescale": False}],
2042 layout = deepcopy(plot["layout"])
2043 except KeyError as err:
2044 logging.error("Finished with error: No layout defined")
2045 logging.error(repr(err))
2048 layout["annotations"] = annotations
2049 layout['updatemenus'] = updatemenus
2053 plpl = plgo.Figure(data=traces, layout=layout)
2056 logging.info(" Writing file '{0}{1}'.".
2057 format(plot["output-file"], plot["output-file-type"]))
2058 ploff.plot(plpl, show_link=False, auto_open=False,
2059 filename='{0}{1}'.format(plot["output-file"],
2060 plot["output-file-type"]))
2061 except PlotlyError as err:
2062 logging.error(" Finished with error: {}".
2063 format(str(err).replace("\n", " ")))
2067 def plot_service_density_heatmap_compare(plot, input_data):
2068 """Generate the plot(s) with algorithm: plot_service_density_heatmap_compare
2069 specified in the specification file.
2071 :param plot: Plot to generate.
2072 :param input_data: Data to process.
2073 :type plot: pandas.Series
2074 :type input_data: InputData
2077 REGEX_CN = re.compile(r'^(\d*)R(\d*)C$')
2078 REGEX_TEST_NAME = re.compile(r'^.*-(\d+ch|\d+pl)-'
2080 r'(\d+vm|\d+dcr).*$')
2081 REGEX_THREADS = re.compile(r'^(\d+)(VM|DCR)(\d+)T$')
2087 # Transform the data
2088 logging.info(" Creating the data set for the {0} '{1}'.".
2089 format(plot.get("type", ""), plot.get("title", "")))
2090 data = input_data.filter_data(plot, continue_on_error=True)
2091 if data is None or data.empty:
2092 logging.error("No data.")
2098 for tag in test['tags']:
2099 groups = re.search(REGEX_CN, tag)
2101 c = str(groups.group(1))
2102 n = str(groups.group(2))
2106 groups = re.search(REGEX_TEST_NAME, test["name"])
2107 if groups and len(groups.groups()) == 3:
2108 hover_name = "{chain}-{vhost}-{vm}".format(
2109 chain=str(groups.group(1)),
2110 vhost=str(groups.group(2)),
2111 vm=str(groups.group(3)))
2114 if vals.get(c, None) is None:
2116 if vals[c].get(n, None) is None:
2117 vals[c][n] = dict(name=hover_name,
2127 if plot["include-tests"] == "MRR":
2128 result = test["result"]["receive-rate"].avg
2129 elif plot["include-tests"] == "PDR":
2130 result = test["throughput"]["PDR"]["LOWER"]
2131 elif plot["include-tests"] == "NDR":
2132 result = test["throughput"]["NDR"]["LOWER"]
2139 for tag in test['tags']:
2140 groups = re.search(REGEX_THREADS, tag)
2141 if groups and len(groups.groups()) == 3:
2142 if str(groups.group(3)) == \
2143 plot["reference"]["include"]:
2144 vals[c][n]["vals_r"].append(result)
2145 elif str(groups.group(3)) == \
2146 plot["compare"]["include"]:
2147 vals[c][n]["vals_c"].append(result)
2150 logging.error("No data.")
2153 for key_c in vals.keys():
2154 txt_chains.append(key_c)
2155 for key_n in vals[key_c].keys():
2156 txt_nodes.append(key_n)
2157 if vals[key_c][key_n]["vals_r"]:
2158 vals[key_c][key_n]["nr_r"] = len(vals[key_c][key_n]["vals_r"])
2159 vals[key_c][key_n]["mean_r"] = \
2160 mean(vals[key_c][key_n]["vals_r"])
2161 vals[key_c][key_n]["stdev_r"] = \
2162 round(stdev(vals[key_c][key_n]["vals_r"]) / 1000000, 1)
2163 if vals[key_c][key_n]["vals_c"]:
2164 vals[key_c][key_n]["nr_c"] = len(vals[key_c][key_n]["vals_c"])
2165 vals[key_c][key_n]["mean_c"] = \
2166 mean(vals[key_c][key_n]["vals_c"])
2167 vals[key_c][key_n]["stdev_c"] = \
2168 round(stdev(vals[key_c][key_n]["vals_c"]) / 1000000, 1)
2170 txt_nodes = list(set(txt_nodes))
2172 txt_chains = sorted(txt_chains, key=lambda chain: int(chain))
2173 txt_nodes = sorted(txt_nodes, key=lambda node: int(node))
2175 chains = [i + 1 for i in range(len(txt_chains))]
2176 nodes = [i + 1 for i in range(len(txt_nodes))]
2178 data_r = [list() for _ in range(len(chains))]
2179 data_c = [list() for _ in range(len(chains))]
2180 diff = [list() for _ in range(len(chains))]
2184 val_r = vals[txt_chains[c - 1]][txt_nodes[n - 1]]["mean_r"]
2185 except (KeyError, IndexError):
2188 val_c = vals[txt_chains[c - 1]][txt_nodes[n - 1]]["mean_c"]
2189 except (KeyError, IndexError):
2191 if val_c is not None and val_r:
2192 val_d = (val_c - val_r) * 100 / val_r
2196 if val_r is not None:
2197 val_r = round(val_r / 1000000, 1)
2198 data_r[c - 1].append(val_r)
2199 if val_c is not None:
2200 val_c = round(val_c / 1000000, 1)
2201 data_c[c - 1].append(val_c)
2202 if val_d is not None:
2203 val_d = int(round(val_d, 0))
2204 diff[c - 1].append(val_d)
2207 my_green = [[0.0, 'rgb(235, 249, 242)'],
2208 [1.0, 'rgb(45, 134, 89)']]
2210 my_blue = [[0.0, 'rgb(236, 242, 248)'],
2211 [1.0, 'rgb(57, 115, 172)']]
2213 my_grey = [[0.0, 'rgb(230, 230, 230)'],
2214 [1.0, 'rgb(102, 102, 102)']]
2218 annotations = list()
2219 annotations_r = list()
2220 annotations_c = list()
2221 annotations_diff = list()
2223 text = ("Test: {name}"
2224 "<br>{title_r}: {text_r}"
2225 "<br>{title_c}: {text_c}{text_diff}")
2226 text_r = "Thput: {val_r}; StDev: {stdev_r}; Runs: {nr_r}"
2227 text_c = "Thput: {val_c}; StDev: {stdev_c}; Runs: {nr_c}"
2228 text_diff = "<br>Relative Difference {title_c} vs. {title_r}: {diff}%"
2230 for c in range(len(txt_chains)):
2232 for n in range(len(txt_nodes)):
2248 point_text_r = "Not present"
2249 point_text_c = "Not present"
2250 point_text_diff = ""
2252 point_r = data_r[c][n]
2253 if point_r is not None:
2254 point_text_r = text_r.format(
2256 stdev_r=vals[txt_chains[c]][txt_nodes[n]]["stdev_r"],
2257 nr_r=vals[txt_chains[c]][txt_nodes[n]]["nr_r"])
2260 point["text"] = "" if point_r is None else point_r
2261 annotations_r.append(deepcopy(point))
2264 point_c = data_c[c][n]
2265 if point_c is not None:
2266 point_text_c = text_c.format(
2268 stdev_c=vals[txt_chains[c]][txt_nodes[n]]["stdev_c"],
2269 nr_c=vals[txt_chains[c]][txt_nodes[n]]["nr_c"])
2272 point["text"] = "" if point_c is None else point_c
2273 annotations_c.append(deepcopy(point))
2276 point_d = diff[c][n]
2277 if point_d is not None:
2278 point_text_diff = text_diff.format(
2279 title_r=plot["reference"]["name"],
2280 title_c=plot["compare"]["name"],
2284 point["text"] = "" if point_d is None else point_d
2285 annotations_diff.append(deepcopy(point))
2288 name = vals[txt_chains[c]][txt_nodes[n]]["name"]
2292 hover_line.append(text.format(
2294 title_r=plot["reference"]["name"],
2295 text_r=point_text_r,
2296 title_c=plot["compare"]["name"],
2297 text_c=point_text_c,
2298 text_diff=point_text_diff
2301 hovertext.append(hover_line)
2304 plgo.Heatmap(x=nodes,
2309 title=plot.get("z-axis", ""),
2323 colorscale=my_green,
2327 plgo.Heatmap(x=nodes,
2332 title=plot.get("z-axis", ""),
2350 plgo.Heatmap(x=nodes,
2356 title="Relative Difference {name_c} vs. {name_r} [%]".
2357 format(name_c=plot["compare"]["name"],
2358 name_r=plot["reference"]["name"]),
2378 for idx, item in enumerate(txt_nodes):
2380 annotations.append(dict(
2394 for idx, item in enumerate(txt_chains):
2396 annotations.append(dict(
2411 annotations.append(dict(
2418 text=plot.get("x-axis", ""),
2426 annotations.append(dict(
2433 text=plot.get("y-axis", ""),
2441 updatemenus = list([
2451 label=plot["reference"]["name"],
2455 "visible": [True, False, False]
2458 "colorscale": [my_green, ],
2459 "reversescale": False,
2460 "annotations": annotations + annotations_r,
2465 label=plot["compare"]["name"],
2469 "visible": [False, True, False]
2472 "colorscale": [my_blue, ],
2473 "reversescale": False,
2474 "annotations": annotations + annotations_c,
2483 "visible": [False, False, True]
2486 "colorscale": [my_grey, ],
2487 "reversescale": False,
2488 "annotations": annotations + annotations_diff,
2497 layout = deepcopy(plot["layout"])
2498 except KeyError as err:
2499 logging.error("Finished with error: No layout defined")
2500 logging.error(repr(err))
2503 layout["annotations"] = annotations + annotations_r
2504 layout['updatemenus'] = updatemenus
2508 plpl = plgo.Figure(data=traces, layout=layout)
2511 logging.info(" Writing file '{0}{1}'.".
2512 format(plot["output-file"], plot["output-file-type"]))
2513 ploff.plot(plpl, show_link=False, auto_open=False,
2514 filename='{0}{1}'.format(plot["output-file"],
2515 plot["output-file-type"]))
2516 except PlotlyError as err:
2517 logging.error(" Finished with error: {}".
2518 format(str(err).replace("\n", " ")))