blob: 857a59401e23c51e96d433d39e50120f212ba682 [file] [log] [blame]
kdanylov aka koder3a9e5db2017-05-09 20:00:44 +03001import logging
kdanylov aka koderb0833332017-05-13 20:39:17 +03002from typing import List
kdanylov aka koder3a9e5db2017-05-09 20:00:44 +03003
4import numpy
kdanylov aka koder3a9e5db2017-05-09 20:00:44 +03005
6# to make seaborn styles available
7import warnings
8with warnings.catch_warnings():
9 warnings.simplefilter("ignore")
10 import seaborn
11
kdanylov aka koderb0833332017-05-13 20:39:17 +030012from cephlib.units import unit_conversion_coef_f
13from cephlib.plot import PlotParams, provide_plot
kdanylov aka koder3a9e5db2017-05-09 20:00:44 +030014
kdanylov aka koderb0833332017-05-13 20:39:17 +030015from .report_profiles import StyleProfile
kdanylov aka koder3a9e5db2017-05-09 20:00:44 +030016from .resources import IOSummary
17
18
19logger = logging.getLogger("wally")
20
21
kdanylov aka koder3a9e5db2017-05-09 20:00:44 +030022@provide_plot(eng=False, no_legend=True, grid='y', style_name='ioqd', noadjust=True)
23def io_chart(pp: PlotParams,
24 legend: str,
25 iosums: List[IOSummary],
26 iops_log_spine: bool = False,
27 lat_log_spine: bool = False) -> None:
28
29 # -------------- MAGIC VALUES ---------------------
30 # IOPS bar width
31 width = 0.2
32
33 # offset from center of bar to deviation/confidence range indicator
34 err_x_offset = 0.03
35
36 # extra space on top and bottom, comparing to maximal tight layout
37 extra_y_space = 0.05
38
39 # additional spine for BW/IOPS on left side of plot
40 extra_io_spine_x_offset = -0.1
41
42 # extra space on left and right sides
43 extra_x_space = 0.5
44
45 # legend location settings
46 legend_location = "center left"
47 legend_bbox_to_anchor = (1.1, 0.81)
48
49 # -------------- END OF MAGIC VALUES ---------------------
50
51 block_size = iosums[0].block_size
52 xpos = numpy.arange(1, len(iosums) + 1, dtype='uint')
53
kdanylov aka koderb0833332017-05-13 20:39:17 +030054 coef_mb = unit_conversion_coef_f(iosums[0].bw.units, "MiBps")
55 coef_iops = unit_conversion_coef_f(iosums[0].bw.units, "KiBps") / block_size
kdanylov aka koder3a9e5db2017-05-09 20:00:44 +030056
57 iops_primary = block_size < pp.style.large_blocks
58
59 coef = coef_iops if iops_primary else coef_mb
60 pp.ax.set_ylabel("IOPS" if iops_primary else "BW (MiBps)")
61
62 vals = [iosum.bw.average * coef for iosum in iosums]
63
64 # set correct x limits for primary IO spine
65 min_io = min(iosum.bw.average - iosum.bw.deviation * pp.style.dev_range_x for iosum in iosums)
66 max_io = max(iosum.bw.average + iosum.bw.deviation * pp.style.dev_range_x for iosum in iosums)
67 border = (max_io - min_io) * extra_y_space
68 io_lims = (min_io - border, max_io + border)
69
70 pp.ax.set_ylim(io_lims[0] * coef, io_lims[-1] * coef)
71 pp.ax.bar(xpos - width / 2, vals, width=width, color=pp.colors.box_color, label=legend)
72
73 # plot deviation and confidence error ranges
74 err1_legend = err2_legend = None
75 for pos, iosum in zip(xpos, iosums):
76 dev_bar_pos = pos - err_x_offset
77 err1_legend = pp.ax.errorbar(dev_bar_pos,
78 iosum.bw.average * coef,
79 iosum.bw.deviation * pp.style.dev_range_x * coef,
80 alpha=pp.colors.subinfo_alpha,
81 color=pp.colors.suppl_color1) # 'magenta'
82
83 conf_bar_pos = pos + err_x_offset
84 err2_legend = pp.ax.errorbar(conf_bar_pos,
85 iosum.bw.average * coef,
86 iosum.bw.confidence * coef,
87 alpha=pp.colors.subinfo_alpha,
88 color=pp.colors.suppl_color2) # 'teal'
89
90 handles1, labels1 = pp.ax.get_legend_handles_labels()
91
92 handles1 += [err1_legend, err2_legend]
93 labels1 += ["{}% dev".format(pp.style.dev_perc),
94 "{}% conf".format(int(100 * iosums[0].bw.confidence_level))]
95
96 # extra y spine for latency on right side
97 ax2 = pp.ax.twinx()
98
99 # plot median and 95 perc latency
kdanylov aka koderb0833332017-05-13 20:39:17 +0300100 lat_coef_ms = unit_conversion_coef_f(iosums[0].lat.units, "ms")
kdanylov aka koder3a9e5db2017-05-09 20:00:44 +0300101 ax2.plot(xpos, [iosum.lat.perc_50 * lat_coef_ms for iosum in iosums], label="lat med")
102 ax2.plot(xpos, [iosum.lat.perc_95 * lat_coef_ms for iosum in iosums], label="lat 95%")
103
104 for grid_line in ax2.get_ygridlines():
105 grid_line.set_linestyle(":")
106
107 # extra y spine for BW/IOPS on left side
108 if pp.style.extra_io_spine:
109 ax3 = pp.ax.twinx()
110 if iops_log_spine:
111 ax3.set_yscale('log')
112
113 ax3.set_ylabel("BW (MiBps)" if iops_primary else "IOPS")
114 secondary_coef = coef_mb if iops_primary else coef_iops
115 ax3.set_ylim(io_lims[0] * secondary_coef, io_lims[1] * secondary_coef)
116 ax3.spines["left"].set_position(("axes", extra_io_spine_x_offset))
117 ax3.spines["left"].set_visible(True)
118 ax3.yaxis.set_label_position('left')
119 ax3.yaxis.set_ticks_position('left')
120 else:
121 ax3 = None
122
123 ax2.set_ylabel("Latency (ms)")
124
125 # legend box
126 handles2, labels2 = ax2.get_legend_handles_labels()
127 pp.ax.legend(handles1 + handles2, labels1 + labels2, loc=legend_location, bbox_to_anchor=legend_bbox_to_anchor)
128
129 # limit and label x spine
130 pp.ax.set_xlim(extra_x_space, len(iosums) + extra_x_space)
131 pp.ax.set_xticks(xpos)
132 pp.ax.set_xticklabels(["{0}*{1}={2}".format(iosum.qd, iosum.nodes_count, iosum.qd * iosum.nodes_count)
133 for iosum in iosums],
134 rotation=30 if len(iosums) > 9 else 0)
135 pp.ax.set_xlabel("IO queue depth * test node count = total parallel requests")
136
137 # apply log scales for X spines, if set
138 if iops_log_spine:
139 pp.ax.set_yscale('log')
140
141 if lat_log_spine:
142 ax2.set_yscale('log')
143
144 # override some styles
145 pp.fig.set_size_inches(*pp.style.qd_chart_inches)
146 pp.fig.subplots_adjust(right=StyleProfile.subplot_adjust_r)
147
148 if pp.style.extra_io_spine:
149 ax3.grid(False)
150