Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(542)

Side by Side Diff: tools/perf_expectations/make_expectations.py

Issue 5607005: Add script to update perf_expectations.json. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/tools/perf_expectations
Patch Set: double quotes -> single Created 10 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/python
2 # Copyright (c) 2010 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6
7 import math
8 import optparse
9 import re
10 import simplejson
11 import subprocess
12 import sys
13 import time
14 import urllib2
15
16
17 __version__ = '1.0'
18 DEFAULT_EXPECTATIONS_FILE = 'perf_expectations.json'
19 DEFAULT_VARIANCE = 0.05
20 USAGE = ''
21
22
23 def ReadFile(filename):
24 try:
25 file = open(filename, 'r')
26 except IOError, e:
27 print >> sys.stderr, ('I/O Error reading file %s(%s): %s' %
28 (filename, e.errno, e.strerror))
29 raise e
30 if not file:
31 return None
32 contents = file.read()
33 file.close()
34 return contents
35
36
37 def ConvertJsonIntoDict(string):
38 """Read a JSON string and convert its contents into a Python datatype."""
39 if len(string) == 0:
40 print >> sys.stderr, ('Error could not parse empty string')
41 raise Exception('JSON data missing')
42
43 try:
44 json = simplejson.loads(string)
45 except ValueError, e:
46 print >> sys.stderr, ('Error parsing string: "%s"' % string)
47 raise e
48 return json
49
50
51 # Floating point representation of last time we fetched a URL.
52 last_fetched_at = None
53 def FetchUrlContents(url):
54 global last_fetched_at
55 if last_fetched_at and ((time.time() - last_fetched_at) <= 0.5):
56 # Sleep for half a second to avoid overloading the server.
57 time.sleep(0.5)
58 try:
59 last_fetched_at = time.time()
60 connection = urllib2.urlopen(url)
61 except urllib2.HTTPError, e:
62 if e.code == 404:
63 return None
64 raise e
65 text = connection.read().strip()
66 connection.close()
67 return text
68
69
70 def WriteJson(filename, data, keys):
71 """Write a list of |keys| in |data| to the file specified in |filename|."""
72 try:
73 file = open(filename, 'w')
74 except IOError, e:
75 print >> sys.stderr, ('I/O Error writing file %s(%s): %s' %
76 (filename, e.errno, e.strerror))
77 return False
78 jsondata = []
79 for key in keys:
80 rowdata = []
81 for subkey in ['reva', 'revb', 'improve', 'regress']:
82 if subkey in data[key]:
83 rowdata.append('"%s": %s' % (subkey, data[key][subkey]))
84 jsondata.append('"%s": {%s}' % (key, ', '.join(rowdata)))
85 jsondata.append('"load": true')
86 json = '{%s\n}' % ',\n '.join(jsondata)
87 file.write(json + '\n')
88 file.close()
89 return True
90
91
92 def Main(args):
93 parser = optparse.OptionParser(usage=USAGE, version=__version__)
94 options, args = parser.parse_args(args)
95
96 # Get the list of summaries for a test.
97 base_url = 'http://build.chromium.org/f/chromium/perf'
98 perf = ConvertJsonIntoDict(ReadFile(DEFAULT_EXPECTATIONS_FILE))
99
100 # Fetch graphs.dat for this combination.
101 perfkeys = perf.keys()
102 # In perf_expectations.json, ignore the 'load' key.
103 perfkeys.remove('load')
104 perfkeys.sort()
105
106 write_new_expectations = False
107 for key in perfkeys:
108 value = perf[key]
109 variance = DEFAULT_VARIANCE
110
111 # Skip expectations that are missing a reva or revb. We can't generate
112 # expectations for those.
113 if not(value.has_key('reva') and value.has_key('revb')):
114 print '%s (skipping, missing revision range)' % key
115 continue
116 revb = int(value['revb'])
117 reva = int(value['reva'])
118
119 # Ensure that reva is less than revb.
120 if reva > revb:
121 temp = reva
122 reva = revb
123 revb = temp
124
125 # Get the system/test/graph/tracename and reftracename for the current key.
126 matchData = re.match(r'^([^/]+)\/([^/]+)\/([^/]+)\/([^/]+)$', key)
127 if not matchData:
128 print '%s (skipping, cannot parse key)' % key
129 continue
130 system = matchData.group(1)
131 test = matchData.group(2)
132 graph = matchData.group(3)
133 tracename = matchData.group(4)
134 reftracename = tracename + '_ref'
135
136 # Create the summary_url and get the json data for that URL.
137 # FetchUrlContents() may sleep to avoid overloading the server with
138 # requests.
139 summary_url = '%s/%s/%s/%s-summary.dat' % (base_url, system, test, graph)
140 summaryjson = FetchUrlContents(summary_url)
141 if not summaryjson:
142 print '%s (skipping, missing json data)' % key
143 continue
144
145 summarylist = summaryjson.split('\n')
146 trace_values = {}
147 for trace in [tracename, reftracename]:
148 trace_values.setdefault(trace, {})
149
150 # Find the high and low values for each of the traces.
151 scanning = False
152 printed_error = False
153 for line in summarylist:
154 json = ConvertJsonIntoDict(line)
155 if int(json['rev']) <= revb:
156 scanning = True
157 if int(json['rev']) < reva:
158 break
159
160 # We found the upper revision in the range. Scan for trace data until we
161 # find the lower revision in the range.
162 if scanning:
163 for trace in [tracename, reftracename]:
164 if trace not in json['traces']:
165 if not printed_error:
166 print '%s (error)' % key
167 printed_error = True
168 print ' trace %s missing' % trace
169 continue
170 if type(json['traces'][trace]) != type([]):
171 if not printed_error:
172 print '%s (error)' % key
173 printed_error = True
174 print ' trace %s format not recognized' % trace
175 continue
176 try:
177 tracevalue = float(json['traces'][trace][0])
178 except ValueError:
179 if not printed_error:
180 print '%s (error)' % key
181 printed_error = True
182 print ' trace %s value error: %s' % (
183 trace, str(json['traces'][trace][0]))
184 continue
185
186 for bound in ['high', 'low']:
187 trace_values[trace].setdefault(bound, tracevalue)
188
189 trace_values[trace]['high'] = max(trace_values[trace]['high'],
190 tracevalue)
191 trace_values[trace]['low'] = min(trace_values[trace]['low'],
192 tracevalue)
193
194 if 'high' not in trace_values[tracename]:
195 print '%s (skipping, no suitable traces matched)' % key
196 continue
197
198 # Calculate assuming high deltas are regressions and low deltas are
199 # improvements.
200 regress = (float(trace_values[tracename]['high']) -
201 float(trace_values[reftracename]['low']))
202 improve = (float(trace_values[tracename]['low']) -
203 float(trace_values[reftracename]['high']))
204 # If the existing values assume regressions are low deltas relative to
205 # improvements, swap our regress and improve. This value must be a
206 # scores-like result.
207 if perf[key]['regress'] < perf[key]['improve']:
208 temp = regress
209 regress = improve
210 improve = temp
211 if regress < improve:
212 regress = int(math.floor(regress - abs(regress*variance)))
213 improve = int(math.ceil(improve + abs(improve*variance)))
214 else:
215 improve = int(math.floor(improve - abs(improve*variance)))
216 regress = int(math.ceil(regress + abs(regress*variance)))
217
218 if (perf[key]['regress'] == regress and perf[key]['improve'] == improve):
219 print '%s (no change)' % key
220 continue
221
222 write_new_expectations = True
223 print key
224 print ' before = %s' % perf[key]
225 print ' traces = %s' % trace_values
226 perf[key]['regress'] = regress
227 perf[key]['improve'] = improve
228 print ' after = %s' % perf[key]
229
230 if write_new_expectations:
231 print 'writing expectations... ',
232 WriteJson(DEFAULT_EXPECTATIONS_FILE, perf, perfkeys)
233 print 'done'
234 else:
235 print 'no updates made'
236
237
238 if __name__ == '__main__':
239 sys.exit(Main(sys.argv))
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698