benchmark-result-collector/make_graphs.py

401 lines
11 KiB
Python
Raw Permalink Normal View History

2020-10-23 02:00:42 +02:00
#!/usr/bin/env python3
"""
Python module to automatically analyze benchmark results
"""
import csv
import os
import abc
2021-07-14 11:07:12 +02:00
import re
2020-10-23 02:56:00 +02:00
import datetime
2021-07-14 11:07:12 +02:00
import time
2020-10-23 02:00:42 +02:00
import matplotlib.pyplot as pplot
2021-07-14 11:07:12 +02:00
EPOCHRE = re.compile(r"\[[0-9]+\.[0-9]+]")
TIMERE = re.compile(r"time=[0-9]+\.[0-9]+ ms")
NUMBERRE = re.compile(r"[0-9]+\.[0-9]+")
2020-10-23 02:00:42 +02:00
def average(lst: list) -> float:
"""
Agerage of list
:param lst:
:return:
"""
return sum(lst) / len(lst)
def keychars(x):
"""
Return hey partnumber
:param x:
:return:
"""
return int(x.split('.')[1])
class Analyzer(abc.ABC):
"""
Very abstract analyzer
"""
def __init__(self, typeof='.csv'):
self.type = typeof
2021-07-14 11:07:12 +02:00
self.responsespersec = []
self.latencypersec = []
2020-10-23 02:00:42 +02:00
def getfiles(self, directory: str = '.') -> list:
"""
Returns a list of csv files in directory
:param directory:
:return:
"""
return [(directory + '/' + filename) for filename in os.listdir(directory) if filename.endswith(self.type)]
def processfile(
self,
fname: str,
shouldprint: bool = False) -> None:
pass
def processallfiles(self, directory: str = '.') -> None:
"""
Process all files in a directory
:param directory:
:return:
"""
files = sorted(self.getfiles(directory), key=keychars)
for f in files:
self.processfile(f, False)
class CsvAnalyzer(Analyzer):
"""
Abstract CSV analyzer
"""
def __init__(self):
"""
Init object
"""
super().__init__()
def walkresponsepersec(
self,
responsepersec: dict,
shouldprint: bool) -> None:
"""
Walks through reponsepersec dict
:param responsepersec:
:param shouldprint:
:return:
"""
for sec in responsepersec:
if len(responsepersec[sec]) != 0:
self.responsespersec.append(len(responsepersec[sec]))
self.latencypersec.append(average(responsepersec[sec]))
if shouldprint:
print(len(responsepersec[sec]))
print(average(responsepersec[sec]))
2021-07-14 11:07:12 +02:00
class PingAnalyzer(Analyzer):
"""
Analyze ping command output.
"""
def __init__(self):
"""
Init object
"""
super().__init__(typeof='.pingtxt')
def walkresponsepersec(
self,
responsepersec: dict,
shouldprint: bool) -> None:
"""
Walks through reponsepersec dict
:param responsepersec:
:param shouldprint:
:return:
"""
for sec in responsepersec:
if len(responsepersec[sec]) != 0:
self.responsespersec.append(len(responsepersec[sec]))
self.latencypersec.append(average(responsepersec[sec]))
if shouldprint:
print(len(responsepersec[sec]))
print(average(responsepersec[sec]))
def processfile(
self,
fname,
shouldprint: bool = False):
"""
Process a single file.
:param fname:
:param shouldprint:
:return:
"""
with open(fname, 'r') as f:
lines = f.readlines()
responsepersec = {}
for line in lines:
try:
epoch = float(NUMBERRE.findall(EPOCHRE.findall(line)[0])[0])
epoch_time = time.strftime("%H:%M:%S", time.localtime(epoch))
delay = float(NUMBERRE.findall(TIMERE.findall(line)[0])[0])
if epoch_time not in responsepersec:
responsepersec[epoch_time] = []
responsepersec[epoch_time].append(delay)
except Exception as e:
print(f"line skipped because {e}")
self.walkresponsepersec(responsepersec, shouldprint)
2020-10-23 02:00:42 +02:00
class HeyAnalyzer(CsvAnalyzer):
"""
Analyze hey benchmark output.
"""
def __init__(self):
"""
Init object
"""
super().__init__()
def processfile(
self,
fname,
shouldprint: bool = False):
"""
Process a single file.
:param fname:
:param shouldprint:
:return:
"""
with open(fname, 'r') as f:
data = csv.reader(f)
fields = next(data)
responsepersec = {}
for row in data:
items = zip(fields, row)
item = {}
for (name, value) in items:
item[name] = value.strip()
sec = int(item['offset'].split('.')[0])
if sec not in responsepersec:
responsepersec[sec] = []
2021-07-14 11:07:12 +02:00
responsepersec[sec].append(float(item['response-time']))
2020-10-23 02:00:42 +02:00
self.walkresponsepersec(responsepersec, shouldprint)
2020-10-23 02:56:00 +02:00
class BirbAnalyzer(CsvAnalyzer):
"""
Analyze Birbnetes benchmark output.
"""
def __init__(self):
"""
Init object
"""
super().__init__()
def processfile(
self,
fname,
shouldprint: bool = False):
"""
Process a single file.
:param fname:
:param shouldprint:
:return:
"""
with open(fname, 'r') as f:
data = csv.reader(f)
fields = next(data)
responsepersec = {}
for row in data:
items = zip(fields, row)
item = {}
for (name, value) in items:
item[name] = value.strip()
2020-10-23 19:07:17 +02:00
sec = datetime.datetime.strptime(item['mqtt_arrive_time'], '%Y-%m-%dT%H:%M:%S.%f').strftime("%H:%M:%S")
2020-10-23 02:56:00 +02:00
if sec not in responsepersec:
responsepersec[sec] = []
2021-07-14 11:07:12 +02:00
responsepersec[sec].append(float(item['rtt']))
2020-10-23 02:56:00 +02:00
self.walkresponsepersec(responsepersec, shouldprint)
class InputAnalyzer(CsvAnalyzer):
"""
Analyze InputService benchmark output.
"""
def __init__(self):
"""
Init object
"""
super().__init__()
def processfile(
self,
fname,
shouldprint: bool = False):
"""
Process a single file.
:param fname:
:param shouldprint:
:return:
"""
with open(fname, 'r') as f:
data = csv.reader(f)
fields = next(data)
responsepersec = {}
for row in data:
items = zip(fields, row)
item = {}
for (name, value) in items:
item[name] = value.strip()
2020-10-23 18:37:18 +02:00
sec = datetime.datetime.strptime(item['http_start_time'], '%Y-%m-%dT%H:%M:%S.%f').strftime("%H:%M:%S")
2020-10-23 02:56:00 +02:00
if sec not in responsepersec:
responsepersec[sec] = []
2021-07-14 11:07:12 +02:00
firetime = datetime.datetime.strptime(item['http_start_time'], '%Y-%m-%dT%H:%M:%S.%f')
responsearrivetime = datetime.datetime.strptime(item['http_complete_time'],
"%Y-%m-%dT%H:%M:%S.%f")
latency = responsearrivetime - firetime
responsepersec[sec].append(int(latency.total_seconds() * 1000))
2020-10-23 02:56:00 +02:00
self.walkresponsepersec(responsepersec, shouldprint)
2020-10-23 02:00:42 +02:00
class ChartCreator:
"""
Create charts automagically
"""
@staticmethod
2021-07-14 11:07:12 +02:00
def savetxtplot(csvfile: Analyzer, directory) -> None:
2020-10-23 02:00:42 +02:00
"""
Save raw data to txt
:param directory:
:param csvfile:
:return:
"""
with open(os.getenv('TEXTDIR', default='.') + '/' + directory + "-rps.txt", 'w') as f:
for item in csvfile.responsespersec:
f.write("%s\n" % item)
with open(os.getenv('TEXTDIR', default='.') + '/' + directory + "-latency.txt", 'w') as f:
for item in csvfile.latencypersec:
f.write("%s\n" % item)
@staticmethod
2021-07-14 11:07:12 +02:00
def savecsvplot(csvfile: Analyzer, directory) -> None:
2020-10-23 02:00:42 +02:00
"""
Save plot of csv file
:param csvfile:
:param directory:
:return:
"""
pplot.plot(csvfile.responsespersec)
pplot.title(directory)
pplot.xlabel("Time (seconds)")
pplot.ylabel("Response/sec")
pplot.savefig(
os.getenv(
'CHARTDIR',
default='.') +
'/' +
directory +
"-rps.png")
pplot.clf()
pplot.plot(csvfile.latencypersec)
pplot.title(directory)
pplot.xlabel("Time (seconds)")
pplot.ylabel("Response time (milliseconds)")
pplot.savefig(
os.getenv(
'CHARTDIR',
default='.') +
'/' +
directory +
"-latency.png")
pplot.clf()
@staticmethod
def analyze_hey(abs_directory, directory):
"""
Analyze hey output
:param abs_directory:
:param directory:
:return:
"""
hey = HeyAnalyzer()
hey.processallfiles(abs_directory)
ChartCreator.savecsvplot(hey, directory)
ChartCreator.savetxtplot(hey, directory)
2020-10-23 02:56:00 +02:00
@staticmethod
def analyze_birb(abs_directory, directory):
"""
Analyze BirbBench output
:param abs_directory:
:param directory:
:return:
"""
birb = BirbAnalyzer()
birb.processallfiles(abs_directory)
ChartCreator.savecsvplot(birb, directory)
ChartCreator.savetxtplot(birb, directory)
2021-07-14 11:07:12 +02:00
@staticmethod
def analyze_ping(abs_directory, directory):
"""
Analyze ping command output
:param abs_directory:
:param directory:
:return:
"""
ping = PingAnalyzer()
ping.processallfiles(abs_directory)
ChartCreator.savecsvplot(ping, directory)
ChartCreator.savetxtplot(ping, directory)
2020-10-23 02:56:00 +02:00
@staticmethod
def analyze_input(abs_directory, directory):
"""
Analyze InputSvc output
:param abs_directory:
:param directory:
:return:
"""
inputsvc = InputAnalyzer()
inputsvc.processallfiles(abs_directory)
ChartCreator.savecsvplot(inputsvc, directory + "input")
ChartCreator.savetxtplot(inputsvc, directory + "input")
2020-10-23 02:00:42 +02:00
def doallruns(self):
"""
Process all directories in repo
:return:
"""
dirs = next(os.walk(os.getenv('SEARCHDIR', default='.')))[1]
for directory in dirs:
abs_directory = os.getenv(
'SEARCHDIR', default='.') + '/' + directory
print(abs_directory)
2020-10-23 18:23:59 +02:00
if 'HEY' in abs_directory.upper():
2021-07-14 11:07:12 +02:00
ChartCreator.analyze_hey(abs_directory, directory)
2020-10-23 02:56:00 +02:00
else:
2021-07-14 11:07:12 +02:00
ChartCreator.analyze_ping(abs_directory, directory)
2020-10-23 02:00:42 +02:00
if __name__ == "__main__":
"""
Entry point
"""
chartcreator = ChartCreator()
chartcreator.doallruns()