2 Define a base class for client-side benchmarking.
9 from .client import Client
13 Base class for concurrent benchmarks. The measure_once() method must be
16 Each sub-benchmark will be run in its own process then a report is done
17 with all the results (shared with the main process using a
18 `multiprocessing.Array`).
21 def __init__(self, subparsers=None):
22 super(Bench, self).__init__(subparsers)
23 self.parser.add_argument('-n', '--samples', metavar='INT',
24 default=100, help='number of measurements to take')
25 # TODO if -n <int>s is given (instead of -n <int>), run the
26 # benchmark for <int> seconds and return the number of iterations.
27 self.parser.add_argument('-o', '--output', metavar='PATH',
28 required=True, help='path to save the generated report')
29 self.parser.add_argument('--append', action='store_true',
30 default=False, help='append the report to an existing file')
31 self.parser.add_argument('-j', '--jobs', metavar='JOBS',
32 default=1, help='number of concurrent workers')
33 self.parser.add_argument('--seed', metavar='SEED',
34 default=0, help='a value to ensure different runs can create unique data')
37 def work(self, iarr=None):
39 # If an array is given, it means we are a worker process...
42 # ... else we are the main process and we will spawn workers,
43 # passing them an array.
46 def work_master(self):
47 N = int(self.args.samples)
48 self.arrs = [(i, multiprocessing.Array('f', range(N)))
49 for i in xrange(int(self.args.jobs))]
50 ps = [multiprocessing.Process(target=self.run, args=(arr,))
52 [p.start() for p in ps]
53 [p.join() for p in ps]
57 def work_slave(self, iarr):
60 N = int(self.args.samples)
61 total_t0 = time.time()
67 print >> sys.stdout, '\r%s' % ('|' * (i * 60 / N)),
68 print >> sys.stdout, '%s %s%%' % \
69 (' ' * (60 - (i * 60 / N)), int(float(i+1)/N*100)),
71 total_t1 = time.time()
72 print '\nDone in %ss.' % (total_t1 - total_t0)
74 def report_html(self):
79 points: { show: true }
80 }""" % ([[x, i] for i, x in enumerate(arr)],)
82 chart_id = hashlib.md5(" ".join(sys.argv)).hexdigest()
83 HEADER = """<!doctype html>
84 <title>Benchmarks</title>
86 <script type="text/javascript" src="js/jquery.min.js"></script>
87 <script type="text/javascript" src="js/jquery.flot.js"></script>
90 CONTENT = """<h1>%s</h1>
92 <div id='chart_%s' style='width:400px;height:300px;'>...</div>
93 <script type="text/javascript">
94 $.plot($("#chart_%s"), [%s],
95 {yaxis: { ticks: false }});
96 </script>""" % (self.bench_name, ' '.join(sys.argv), chart_id, chart_id,
99 with open(self.args.output, 'a') as f:
102 with open(self.args.output, 'w') as f:
103 f.write(HEADER + CONTENT,)
105 def measure_once(self, i):
107 The `measure_once` method is called --jobs times. A `i` argument is
108 supplied to allow to create unique values for each execution (e.g. to
109 supply fresh identifiers to a `create` method.
113 class BenchRead(Bench):
114 """Read a record repeatedly."""
116 command_name = 'bench-read'
117 bench_name = 'res.users.read(1)'
119 def __init__(self, subparsers=None):
120 super(BenchRead, self).__init__(subparsers)
121 self.parser.add_argument('-m', '--model', metavar='MODEL',
122 required=True, help='the model')
123 self.parser.add_argument('-i', '--id', metavar='RECORDID',
124 required=True, help='the record id')
126 def measure_once(self, i):
127 self.execute(self.args.model, 'read', [self.args.id], [])
129 class BenchFieldsViewGet(Bench):
130 """Read a record's fields and view architecture repeatedly."""
132 command_name = 'bench-view'
133 bench_name = 'res.users.fields_view_get(1)'
135 def __init__(self, subparsers=None):
136 super(BenchFieldsViewGet, self).__init__(subparsers)
137 self.parser.add_argument('-m', '--model', metavar='MODEL',
138 required=True, help='the model')
139 self.parser.add_argument('-i', '--id', metavar='RECORDID',
140 required=True, help='the record id')
142 def measure_once(self, i):
143 self.execute(self.args.model, 'fields_view_get', self.args.id)
145 class BenchDummy(Bench):
146 """Dummy (call test.limits.model.consume_nothing())."""
148 command_name = 'bench-dummy'
149 bench_name = 'test.limits.model.consume_nothing()'
151 def __init__(self, subparsers=None):
152 super(BenchDummy, self).__init__(subparsers)
153 self.parser.add_argument('-a', '--args', metavar='ARGS',
154 default='', help='some arguments to serialize')
156 def measure_once(self, i):
157 self.execute('test.limits.model', 'consume_nothing')
159 class BenchLogin(Bench):
160 """Login (update res_users.date)."""
162 command_name = 'bench-login'
163 bench_name = 'res.users.login(1)'
165 def measure_once(self, i):
166 self.common_proxy.login(self.database, self.user, self.password)