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