mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 02:48:38 -04:00
110 lines
4 KiB
Python
110 lines
4 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""
|
||
|
|
Merge the CSVs written by scripts/api_perf.py into one cross-host table.
|
||
|
|
|
||
|
|
python3 scripts/api_perf_compare.py perf_results/*.csv
|
||
|
|
python3 scripts/api_perf_compare.py perf_results/*.csv --sort hot --top 25
|
||
|
|
python3 scripts/api_perf_compare.py perf_results/*.csv --markdown > perf_results/summary.md
|
||
|
|
|
||
|
|
For each endpoint and host prints cold (ms, first request after an API restart) and hot median
|
||
|
|
(ms, over the hot passes). When several CSVs exist for the same host the newest one wins.
|
||
|
|
"""
|
||
|
|
import argparse
|
||
|
|
import csv
|
||
|
|
import statistics
|
||
|
|
from collections import defaultdict
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
|
||
|
|
def load(paths):
|
||
|
|
# host -> newest file
|
||
|
|
newest = {}
|
||
|
|
for p in paths:
|
||
|
|
host = p.stem.rsplit('_', 2)[0]
|
||
|
|
if host not in newest or p.stat().st_mtime > newest[host].stat().st_mtime:
|
||
|
|
newest[host] = p
|
||
|
|
data = defaultdict(lambda: defaultdict(lambda: dict(cold=None, cold_each=None, hot=[], status=set())))
|
||
|
|
order = []
|
||
|
|
for host, p in sorted(newest.items()):
|
||
|
|
with open(p) as f:
|
||
|
|
for row in csv.DictReader(f):
|
||
|
|
d = data[row['name']][host]
|
||
|
|
ms = float(row['ms'])
|
||
|
|
if row['phase'] == 'cold':
|
||
|
|
d['cold'] = ms
|
||
|
|
elif row['phase'] == 'cold-each':
|
||
|
|
d['cold_each'] = ms
|
||
|
|
elif row['phase'] == 'hot':
|
||
|
|
d['hot'].append(ms)
|
||
|
|
d['status'].add(row['status'])
|
||
|
|
if row['name'] not in order:
|
||
|
|
order.append(row['name'])
|
||
|
|
return sorted(newest), data, order
|
||
|
|
|
||
|
|
|
||
|
|
def fmt(v):
|
||
|
|
if v is None:
|
||
|
|
return '-'
|
||
|
|
return f'{v:,.0f}'
|
||
|
|
|
||
|
|
|
||
|
|
def main():
|
||
|
|
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||
|
|
ap.add_argument('csvs', nargs='+', type=Path)
|
||
|
|
ap.add_argument('--sort', choices=['catalog', 'hot', 'cold'], default='catalog',
|
||
|
|
help='catalog order (default), or by worst hot median / worst cold across hosts.')
|
||
|
|
ap.add_argument('--top', type=int, help='Only print the first N rows after sorting.')
|
||
|
|
ap.add_argument('--markdown', action='store_true')
|
||
|
|
args = ap.parse_args()
|
||
|
|
|
||
|
|
hosts, data, order = load(args.csvs)
|
||
|
|
|
||
|
|
def hot_med(name, host):
|
||
|
|
h = data[name][host]['hot']
|
||
|
|
return statistics.median(h) if h else None
|
||
|
|
|
||
|
|
def worst(name, key):
|
||
|
|
vals = [data[name][h][key] if key == 'cold' else hot_med(name, h) for h in hosts]
|
||
|
|
vals = [v for v in vals if v is not None]
|
||
|
|
return max(vals) if vals else -1
|
||
|
|
|
||
|
|
names = list(order)
|
||
|
|
if args.sort == 'hot':
|
||
|
|
names.sort(key=lambda n: -worst(n, 'hot'))
|
||
|
|
elif args.sort == 'cold':
|
||
|
|
names.sort(key=lambda n: -worst(n, 'cold'))
|
||
|
|
if args.top:
|
||
|
|
names = names[:args.top]
|
||
|
|
|
||
|
|
cols = ['endpoint'] + [f'{h} cold' for h in hosts] + [f'{h} hot' for h in hosts]
|
||
|
|
rows = []
|
||
|
|
for n in names:
|
||
|
|
row = [n]
|
||
|
|
for h in hosts:
|
||
|
|
d = data[n][h]
|
||
|
|
c = d['cold_each'] if d['cold_each'] is not None else d['cold']
|
||
|
|
bad = d['status'] and d['status'] != {'200'}
|
||
|
|
row.append(fmt(c) + ('*' if d['cold_each'] is not None else '') + (' !' if bad else ''))
|
||
|
|
for h in hosts:
|
||
|
|
row.append(fmt(hot_med(n, h)))
|
||
|
|
rows.append(row)
|
||
|
|
|
||
|
|
if args.markdown:
|
||
|
|
print('| ' + ' | '.join(cols) + ' |')
|
||
|
|
print('|' + '|'.join(['---'] + ['---:'] * (len(cols) - 1)) + '|')
|
||
|
|
for r in rows:
|
||
|
|
print('| ' + ' | '.join(r) + ' |')
|
||
|
|
else:
|
||
|
|
widths = [max(len(str(x)) for x in col) for col in zip(cols, *rows)]
|
||
|
|
line = ' '.join(c.ljust(w) if i == 0 else c.rjust(w) for i, (c, w) in enumerate(zip(cols, widths)))
|
||
|
|
print(line)
|
||
|
|
print('-' * len(line))
|
||
|
|
for r in rows:
|
||
|
|
print(' '.join(c.ljust(w) if i == 0 else c.rjust(w) for i, (c, w) in enumerate(zip(r, widths))))
|
||
|
|
print('\nms; cold = first request after API restart (* = own restart via --cold-each); hot = median of hot passes;'
|
||
|
|
' ! = non-200 seen')
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == '__main__':
|
||
|
|
main()
|