1import sys 2import argparse 3import sqlite3 4import matplotlib.pyplot as plt 5import numpy as np 6 7 8# usage: single db file 9# python3 rolling.py plot DB_FILE_PATH [--perf-name PERF_NAME] [--aggregate AGGREGATE] [--interval INTERVAL] [--perf-file PERF_FILE] 10# 11# usage: diff mutiple db files 12# python3 rolling.py diff MUTI_DB_FILE_PATH [--perf-name PERF_NAME] [--aggregate AGGREGATE] [--interval INTERVAL] [--perf-file PERF_FILE] 13# 14# If you only observe one rolling counter, indicate the --perf-name parameter. 15# If you want to observe multiple at the same time, you can indicate the --perf-file parameter, 16# pointing to the path to a description file, each line in the file is a rolling counter, 17# and you can use the '//' comment at the beginning of the line to remove the unconcerned counter. 18# 19# Note that generally speaking, when observing multiple rolling counters, 20# the meaning of the x-axis needs to be the same, then you can use the intervalBased mode. 21# 22# If you want to compare multiple dbs to observe the difference between multiple runs, you can use diff mode. 23# This requires specifying the path of a description file. Each line in this description file contains a specific db path. 24# 25# eg. 26# exec emu twice with different parameters and obtained different db files (db0, db1). 27# want to observe the changes in IPC and prefetch accuracy. 28# create a file named db.txt: 29# path to db0 30# path to db1 31# create a file named perf.txt: 32# IPC 33# L1PrefetchAccuracy 34# run `python3 rolling.py diff db.txt --perf-file perf.txt -I (interval in RTL)` 35# eg. 36# want to observe the IPC rolling in single db (db0). 37# run `python3 rolling.py plot path-to-db0 --perf-name IPC` 38# 39 40 41class DataSet: 42 43 def __init__(self, db_path): 44 self.conn = sqlite3.connect(db_path) 45 self.cursor = self.conn.cursor() 46 self.xdata = [] 47 self.ydata = [] 48 49 def derive(self, perf_name, aggregate, clk_itval, hart): 50 sql = "SELECT xAxisPt, yAxisPt FROM {}_rolling_{}".format(perf_name, hart) 51 self.cursor.execute(sql) 52 result = self.cursor.fetchall() 53 aggcnt = 0 54 recordcnt = 0 55 aggydata = 0 56 aggxdata = 0 57 self.xdata = [] 58 self.ydata = [] 59 if clk_itval == -1: 60 # normal mode 61 # db log in normal mode: (xAxis, ydata) 62 # xAxis is current position in X Axis, ydata is the Increment value between this point and last point 63 for row in result: 64 aggcnt += 1 65 aggydata += row[1] 66 if aggcnt == aggregate: 67 self.xdata.append(row[0]) 68 self.ydata.append(aggydata/(row[0]-aggxdata)) 69 aggcnt = 0 70 aggydata = 0 71 aggxdata = row[0] 72 else: 73 # intervalBased mode, -I interval should be specified 74 # db log in intervalBased mode: (xdata, ydata) 75 # xdata, ydata in the Increment value in a certain interval 76 for row in result: 77 aggcnt += 1 78 aggxdata += row[0] 79 aggydata += row[1] 80 if aggcnt == aggregate: 81 self.xdata.append((clk_itval * aggregate) * (recordcnt + 1)) 82 self.ydata.append(0 if aggydata == 0 else aggxdata/aggydata) 83 aggcnt = 0 84 aggxdata = 0 85 aggydata = 0 86 recordcnt += 1 87 88 def plot(self, lb='PERF'): 89 plt.plot(self.xdata, self.ydata, lw=1, ls='-', label=lb) 90 91 def legend(): 92 plt.legend() 93 94 def show(): 95 plt.show() 96 97def err_exit(msg): 98 print(msg) 99 sys.exit(1) 100 101def check_args(args): 102 if args.aggregate <= 0: 103 err_exit("aggregation ratio must be no less than 1") 104 if not args.perf_name and not args.perf_file: 105 err_exit("should either specify perf-name or perf-file") 106 107def plot_dataset(path, perf_name, aggregate, clk_itval, perf_file, db_id=-1): 108 dataset = DataSet(path) 109 label = '_' + str(db_id) if db_id != -1 else '' 110 111 if perf_file: 112 with open(perf_file) as fp: 113 perfs = fp.readlines() 114 perfs = [perf.strip() for perf in perfs] 115 perfs = list(filter(lambda x: not x.startswith('//'), perfs)) 116 for perf in perfs: 117 dataset.derive(perf, aggregate, clk_itval, 0) 118 dataset.plot(perf + label) 119 else: 120 dataset.derive(perf_name, aggregate, clk_itval, 0) 121 dataset.plot(perf_name + label) 122 123def handle_plot(args): 124 check_args(args) 125 126 plot_dataset(args.db_path, args.perf_name, args.aggregate, args.interval, args.perf_file) 127 128 DataSet.legend() 129 DataSet.show() 130 131def handle_diff(args): 132 check_args(args) 133 134 db_path = args.db_path 135 136 with open(db_path) as fp: 137 for (idx, db) in enumerate(fp): 138 plot_dataset(db.strip(), args.perf_name, args.aggregate, args.interval, args.perf_file, idx) 139 140 DataSet.legend() 141 DataSet.show() 142 143if __name__ == "__main__": 144 parser = argparse.ArgumentParser(description="performance rolling plot script for xs") 145 subparsers = parser.add_subparsers(title='useful sub function', dest='subcommand', help='useful sub function') 146 147 # sub function for single db file 148 cmd1_parser = subparsers.add_parser('plot', help='for single db file') 149 cmd1_parser.add_argument('db_path', metavar='db_path', type=str, help='path to chiseldb file') 150 cmd1_parser.add_argument('--perf-name', default=None, type=str, help="name of the performance counter") 151 cmd1_parser.add_argument('--aggregate', '-A', default=1, type=int, help="aggregation ratio") 152 cmd1_parser.add_argument('--interval', '-I', default=-1, type=int, help="interval value in the interval based mode") 153 cmd1_parser.add_argument('--perf-file', '-F', default=None, type=str, help="path to a file including all interested performance counters") 154 155 # sub function for diff multiple db files 156 cmd2_parser = subparsers.add_parser('diff', help='for diff multiple db files') 157 cmd2_parser.add_argument('db_path', metavar='muti_db_path', type=str, help="path to a file including all path to chiseldb files") 158 cmd2_parser.add_argument('--perf-name', default=None, type=str, help="name of the performance counter") 159 cmd2_parser.add_argument('--aggregate', '-A', default=1, type=int, help="aggregation ratio") 160 cmd2_parser.add_argument('--interval', '-I', default=-1, type=int, help="interval value in the interval based mode") 161 cmd2_parser.add_argument('--perf-file', '-F', default=None, type=str, help="path to a file including all interested performance counters") 162 163 args = parser.parse_args() 164 165 if args.subcommand == 'plot': 166 handle_plot(args) 167 elif args.subcommand == 'diff': 168 handle_diff(args) 169 else: 170 err_exit('invalid command')