koder aka kdanilov | 94e3a2c | 2015-03-27 11:36:34 +0200 | [diff] [blame] | 1 | import sys |
| 2 | import math |
| 3 | import itertools |
| 4 | |
| 5 | from colorama import Fore, Style |
| 6 | |
| 7 | |
| 8 | def med_dev(vals): |
| 9 | med = sum(vals) / len(vals) |
| 10 | dev = ((sum(abs(med - i) ** 2 for i in vals) / len(vals)) ** 0.5) |
| 11 | return int(med), int(dev) |
| 12 | |
| 13 | |
| 14 | def round_deviation(med_dev): |
| 15 | med, dev = med_dev |
| 16 | |
| 17 | if dev < 1E-7: |
| 18 | return med_dev |
| 19 | |
| 20 | dev_div = 10.0 ** (math.floor(math.log10(dev)) - 1) |
| 21 | dev = int(dev / dev_div) * dev_div |
| 22 | med = int(med / dev_div) * dev_div |
| 23 | return (type(med_dev[0])(med), |
| 24 | type(med_dev[1])(dev)) |
| 25 | |
| 26 | |
| 27 | def groupby_globally(data, key_func): |
| 28 | grouped = {} |
| 29 | grouped_iter = itertools.groupby(data, key_func) |
| 30 | |
Ved-vampir | 0d0740c | 2015-04-06 15:39:33 +0300 | [diff] [blame] | 31 | for (bs, cache_tp, act), curr_data_it in grouped_iter: |
| 32 | key = (bs, cache_tp, act) |
koder aka kdanilov | 94e3a2c | 2015-03-27 11:36:34 +0200 | [diff] [blame] | 33 | grouped.setdefault(key, []).extend(curr_data_it) |
| 34 | |
| 35 | return grouped |
| 36 | |
| 37 | |
| 38 | class Data(object): |
| 39 | def __init__(self, name): |
| 40 | self.name = name |
| 41 | self.series = {} |
| 42 | self.processed_series = {} |
| 43 | |
| 44 | |
| 45 | def process_inplace(data): |
| 46 | processed = {} |
| 47 | for key, values in data.series.items(): |
| 48 | processed[key] = round_deviation(med_dev(values)) |
| 49 | data.processed_series = processed |
| 50 | |
| 51 | |
| 52 | def diff_table(*datas): |
| 53 | res_table = {} |
| 54 | |
| 55 | for key in datas[0].processed_series: |
| 56 | baseline = datas[0].processed_series[key] |
| 57 | base_max = baseline[0] + baseline[1] |
| 58 | base_min = baseline[0] - baseline[1] |
| 59 | |
| 60 | res_line = [baseline] |
| 61 | |
| 62 | for data in datas[1:]: |
| 63 | val, dev = data.processed_series[key] |
| 64 | val_min = val - dev |
| 65 | val_max = val + dev |
| 66 | |
| 67 | diff_1 = int(float(val_min - base_max) / base_max * 100) |
| 68 | diff_2 = int(float(val_max - base_min) / base_max * 100) |
| 69 | |
| 70 | diff_max = max(diff_1, diff_2) |
| 71 | diff_min = min(diff_1, diff_2) |
| 72 | |
| 73 | res_line.append((diff_max, diff_min)) |
| 74 | res_table[key] = res_line |
| 75 | |
| 76 | return [data.name for data in datas], res_table |
| 77 | |
| 78 | |
| 79 | def print_table(headers, table): |
| 80 | lines = [] |
| 81 | items = sorted(table.items()) |
| 82 | lines.append([(len(i), i) for i in [""] + headers]) |
| 83 | item_frmt = "{0}{1:>4}{2} ~ {3}{4:>4}{5}" |
| 84 | |
| 85 | for key, vals in items: |
| 86 | ln1 = "{0:>4} {1} {2:>9} {3}".format(*map(str, key)) |
| 87 | ln2 = "{0:>4} ~ {1:>3}".format(*vals[0]) |
| 88 | |
| 89 | line = [(len(ln1), ln1), (len(ln2), ln2)] |
| 90 | |
| 91 | for idx, val in enumerate(vals[1:], 2): |
| 92 | cval = [] |
| 93 | for vl in val: |
| 94 | if vl < -10: |
| 95 | cval.extend([Fore.RED, vl, Style.RESET_ALL]) |
| 96 | elif vl > 10: |
| 97 | cval.extend([Fore.GREEN, vl, Style.RESET_ALL]) |
| 98 | else: |
| 99 | cval.extend(["", vl, ""]) |
| 100 | |
| 101 | ln = len(item_frmt.format("", cval[1], "", "", cval[4], "")) |
| 102 | line.append((ln, item_frmt.format(*cval))) |
| 103 | |
| 104 | lines.append(line) |
| 105 | |
| 106 | max_columns_with = [] |
| 107 | for idx in range(len(lines[0])): |
| 108 | max_columns_with.append( |
| 109 | max(line[idx][0] for line in lines)) |
| 110 | |
| 111 | sep = '-' * (4 + sum(max_columns_with) + 3 * (len(lines[0]) - 1)) |
| 112 | |
| 113 | print sep |
| 114 | for idx, line in enumerate(lines): |
| 115 | cline = [] |
| 116 | for (curr_len, txt), exp_ln in zip(line, max_columns_with): |
| 117 | cline.append(" " * (exp_ln - curr_len) + txt) |
| 118 | print "| " + " | ".join(cline) + " |" |
| 119 | if 0 == idx: |
| 120 | print sep |
| 121 | print sep |
| 122 | |
| 123 | |
| 124 | def key_func(x): |
| 125 | return (x['__meta__']['blocksize'], |
Ved-vampir | 0d0740c | 2015-04-06 15:39:33 +0300 | [diff] [blame] | 126 | 'd' if 'direct' in x['__meta__'] else 's', |
| 127 | x['__meta__']['name']) |
koder aka kdanilov | 94e3a2c | 2015-03-27 11:36:34 +0200 | [diff] [blame] | 128 | |
| 129 | |
| 130 | template = "{bs:>4} {action:>12} {cache_tp:>3} {conc:>4}" |
| 131 | template += " | {iops[0]:>6} ~ {iops[1]:>5} | {bw[0]:>7} ~ {bw[1]:>6}" |
| 132 | template += " | {lat[0]:>6} ~ {lat[1]:>5} |" |
| 133 | |
| 134 | headers = dict(bs="BS", |
| 135 | action="operation", |
| 136 | cache_tp="S/D", |
| 137 | conc="CONC", |
| 138 | iops=("IOPS", "dev"), |
| 139 | bw=("BW kBps", "dev"), |
| 140 | lat=("LAT ms", "dev")) |
| 141 | |
| 142 | |
| 143 | def load_io_py_file(fname): |
| 144 | with open(fname) as fc: |
| 145 | block = None |
| 146 | for line in fc: |
Ved-vampir | 0d0740c | 2015-04-06 15:39:33 +0300 | [diff] [blame] | 147 | if line.startswith("{"): |
koder aka kdanilov | 94e3a2c | 2015-03-27 11:36:34 +0200 | [diff] [blame] | 148 | block = line |
| 149 | elif block is not None: |
| 150 | block += line |
| 151 | |
| 152 | if block is not None and block.count('}') == block.count('{'): |
Ved-vampir | 0d0740c | 2015-04-06 15:39:33 +0300 | [diff] [blame] | 153 | cut = block.rfind('}') |
| 154 | block = block[0:cut+1] |
koder aka kdanilov | 94e3a2c | 2015-03-27 11:36:34 +0200 | [diff] [blame] | 155 | yield eval(block) |
| 156 | block = None |
| 157 | |
| 158 | if block is not None and block.count('}') == block.count('{'): |
| 159 | yield eval(block) |
| 160 | |
| 161 | |
| 162 | def main(argv): |
| 163 | items = [] |
| 164 | CONC_POS = 3 |
| 165 | for hdr_fname in argv[1:]: |
| 166 | hdr, fname = hdr_fname.split("=", 1) |
| 167 | data = list(load_io_py_file(fname)) |
| 168 | item = Data(hdr) |
| 169 | for key, vals in groupby_globally(data, key_func).items(): |
| 170 | item.series[key] = [val['iops'] * key[CONC_POS] for val in vals] |
| 171 | process_inplace(item) |
| 172 | items.append(item) |
| 173 | |
| 174 | print_table(*diff_table(*items)) |
| 175 | |
| 176 | # print template.format(**headers) |
| 177 | |
| 178 | # for (bs, cache_tp, act, conc), curr_data in sorted(grouped.items()): |
| 179 | # iops = med_dev([i['iops'] * int(conc) for i in curr_data]) |
koder aka kdanilov | 4e9f3ed | 2015-04-14 11:26:12 +0300 | [diff] [blame] | 180 | # bw = med_dev([i['bw'] * int(conc) for i in curr_data]) |
koder aka kdanilov | 94e3a2c | 2015-03-27 11:36:34 +0200 | [diff] [blame] | 181 | # lat = med_dev([i['lat'] / 1000 for i in curr_data]) |
| 182 | |
| 183 | # iops = round_deviation(iops) |
koder aka kdanilov | 4e9f3ed | 2015-04-14 11:26:12 +0300 | [diff] [blame] | 184 | # bw = round_deviation(bw) |
koder aka kdanilov | 94e3a2c | 2015-03-27 11:36:34 +0200 | [diff] [blame] | 185 | # lat = round_deviation(lat) |
| 186 | |
| 187 | # params = dict( |
| 188 | # bs=bs, |
| 189 | # action=act, |
| 190 | # cache_tp=cache_tp, |
| 191 | # iops=iops, |
koder aka kdanilov | 4e9f3ed | 2015-04-14 11:26:12 +0300 | [diff] [blame] | 192 | # bw=bw, |
koder aka kdanilov | 94e3a2c | 2015-03-27 11:36:34 +0200 | [diff] [blame] | 193 | # lat=lat, |
| 194 | # conc=conc |
| 195 | # ) |
| 196 | |
| 197 | # print template.format(**params) |
| 198 | |
| 199 | |
| 200 | if __name__ == "__main__": |
| 201 | exit(main(sys.argv)) |
| 202 | |
| 203 | # vals = [(123, 23), (125678, 5678), (123.546756, 23.77), |
| 204 | # (123.546756, 102.77), (0.1234, 0.0224), |
| 205 | # (0.001234, 0.000224), (0.001234, 0.0000224)] |
| 206 | # for val in : |
| 207 | # print val, "=>", round_deviation(val) |