xref: /XiangShan/scripts/rolling/rolling.py (revision 7cf78eb25d45a53e97cb143ddd0185eddc10672c)
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')