source: benchmark/plot.py @ 748877f

Last change on this file since 748877f was 8fca132, checked in by Thierry Delisle <tdelisle@…>, 2 years ago

Changed plots to use different markers and dotted lines for minimum

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