kdanylov aka koder | 3a9e5db | 2017-05-09 20:00:44 +0300 | [diff] [blame] | 1 | import logging |
kdanylov aka koder | b083333 | 2017-05-13 20:39:17 +0300 | [diff] [blame] | 2 | from typing import List |
kdanylov aka koder | 3a9e5db | 2017-05-09 20:00:44 +0300 | [diff] [blame] | 3 | |
| 4 | import numpy |
kdanylov aka koder | 3a9e5db | 2017-05-09 20:00:44 +0300 | [diff] [blame] | 5 | |
kdanylov aka koder | b083333 | 2017-05-13 20:39:17 +0300 | [diff] [blame] | 6 | from cephlib.units import unit_conversion_coef_f |
| 7 | from cephlib.plot import PlotParams, provide_plot |
kdanylov aka koder | 3a9e5db | 2017-05-09 20:00:44 +0300 | [diff] [blame] | 8 | |
kdanylov aka koder | 3a9e5db | 2017-05-09 20:00:44 +0300 | [diff] [blame] | 9 | from .resources import IOSummary |
| 10 | |
| 11 | |
| 12 | logger = logging.getLogger("wally") |
| 13 | |
| 14 | |
kdanylov aka koder | 3a9e5db | 2017-05-09 20:00:44 +0300 | [diff] [blame] | 15 | @provide_plot(eng=False, no_legend=True, grid='y', style_name='ioqd', noadjust=True) |
| 16 | def 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 koder | b083333 | 2017-05-13 20:39:17 +0300 | [diff] [blame] | 47 | 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 koder | 3a9e5db | 2017-05-09 20:00:44 +0300 | [diff] [blame] | 49 | |
| 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 koder | b083333 | 2017-05-13 20:39:17 +0300 | [diff] [blame] | 93 | lat_coef_ms = unit_conversion_coef_f(iosums[0].lat.units, "ms") |
kdanylov aka koder | 3a9e5db | 2017-05-09 20:00:44 +0300 | [diff] [blame] | 94 | 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 koder | 470a8fa | 2017-07-14 21:07:58 +0300 | [diff] [blame^] | 124 | pp.ax.set_ylim(bottom=0) |
kdanylov aka koder | 3a9e5db | 2017-05-09 20:00:44 +0300 | [diff] [blame] | 125 | 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 koder | 026e5f2 | 2017-05-15 01:04:39 +0300 | [diff] [blame] | 140 | pp.fig.subplots_adjust(right=pp.style.subplot_adjust_r) |
kdanylov aka koder | 3a9e5db | 2017-05-09 20:00:44 +0300 | [diff] [blame] | 141 | |
| 142 | if pp.style.extra_io_spine: |
| 143 | ax3.grid(False) |
| 144 | |