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

Side by Side Diff: experimental/benchtools/rebase.py

Issue 370413002: bench rebase tool: add ability to specify a second CL for fluctuating data adjustment. (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: formatting Created 6 years, 5 months 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
« 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
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2014 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2014 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 6
7 """rebase.py: standalone script to batch update bench expectations. 7 """rebase.py: standalone script to batch update bench expectations.
8 8
9 Requires gsutil to access gs://chromium-skia-gm and Rietveld credentials. 9 Requires gsutil to access gs://chromium-skia-gm and Rietveld credentials.
10 10
(...skipping 14 matching lines...) Expand all
25 import time 25 import time
26 import urllib2 26 import urllib2
27 27
28 28
29 # googlesource url that has most recent Skia git hash info. 29 # googlesource url that has most recent Skia git hash info.
30 SKIA_GIT_HEAD_URL = 'https://skia.googlesource.com/skia/+log/HEAD' 30 SKIA_GIT_HEAD_URL = 'https://skia.googlesource.com/skia/+log/HEAD'
31 31
32 # Google Storage bench file prefix. 32 # Google Storage bench file prefix.
33 GS_PREFIX = 'gs://chromium-skia-gm/perfdata' 33 GS_PREFIX = 'gs://chromium-skia-gm/perfdata'
34 34
35 # List of Perf platforms we want to process. Populate from expectations/bench.
36 PLATFORMS = []
37
38 # Regular expression for matching githash data. 35 # Regular expression for matching githash data.
39 HA_RE = '<a href="/skia/\+/([0-9a-f]+)">' 36 HA_RE = '<a href="/skia/\+/([0-9a-f]+)">'
40 HA_RE_COMPILED = re.compile(HA_RE) 37 HA_RE_COMPILED = re.compile(HA_RE)
41 38
42 39
43 def get_git_hashes(): 40 def get_git_hashes():
44 print 'Getting recent git hashes...' 41 print 'Getting recent git hashes...'
45 hashes = HA_RE_COMPILED.findall( 42 hashes = HA_RE_COMPILED.findall(
46 urllib2.urlopen(SKIA_GIT_HEAD_URL).read()) 43 urllib2.urlopen(SKIA_GIT_HEAD_URL).read())
47 44
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
81 files = 0 78 files = 0
82 for f in os.listdir(os.path.join(gs_dir, p)): 79 for f in os.listdir(os.path.join(gs_dir, p)):
83 if filter_file(f): 80 if filter_file(f):
84 os.remove(os.path.join(gs_dir, p, f)) 81 os.remove(os.path.join(gs_dir, p, f))
85 else: 82 else:
86 files += 1 83 files += 1
87 if files: 84 if files:
88 return True 85 return True
89 return False 86 return False
90 87
91 def calc_expectations(p, h, gs_dir, exp_dir, repo_dir): 88 def get_expectations_dict(f):
89 """Given an expectations file f, returns a dictionary of data."""
90 # maps row_key to (expected, lower_bound, upper_bound) float tuple.
91 dic = {}
92 for l in open(f).readlines():
93 line_parts = l.strip().split(',')
94 if line_parts[0].startswith('#') or len(line_parts) != 5:
95 continue
96 dic[','.join(line_parts[:2])] = (float(line_parts[2]), float(line_parts[3]),
97 float(line_parts[4]))
98
99 return dic
100
101 def calc_expectations(p, h, gs_dir, exp_dir, repo_dir, extra_dir, extra_hash):
92 exp_filename = 'bench_expectations_%s.txt' % p 102 exp_filename = 'bench_expectations_%s.txt' % p
103 exp_fullname = os.path.join(exp_dir, exp_filename)
93 proc = subprocess.Popen(['python', 'skia/bench/gen_bench_expectations.py', 104 proc = subprocess.Popen(['python', 'skia/bench/gen_bench_expectations.py',
94 '-r', h, '-b', p, '-d', os.path.join(gs_dir, p), '-o', 105 '-r', h, '-b', p, '-d', os.path.join(gs_dir, p), '-o', exp_fullname],
95 os.path.join(exp_dir, exp_filename)],
96 stdout=subprocess.PIPE) 106 stdout=subprocess.PIPE)
97 out, err = proc.communicate() 107 out, err = proc.communicate()
98 if err: 108 if err:
99 print 'ERR_CALCULATING_EXPECTATIONS: ' + err 109 print 'ERR_CALCULATING_EXPECTATIONS: ' + err
100 return False 110 return False
101 print 'CALCULATED_EXPECTATIONS: ' + out 111 print 'CALCULATED_EXPECTATIONS: ' + out
112 if extra_dir: # Adjust data with the ones in extra_dir
113 print 'USE_EXTRA_DATA_FOR_ADJUSTMENT.'
114 proc = subprocess.Popen(['python', 'skia/bench/gen_bench_expectations.py',
115 '-r', extra_hash, '-b', p, '-d', os.path.join(extra_dir, p), '-o',
116 os.path.join(extra_dir, exp_filename)],
117 stdout=subprocess.PIPE)
118 out, err = proc.communicate()
119 if err:
120 print 'ERR_CALCULATING_EXTRA_EXPECTATIONS: ' + err
121 return False
122 extra_dic = get_expectations_dict(os.path.join(extra_dir, exp_filename))
123 output_lines = []
124 for l in open(exp_fullname).readlines():
125 parts = l.strip().split(',')
126 if parts[0].startswith('#') or len(parts) != 5:
127 output_lines.append(l.strip())
128 continue
129 key = ','.join(parts[:2])
130 if key in extra_dic:
131 exp, lb, ub = (float(parts[2]), float(parts[3]), float(parts[4]))
132 alt, _, _ = extra_dic[key]
133 avg = (exp + alt) / 2
134 # Keeps the extra range in lower/upper bounds from two actual values.
135 new_lb = min(exp, alt) - (exp - lb)
136 new_ub = max(exp, alt) + (ub - exp)
137 output_lines.append('%s,%.2f,%.2f,%.2f' % (key, avg, new_lb, new_ub))
138 else:
139 output_lines.append(l.strip())
140 with open(exp_fullname, 'w') as f:
141 f.write('\n'.join(output_lines))
142
102 repo_file = os.path.join(repo_dir, 'expectations', 'bench', exp_filename) 143 repo_file = os.path.join(repo_dir, 'expectations', 'bench', exp_filename)
103 if (os.path.isfile(repo_file) and 144 if (os.path.isfile(repo_file) and
104 filecmp.cmp(repo_file, os.path.join(exp_dir, exp_filename))): 145 filecmp.cmp(repo_file, os.path.join(exp_dir, exp_filename))):
105 print 'NO CHANGE ON %s' % repo_file 146 print 'NO CHANGE ON %s' % repo_file
106 return False 147 return False
107 return True 148 return True
108 149
109 def checkout_or_update_skia(repo_dir): 150 def checkout_or_update_skia(repo_dir):
110 status = True 151 status = True
111 old_cwd = os.getcwd() 152 old_cwd = os.getcwd()
112 os.chdir(repo_dir) 153 os.chdir(repo_dir)
113 print 'CHECK SKIA REPO...' 154 print 'CHECK SKIA REPO...'
114 if subprocess.call(['git', 'pull'], 155 if subprocess.call(['git', 'pull'],
115 stderr=subprocess.PIPE): 156 stderr=subprocess.PIPE):
116 print 'Checking out Skia from git, please be patient...' 157 print 'Checking out Skia from git, please be patient...'
117 os.chdir(old_cwd) 158 os.chdir(old_cwd)
118 clean_dir(repo_dir) 159 clean_dir(repo_dir)
119 os.chdir(repo_dir) 160 os.chdir(repo_dir)
120 if subprocess.call(['git', 'clone', '-q', '--depth=50', '--single-branch', 161 if subprocess.call(['git', 'clone', '-q', '--depth=50', '--single-branch',
121 'https://skia.googlesource.com/skia.git', '.']): 162 'https://skia.googlesource.com/skia.git', '.']):
122 status = False 163 status = False
123 subprocess.call(['git', 'checkout', 'master']) 164 subprocess.call(['git', 'checkout', 'master'])
124 subprocess.call(['git', 'pull']) 165 subprocess.call(['git', 'pull'])
125 os.chdir(old_cwd) 166 os.chdir(old_cwd)
126 return status 167 return status
127 168
128 def git_commit_expectations(repo_dir, exp_dir, update_li, h, commit): 169 def git_commit_expectations(repo_dir, exp_dir, update_li, h, commit,
129 commit_msg = """manual bench rebase after %s 170 extra_hash):
171 if extra_hash:
172 extra_hash = ', adjusted with ' + extra_hash
173 commit_msg = """manual bench rebase after %s%s
130 174
131 TBR=robertphillips@google.com 175 TBR=robertphillips@google.com
132 176
133 Bypassing trybots: 177 Bypassing trybots:
134 NOTRY=true""" % h 178 NOTRY=true""" % (h, extra_hash)
135 old_cwd = os.getcwd() 179 old_cwd = os.getcwd()
136 os.chdir(repo_dir) 180 os.chdir(repo_dir)
137 upload = ['git', 'cl', 'upload', '-f', '--bypass-hooks', 181 upload = ['git', 'cl', 'upload', '-f', '--bypass-hooks',
138 '--bypass-watchlists', '-m', commit_msg] 182 '--bypass-watchlists', '-m', commit_msg]
139 branch = exp_dir.split('/')[-1] 183 branch = exp_dir.split('/')[-1]
140 if commit: 184 if commit:
141 upload.append('--use-commit-queue') 185 upload.append('--use-commit-queue')
142 cmds = ([['git', 'checkout', 'master'], 186 cmds = ([['git', 'checkout', 'master'],
143 ['git', 'pull'], 187 ['git', 'pull'],
144 ['git', 'checkout', '-b', branch, '-t', 'origin/master']] + 188 ['git', 'checkout', '-b', branch, '-t', 'origin/master']] +
(...skipping 23 matching lines...) Expand all
168 212
169 213
170 def main(): 214 def main():
171 d = os.path.dirname(os.path.abspath(__file__)) 215 d = os.path.dirname(os.path.abspath(__file__))
172 os.chdir(d) 216 os.chdir(d)
173 if not subprocess.call(['git', 'rev-parse'], stderr=subprocess.PIPE): 217 if not subprocess.call(['git', 'rev-parse'], stderr=subprocess.PIPE):
174 print 'Please copy script to a separate dir outside git repos to use.' 218 print 'Please copy script to a separate dir outside git repos to use.'
175 return 219 return
176 parser = argparse.ArgumentParser() 220 parser = argparse.ArgumentParser()
177 parser.add_argument('--githash', 221 parser.add_argument('--githash',
178 help='Githash prefix (7+ chars) to rebaseline to.') 222 help=('Githash prefix (7+ chars) to rebaseline to. If '
223 'a second one is supplied after comma, and it has '
224 'corresponding bench data, will shift the range '
225 'center to the average of two expected values.'))
226 parser.add_argument('--bots',
227 help=('Comma-separated list of bots to work on. If no '
228 'matching bots are found in the list, will default '
229 'to processing all bots.'))
179 parser.add_argument('--commit', action='store_true', 230 parser.add_argument('--commit', action='store_true',
180 help='Whether to commit changes automatically.') 231 help='Whether to commit changes automatically.')
181 args = parser.parse_args() 232 args = parser.parse_args()
182 233
183 repo_dir = os.path.join(d, 'skia') 234 repo_dir = os.path.join(d, 'skia')
184 if not os.path.exists(repo_dir): 235 if not os.path.exists(repo_dir):
185 os.makedirs(repo_dir) 236 os.makedirs(repo_dir)
186 if not checkout_or_update_skia(repo_dir): 237 if not checkout_or_update_skia(repo_dir):
187 print 'ERROR setting up Skia repo at %s' % repo_dir 238 print 'ERROR setting up Skia repo at %s' % repo_dir
188 return 1 239 return 1
189 240
190 file_in_repo = os.path.join(d, 'skia/experimental/benchtools/rebase.py') 241 file_in_repo = os.path.join(d, 'skia/experimental/benchtools/rebase.py')
191 if not filecmp.cmp(__file__, file_in_repo): 242 if not filecmp.cmp(__file__, file_in_repo):
192 shutil.copy(file_in_repo, __file__) 243 shutil.copy(file_in_repo, __file__)
193 print 'Updated this script from repo; please run again.' 244 print 'Updated this script from repo; please run again.'
194 return 245 return
195 246
247 all_platforms = [] # Find existing list of platforms with expectations.
196 for item in os.listdir(os.path.join(d, 'skia/expectations/bench')): 248 for item in os.listdir(os.path.join(d, 'skia/expectations/bench')):
197 PLATFORMS.append( 249 all_platforms.append(
198 item.replace('bench_expectations_', '').replace('.txt', '')) 250 item.replace('bench_expectations_', '').replace('.txt', ''))
199 251
252 platforms = []
253 # If at least one given bot is in all_platforms, use list of valid args.bots.
254 if args.bots:
255 bots = args.bots.strip().split(',')
256 for bot in bots:
257 if bot in all_platforms: # Filters platforms with given bot list.
258 platforms.append(bot)
259 if not platforms: # Include all existing platforms with expectations.
260 platforms = all_platforms
261
200 if not args.githash or len(args.githash) < 7: 262 if not args.githash or len(args.githash) < 7:
201 raise Exception('Please provide --githash with a longer prefix (7+).') 263 raise Exception('Please provide --githash with a longer prefix (7+).')
264 githashes = args.githash.strip().split(',')
265 if len(githashes[0]) < 7:
266 raise Exception('Please provide --githash with longer prefixes (7+).')
202 commit = False 267 commit = False
203 if args.commit: 268 if args.commit:
204 commit = True 269 commit = True
205 rebase_hash = args.githash[:7] 270 rebase_hash = githashes[0][:7]
271 extra_hash = ''
272 if len(githashes) == 2:
273 extra_hash = githashes[1][:7]
206 hashes = get_git_hashes() 274 hashes = get_git_hashes()
207 short_hashes = [h[:7] for h in hashes] 275 short_hashes = [h[:7] for h in hashes]
208 if rebase_hash not in short_hashes: 276 if (rebase_hash not in short_hashes or
209 raise Exception('Provided --githash not found in recent history!') 277 (extra_hash and extra_hash not in short_hashes) or
278 rebase_hash == extra_hash):
279 raise Exception('Provided --githashes not found, or identical!')
280 if extra_hash:
281 extra_hash = hashes[short_hashes.index(extra_hash)]
210 hashes = hashes[:short_hashes.index(rebase_hash) + 1] 282 hashes = hashes[:short_hashes.index(rebase_hash) + 1]
211 update_li = [] 283 update_li = []
212 284
213 ts_str = '%s' % time.time() 285 ts_str = '%s' % time.time()
214 gs_dir = os.path.join(d, 'gs' + ts_str) 286 gs_dir = os.path.join(d, 'gs' + ts_str)
215 exp_dir = os.path.join(d, 'exp' + ts_str) 287 exp_dir = os.path.join(d, 'exp' + ts_str)
288 extra_dir = os.path.join(d, 'extra' + ts_str)
216 clean_dir(gs_dir) 289 clean_dir(gs_dir)
217 clean_dir(exp_dir) 290 clean_dir(exp_dir)
218 for p in PLATFORMS: 291 clean_dir(extra_dir)
292 for p in platforms:
219 clean_dir(os.path.join(gs_dir, p)) 293 clean_dir(os.path.join(gs_dir, p))
294 clean_dir(os.path.join(extra_dir, p))
220 hash_to_use = '' 295 hash_to_use = ''
221 for h in reversed(hashes): 296 for h in reversed(hashes):
222 li = get_gs_filelist(p, h) 297 li = get_gs_filelist(p, h)
223 if not len(li): # no data 298 if not len(li): # no data
224 continue 299 continue
225 if download_gs_files(p, h, gs_dir): 300 if download_gs_files(p, h, gs_dir):
226 print 'Copied %s/%s' % (p, h) 301 print 'Copied %s/%s' % (p, h)
227 hash_to_use = h 302 hash_to_use = h
228 break 303 break
229 else: 304 else:
230 print 'DOWNLOAD BENCH FAILED %s/%s' % (p, h) 305 print 'DOWNLOAD BENCH FAILED %s/%s' % (p, h)
231 break 306 break
232 if hash_to_use: 307 if hash_to_use:
233 if calc_expectations(p, h, gs_dir, exp_dir, repo_dir): 308 if extra_hash and download_gs_files(p, extra_hash, extra_dir):
309 print 'Copied extra data %s/%s' % (p, extra_hash)
310 if calc_expectations(p, h, gs_dir, exp_dir, repo_dir, extra_dir,
311 extra_hash):
312 update_li.append('bench_expectations_%s.txt' % p)
313 elif calc_expectations(p, h, gs_dir, exp_dir, repo_dir, '', ''):
234 update_li.append('bench_expectations_%s.txt' % p) 314 update_li.append('bench_expectations_%s.txt' % p)
235 if not update_li: 315 if not update_li:
236 print 'No bench data to update after %s!' % args.githash 316 print 'No bench data to update after %s!' % args.githash
237 elif not git_commit_expectations( 317 elif not git_commit_expectations(
238 repo_dir, exp_dir, update_li, args.githash[:7], commit): 318 repo_dir, exp_dir, update_li, rebase_hash, commit, extra_hash):
239 print 'ERROR uploading expectations using git.' 319 print 'ERROR uploading expectations using git.'
240 elif not commit: 320 elif not commit:
241 print 'CL created. Please take a look at the link above.' 321 print 'CL created. Please take a look at the link above.'
242 else: 322 else:
243 print 'New bench baselines should be in CQ now.' 323 print 'New bench baselines should be in CQ now.'
244 delete_dirs([gs_dir, exp_dir]) 324 delete_dirs([gs_dir, exp_dir, extra_dir])
245 325
246 326
247 if __name__ == "__main__": 327 if __name__ == "__main__":
248 main() 328 main()
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