source: benchmark/plot.py @ 41a6a78

pthread-emulation
Last change on this file since 41a6a78 was 41a6a78, checked in by Thierry Delisle <tdelisle@…>, 4 months ago

It was brought to my attention that I forgot to commit this file

  • Property mode set to 100755
File size: 7.5 KB
Line 
1#!/usr/bin/python3
2"""
3Python Script to plot values obtained by the rmit.py script
4Runs a R.I.P.L.
5
6./plot.py
7-t trials
8-o option:values
9"""
10
11import argparse
12import itertools
13import json
14import math
15import numpy
16import os
17import re
18import statistics
19import sys
20import time
21
22import matplotlib
23import matplotlib.pyplot as plt
24from matplotlib.ticker import EngFormatter, ScalarFormatter
25
26def fmtDur( duration ):
27        if duration :
28                hours, rem = divmod(duration, 3600)
29                minutes, rem = divmod(rem, 60)
30                seconds, millis = divmod(rem, 1)
31                return "%2d:%02d.%03d" % (minutes, seconds, millis * 1000)
32        return " n/a"
33
34class Field:
35        def __init__(self, unit, _min, _log, _name=None, _factor=1.0):
36                self.unit = unit
37                self.min  = _min
38                self.log  = _log
39                self.name = _name
40                self.factor = _factor
41
42field_names = {
43        "ns per ops"            : Field('ns'    , 0, False),
44        "Number of processors"  : Field(''      , 1, "exact"),
45        "Ops per procs"         : Field('Ops'   , 0, False),
46        "Ops per threads"       : Field('Ops'   , 0, False),
47        "ns per ops/procs"      : Field(''      , 0, False, _name = "ns $\\times$ (Processor $/$ Total Ops)" ),
48        "Number of threads"     : Field(''      , 1, False),
49        "Total Operations(ops)" : Field('Ops'   , 0, False),
50        "Ops/sec/procs"         : Field('Ops'   , 0, False),
51        "Total blocks"          : Field('Blocks', 0, False),
52        "Ops per second"        : Field(''      , 0, False),
53        "Cycle size (# thrds)"  : Field('thrd'  , 1, False),
54        "Duration (ms)"         : Field('ms'    , 0, False),
55        "Target QPS"            : Field(''      , 0, False),
56        "Actual QPS"            : Field(''      , 0, False),
57        "Average Read Latency"  : Field('s'     , 0, False, _factor = 0.000001),
58        "Median Read Latency"   : Field('s'     , 0, True, _factor = 0.000001),
59        "Tail Read Latency"     : Field('s'     , 0, True, _factor = 0.000001),
60        "Average Update Latency": Field('s'     , 0, True, _factor = 0.000001),
61        "Median Update Latency" : Field('s'     , 0, True, _factor = 0.000001),
62        "Tail Update Latency"   : Field('s'     , 0, True, _factor = 0.000001),
63        "Update Ratio"          : Field('%'   , 0, False),
64        "Request Rate"          : Field('req/s' , 0, False),
65        "Data Rate"             : Field('b/s'   , 0, False, _factor = 1000 * 1000, _name = "Response Throughput"),
66        "Errors"                : Field('%'   , 0, False),
67}
68
69def plot(in_data, x, y, options, prefix):
70        fig, ax = plt.subplots()
71        colors = itertools.cycle(['#006cb4','#0aa000','#ff6600','#8510a1','#0095e3','#fd8f00','#e30002','#8f00d6','#4b009a','#ffff00','#69df00','#fb0300','#b13f00'])
72        series = {} # scatter data for each individual data point
73        groups = {} # data points for x value
74
75        print("Preparing Data")
76
77        for entry in in_data:
78                name = entry[0]
79                if options.filter and not name.startswith(options.filter):
80                        continue
81
82                if not name in series:
83                        series[name] = {'x':[], 'y':[]}
84
85                if not name in groups:
86                        groups[name] = {}
87
88                if x in entry[2] and y in entry[2]:
89                        xval = entry[2][x]
90                        yval = entry[2][y] * field_names[y].factor
91                        series[name]['x'].append(xval)
92                        series[name]['y'].append(yval)
93
94                        if not xval in groups[name]:
95                                groups[name][xval] = []
96
97                        groups[name][xval].append(yval)
98
99        print("Preparing Lines")
100
101        lines = {} # lines from groups with min, max, median, etc.
102        for name, data in groups.items():
103                if not name in lines:
104                        lines[name] = { 'x': [], 'min':[], 'max':[], 'med':[], 'avg':[] }
105
106                for xkey in sorted(data):
107                        ys = data[xkey]
108                        lines[name]['x']  .append(xkey)
109                        lines[name]['min'].append(min(ys))
110                        lines[name]['max'].append(max(ys))
111                        lines[name]['med'].append(statistics.median(ys))
112                        lines[name]['avg'].append(statistics.mean(ys))
113
114        print("Making Plots")
115
116        for name, data in sorted(series.items()):
117                _col = next(colors)
118                plt.scatter(data['x'], data['y'], color=_col, label=name[len(prefix):], marker='x')
119                plt.plot(lines[name]['x'], lines[name]['min'], '--', color=_col)
120                plt.plot(lines[name]['x'], lines[name]['max'], '--', color=_col)
121                plt.plot(lines[name]['x'], lines[name]['med'], '-', color=_col)
122
123        print("Calculating Extremums")
124
125        mx = max([max(s['x']) for s in series.values()])
126        my = max([max(s['y']) for s in series.values()])
127
128        print("Finishing Plots")
129
130        plt.ylabel(field_names[y].name if field_names[y].name else y)
131        # plt.xticks(range(1, math.ceil(mx) + 1))
132        plt.xlabel(field_names[x].name if field_names[x].name else x)
133        plt.grid(b = True)
134        ax.xaxis.set_major_formatter( EngFormatter(unit=field_names[x].unit) )
135        if options.logx:
136                ax.set_xscale('log')
137        elif field_names[x].log:
138                ax.set_xscale('log')
139                if field_names[x].log == "exact":
140                        xvals = set()
141                        for s in series.values():
142                                xvals |= set(s['x'])
143                        ax.set_xticks(sorted(xvals))
144                        ax.get_xaxis().set_major_formatter(ScalarFormatter())
145                        plt.xticks(rotation = 45)
146        else:
147                plt.xlim(field_names[x].min, mx + 0.25)
148
149        if options.logy:
150                ax.set_yscale('log')
151        elif field_names[y].log:
152                ax.set_yscale('log')
153        else:
154                plt.ylim(field_names[y].min, options.MaxY if options.MaxY else my*1.2)
155
156        ax.yaxis.set_major_formatter( EngFormatter(unit=field_names[y].unit) )
157
158        plt.legend(loc='upper left')
159
160        print("Results Ready")
161        start = time.time()
162        if options.out:
163                plt.savefig(options.out, bbox_inches='tight')
164        else:
165                plt.show()
166        end = time.time()
167        print("Took {}".format(fmtDur(end - start)))
168
169
170if __name__ == "__main__":
171        # ================================================================================
172        # parse command line arguments
173        parser = argparse.ArgumentParser(description='Python Script to draw R.M.I.T. results')
174        parser.add_argument('-f', '--file', nargs='?', type=argparse.FileType('r'), default=sys.stdin, help="Input file")
175        parser.add_argument('-o', '--out', nargs='?', type=str, default=None, help="Output file")
176        parser.add_argument('-y', nargs='?', type=str, default="", help="Which field to use as the Y axis")
177        parser.add_argument('-x', nargs='?', type=str, default="", help="Which field to use as the X axis")
178        parser.add_argument('--logx', action='store_true', help="if set, makes the x-axis logscale")
179        parser.add_argument('--logy', action='store_true', help="if set, makes the y-axis logscale")
180        parser.add_argument('--MaxY', nargs='?', type=int, help="maximum value of the y-axis")
181        parser.add_argument('--filter', nargs='?', type=str, default="", help="if not empty, only print series that start with specified filter")
182
183        options =  parser.parse_args()
184
185        # if not options.out:
186        #       matplotlib.use('SVG')
187
188        # ================================================================================
189        # load data
190        try :
191                data = json.load(options.file)
192        except :
193                print('ERROR: could not read input', file=sys.stderr)
194                parser.print_help(sys.stderr)
195                sys.exit(1)
196
197        # ================================================================================
198        # identify the keys
199
200        series = set()
201        fields = set()
202
203        for entry in data:
204                series.add(entry[0])
205                for label in entry[2].keys():
206                        fields.add(label)
207
208        # filter out the series if needed
209        if options.filter:
210                series = set(filter(lambda elem: elem.startswith(options.filter), series))
211
212        # find the common prefix on series for removal (only if no filter)
213        prefix = os.path.commonprefix(list(series))
214
215        if not options.out :
216                print(series)
217                print("fields: ", ' '.join(fields))
218
219        wantx = "Number of processors"
220        wanty = "ns per ops"
221
222        if options.x:
223                if options.x in field_names.keys():
224                        wantx = options.x
225                else:
226                        print("Could not find X key '{}', defaulting to '{}'".format(options.x, wantx))
227
228        if options.y:
229                if options.y in field_names.keys():
230                        wanty = options.y
231                else:
232                        print("Could not find Y key '{}', defaulting to '{}'".format(options.y, wanty))
233
234
235        plot(data, wantx, wanty, options, prefix)
Note: See TracBrowser for help on using the repository browser.