##// END OF EJS Templates
perf: support obtaining contexts from perfrevset...
Gregory Szorc -
r27072:e18a9cea default
parent child Browse files
Show More
@@ -1,578 +1,582
1 1 # perf.py - performance test routines
2 2 '''helper extension to measure performance'''
3 3
4 4 from mercurial import cmdutil, scmutil, util, commands, obsolete
5 5 from mercurial import repoview, branchmap, merge, copies
6 6 import time, os, sys
7 7 import functools
8 8
9 9 formatteropts = commands.formatteropts
10 10
11 11 cmdtable = {}
12 12 command = cmdutil.command(cmdtable)
13 13
14 14 def gettimer(ui, opts=None):
15 15 """return a timer function and formatter: (timer, formatter)
16 16
17 17 This functions exist to gather the creation of formatter in a single
18 18 place instead of duplicating it in all performance command."""
19 19
20 20 # enforce an idle period before execution to counteract power management
21 21 # experimental config: perf.presleep
22 22 time.sleep(ui.configint("perf", "presleep", 1))
23 23
24 24 if opts is None:
25 25 opts = {}
26 26 # redirect all to stderr
27 27 ui = ui.copy()
28 28 ui.fout = ui.ferr
29 29 # get a formatter
30 30 fm = ui.formatter('perf', opts)
31 31 return functools.partial(_timer, fm), fm
32 32
33 33 def _timer(fm, func, title=None):
34 34 results = []
35 35 begin = time.time()
36 36 count = 0
37 37 while True:
38 38 ostart = os.times()
39 39 cstart = time.time()
40 40 r = func()
41 41 cstop = time.time()
42 42 ostop = os.times()
43 43 count += 1
44 44 a, b = ostart, ostop
45 45 results.append((cstop - cstart, b[0] - a[0], b[1]-a[1]))
46 46 if cstop - begin > 3 and count >= 100:
47 47 break
48 48 if cstop - begin > 10 and count >= 3:
49 49 break
50 50
51 51 fm.startitem()
52 52
53 53 if title:
54 54 fm.write('title', '! %s\n', title)
55 55 if r:
56 56 fm.write('result', '! result: %s\n', r)
57 57 m = min(results)
58 58 fm.plain('!')
59 59 fm.write('wall', ' wall %f', m[0])
60 60 fm.write('comb', ' comb %f', m[1] + m[2])
61 61 fm.write('user', ' user %f', m[1])
62 62 fm.write('sys', ' sys %f', m[2])
63 63 fm.write('count', ' (best of %d)', count)
64 64 fm.plain('\n')
65 65
66 66 @command('perfwalk', formatteropts)
67 67 def perfwalk(ui, repo, *pats, **opts):
68 68 timer, fm = gettimer(ui, opts)
69 69 try:
70 70 m = scmutil.match(repo[None], pats, {})
71 71 timer(lambda: len(list(repo.dirstate.walk(m, [], True, False))))
72 72 except Exception:
73 73 try:
74 74 m = scmutil.match(repo[None], pats, {})
75 75 timer(lambda: len([b for a, b, c in repo.dirstate.statwalk([], m)]))
76 76 except Exception:
77 77 timer(lambda: len(list(cmdutil.walk(repo, pats, {}))))
78 78 fm.end()
79 79
80 80 @command('perfannotate', formatteropts)
81 81 def perfannotate(ui, repo, f, **opts):
82 82 timer, fm = gettimer(ui, opts)
83 83 fc = repo['.'][f]
84 84 timer(lambda: len(fc.annotate(True)))
85 85 fm.end()
86 86
87 87 @command('perfstatus',
88 88 [('u', 'unknown', False,
89 89 'ask status to look for unknown files')] + formatteropts)
90 90 def perfstatus(ui, repo, **opts):
91 91 #m = match.always(repo.root, repo.getcwd())
92 92 #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
93 93 # False))))
94 94 timer, fm = gettimer(ui, opts)
95 95 timer(lambda: sum(map(len, repo.status(unknown=opts['unknown']))))
96 96 fm.end()
97 97
98 98 @command('perfaddremove', formatteropts)
99 99 def perfaddremove(ui, repo, **opts):
100 100 timer, fm = gettimer(ui, opts)
101 101 try:
102 102 oldquiet = repo.ui.quiet
103 103 repo.ui.quiet = True
104 104 matcher = scmutil.match(repo[None])
105 105 timer(lambda: scmutil.addremove(repo, matcher, "", dry_run=True))
106 106 finally:
107 107 repo.ui.quiet = oldquiet
108 108 fm.end()
109 109
110 110 def clearcaches(cl):
111 111 # behave somewhat consistently across internal API changes
112 112 if util.safehasattr(cl, 'clearcaches'):
113 113 cl.clearcaches()
114 114 elif util.safehasattr(cl, '_nodecache'):
115 115 from mercurial.node import nullid, nullrev
116 116 cl._nodecache = {nullid: nullrev}
117 117 cl._nodepos = None
118 118
119 119 @command('perfheads', formatteropts)
120 120 def perfheads(ui, repo, **opts):
121 121 timer, fm = gettimer(ui, opts)
122 122 cl = repo.changelog
123 123 def d():
124 124 len(cl.headrevs())
125 125 clearcaches(cl)
126 126 timer(d)
127 127 fm.end()
128 128
129 129 @command('perftags', formatteropts)
130 130 def perftags(ui, repo, **opts):
131 131 import mercurial.changelog
132 132 import mercurial.manifest
133 133 timer, fm = gettimer(ui, opts)
134 134 def t():
135 135 repo.changelog = mercurial.changelog.changelog(repo.svfs)
136 136 repo.manifest = mercurial.manifest.manifest(repo.svfs)
137 137 repo._tags = None
138 138 return len(repo.tags())
139 139 timer(t)
140 140 fm.end()
141 141
142 142 @command('perfancestors', formatteropts)
143 143 def perfancestors(ui, repo, **opts):
144 144 timer, fm = gettimer(ui, opts)
145 145 heads = repo.changelog.headrevs()
146 146 def d():
147 147 for a in repo.changelog.ancestors(heads):
148 148 pass
149 149 timer(d)
150 150 fm.end()
151 151
152 152 @command('perfancestorset', formatteropts)
153 153 def perfancestorset(ui, repo, revset, **opts):
154 154 timer, fm = gettimer(ui, opts)
155 155 revs = repo.revs(revset)
156 156 heads = repo.changelog.headrevs()
157 157 def d():
158 158 s = repo.changelog.ancestors(heads)
159 159 for rev in revs:
160 160 rev in s
161 161 timer(d)
162 162 fm.end()
163 163
164 164 @command('perfdirs', formatteropts)
165 165 def perfdirs(ui, repo, **opts):
166 166 timer, fm = gettimer(ui, opts)
167 167 dirstate = repo.dirstate
168 168 'a' in dirstate
169 169 def d():
170 170 dirstate.dirs()
171 171 del dirstate._dirs
172 172 timer(d)
173 173 fm.end()
174 174
175 175 @command('perfdirstate', formatteropts)
176 176 def perfdirstate(ui, repo, **opts):
177 177 timer, fm = gettimer(ui, opts)
178 178 "a" in repo.dirstate
179 179 def d():
180 180 repo.dirstate.invalidate()
181 181 "a" in repo.dirstate
182 182 timer(d)
183 183 fm.end()
184 184
185 185 @command('perfdirstatedirs', formatteropts)
186 186 def perfdirstatedirs(ui, repo, **opts):
187 187 timer, fm = gettimer(ui, opts)
188 188 "a" in repo.dirstate
189 189 def d():
190 190 "a" in repo.dirstate._dirs
191 191 del repo.dirstate._dirs
192 192 timer(d)
193 193 fm.end()
194 194
195 195 @command('perfdirstatefoldmap', formatteropts)
196 196 def perffilefoldmap(ui, repo, **opts):
197 197 timer, fm = gettimer(ui, opts)
198 198 dirstate = repo.dirstate
199 199 'a' in dirstate
200 200 def d():
201 201 dirstate._filefoldmap.get('a')
202 202 del dirstate._filefoldmap
203 203 timer(d)
204 204 fm.end()
205 205
206 206 @command('perfdirfoldmap', formatteropts)
207 207 def perfdirfoldmap(ui, repo, **opts):
208 208 timer, fm = gettimer(ui, opts)
209 209 dirstate = repo.dirstate
210 210 'a' in dirstate
211 211 def d():
212 212 dirstate._dirfoldmap.get('a')
213 213 del dirstate._dirfoldmap
214 214 del dirstate._dirs
215 215 timer(d)
216 216 fm.end()
217 217
218 218 @command('perfdirstatewrite', formatteropts)
219 219 def perfdirstatewrite(ui, repo, **opts):
220 220 timer, fm = gettimer(ui, opts)
221 221 ds = repo.dirstate
222 222 "a" in ds
223 223 def d():
224 224 ds._dirty = True
225 225 ds.write(repo.currenttransaction())
226 226 timer(d)
227 227 fm.end()
228 228
229 229 @command('perfmergecalculate',
230 230 [('r', 'rev', '.', 'rev to merge against')] + formatteropts)
231 231 def perfmergecalculate(ui, repo, rev, **opts):
232 232 timer, fm = gettimer(ui, opts)
233 233 wctx = repo[None]
234 234 rctx = scmutil.revsingle(repo, rev, rev)
235 235 ancestor = wctx.ancestor(rctx)
236 236 # we don't want working dir files to be stat'd in the benchmark, so prime
237 237 # that cache
238 238 wctx.dirty()
239 239 def d():
240 240 # acceptremote is True because we don't want prompts in the middle of
241 241 # our benchmark
242 242 merge.calculateupdates(repo, wctx, rctx, ancestor, False, False, False,
243 243 acceptremote=True)
244 244 timer(d)
245 245 fm.end()
246 246
247 247 @command('perfpathcopies', [], "REV REV")
248 248 def perfpathcopies(ui, repo, rev1, rev2, **opts):
249 249 timer, fm = gettimer(ui, opts)
250 250 ctx1 = scmutil.revsingle(repo, rev1, rev1)
251 251 ctx2 = scmutil.revsingle(repo, rev2, rev2)
252 252 def d():
253 253 copies.pathcopies(ctx1, ctx2)
254 254 timer(d)
255 255 fm.end()
256 256
257 257 @command('perfmanifest', [], 'REV')
258 258 def perfmanifest(ui, repo, rev, **opts):
259 259 timer, fm = gettimer(ui, opts)
260 260 ctx = scmutil.revsingle(repo, rev, rev)
261 261 t = ctx.manifestnode()
262 262 def d():
263 263 repo.manifest._mancache.clear()
264 264 repo.manifest._cache = None
265 265 repo.manifest.read(t)
266 266 timer(d)
267 267 fm.end()
268 268
269 269 @command('perfchangeset', formatteropts)
270 270 def perfchangeset(ui, repo, rev, **opts):
271 271 timer, fm = gettimer(ui, opts)
272 272 n = repo[rev].node()
273 273 def d():
274 274 repo.changelog.read(n)
275 275 #repo.changelog._cache = None
276 276 timer(d)
277 277 fm.end()
278 278
279 279 @command('perfindex', formatteropts)
280 280 def perfindex(ui, repo, **opts):
281 281 import mercurial.revlog
282 282 timer, fm = gettimer(ui, opts)
283 283 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
284 284 n = repo["tip"].node()
285 285 def d():
286 286 cl = mercurial.revlog.revlog(repo.svfs, "00changelog.i")
287 287 cl.rev(n)
288 288 timer(d)
289 289 fm.end()
290 290
291 291 @command('perfstartup', formatteropts)
292 292 def perfstartup(ui, repo, **opts):
293 293 timer, fm = gettimer(ui, opts)
294 294 cmd = sys.argv[0]
295 295 def d():
296 296 os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
297 297 timer(d)
298 298 fm.end()
299 299
300 300 @command('perfparents', formatteropts)
301 301 def perfparents(ui, repo, **opts):
302 302 timer, fm = gettimer(ui, opts)
303 303 nl = [repo.changelog.node(i) for i in xrange(1000)]
304 304 def d():
305 305 for n in nl:
306 306 repo.changelog.parents(n)
307 307 timer(d)
308 308 fm.end()
309 309
310 310 @command('perfctxfiles', formatteropts)
311 311 def perfparents(ui, repo, x, **opts):
312 312 x = int(x)
313 313 timer, fm = gettimer(ui, opts)
314 314 def d():
315 315 len(repo[x].files())
316 316 timer(d)
317 317 fm.end()
318 318
319 319 @command('perfrawfiles', formatteropts)
320 320 def perfparents(ui, repo, x, **opts):
321 321 x = int(x)
322 322 timer, fm = gettimer(ui, opts)
323 323 cl = repo.changelog
324 324 def d():
325 325 len(cl.read(x)[3])
326 326 timer(d)
327 327 fm.end()
328 328
329 329 @command('perflookup', formatteropts)
330 330 def perflookup(ui, repo, rev, **opts):
331 331 timer, fm = gettimer(ui, opts)
332 332
333 333 @command('perflookup', formatteropts)
334 334 def perflookup(ui, repo, rev, **opts):
335 335 timer, fm = gettimer(ui, opts)
336 336 timer(lambda: len(repo.lookup(rev)))
337 337 fm.end()
338 338
339 339 @command('perfrevrange', formatteropts)
340 340 def perfrevrange(ui, repo, *specs, **opts):
341 341 timer, fm = gettimer(ui, opts)
342 342 revrange = scmutil.revrange
343 343 timer(lambda: len(revrange(repo, specs)))
344 344 fm.end()
345 345
346 346 @command('perfnodelookup', formatteropts)
347 347 def perfnodelookup(ui, repo, rev, **opts):
348 348 timer, fm = gettimer(ui, opts)
349 349 import mercurial.revlog
350 350 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
351 351 n = repo[rev].node()
352 352 cl = mercurial.revlog.revlog(repo.svfs, "00changelog.i")
353 353 def d():
354 354 cl.rev(n)
355 355 clearcaches(cl)
356 356 timer(d)
357 357 fm.end()
358 358
359 359 @command('perflog',
360 360 [('', 'rename', False, 'ask log to follow renames')] + formatteropts)
361 361 def perflog(ui, repo, **opts):
362 362 timer, fm = gettimer(ui, opts)
363 363 ui.pushbuffer()
364 364 timer(lambda: commands.log(ui, repo, rev=[], date='', user='',
365 365 copies=opts.get('rename')))
366 366 ui.popbuffer()
367 367 fm.end()
368 368
369 369 @command('perfmoonwalk', formatteropts)
370 370 def perfmoonwalk(ui, repo, **opts):
371 371 """benchmark walking the changelog backwards
372 372
373 373 This also loads the changelog data for each revision in the changelog.
374 374 """
375 375 timer, fm = gettimer(ui, opts)
376 376 def moonwalk():
377 377 for i in xrange(len(repo), -1, -1):
378 378 ctx = repo[i]
379 379 ctx.branch() # read changelog data (in addition to the index)
380 380 timer(moonwalk)
381 381 fm.end()
382 382
383 383 @command('perftemplating', formatteropts)
384 384 def perftemplating(ui, repo, **opts):
385 385 timer, fm = gettimer(ui, opts)
386 386 ui.pushbuffer()
387 387 timer(lambda: commands.log(ui, repo, rev=[], date='', user='',
388 388 template='{date|shortdate} [{rev}:{node|short}]'
389 389 ' {author|person}: {desc|firstline}\n'))
390 390 ui.popbuffer()
391 391 fm.end()
392 392
393 393 @command('perfcca', formatteropts)
394 394 def perfcca(ui, repo, **opts):
395 395 timer, fm = gettimer(ui, opts)
396 396 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
397 397 fm.end()
398 398
399 399 @command('perffncacheload', formatteropts)
400 400 def perffncacheload(ui, repo, **opts):
401 401 timer, fm = gettimer(ui, opts)
402 402 s = repo.store
403 403 def d():
404 404 s.fncache._load()
405 405 timer(d)
406 406 fm.end()
407 407
408 408 @command('perffncachewrite', formatteropts)
409 409 def perffncachewrite(ui, repo, **opts):
410 410 timer, fm = gettimer(ui, opts)
411 411 s = repo.store
412 412 s.fncache._load()
413 413 def d():
414 414 s.fncache._dirty = True
415 415 s.fncache.write()
416 416 timer(d)
417 417 fm.end()
418 418
419 419 @command('perffncacheencode', formatteropts)
420 420 def perffncacheencode(ui, repo, **opts):
421 421 timer, fm = gettimer(ui, opts)
422 422 s = repo.store
423 423 s.fncache._load()
424 424 def d():
425 425 for p in s.fncache.entries:
426 426 s.encode(p)
427 427 timer(d)
428 428 fm.end()
429 429
430 430 @command('perfdiffwd', formatteropts)
431 431 def perfdiffwd(ui, repo, **opts):
432 432 """Profile diff of working directory changes"""
433 433 timer, fm = gettimer(ui, opts)
434 434 options = {
435 435 'w': 'ignore_all_space',
436 436 'b': 'ignore_space_change',
437 437 'B': 'ignore_blank_lines',
438 438 }
439 439
440 440 for diffopt in ('', 'w', 'b', 'B', 'wB'):
441 441 opts = dict((options[c], '1') for c in diffopt)
442 442 def d():
443 443 ui.pushbuffer()
444 444 commands.diff(ui, repo, **opts)
445 445 ui.popbuffer()
446 446 title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
447 447 timer(d, title)
448 448 fm.end()
449 449
450 450 @command('perfrevlog',
451 451 [('d', 'dist', 100, 'distance between the revisions')] + formatteropts,
452 452 "[INDEXFILE]")
453 453 def perfrevlog(ui, repo, file_, **opts):
454 454 timer, fm = gettimer(ui, opts)
455 455 from mercurial import revlog
456 456 dist = opts['dist']
457 457 def d():
458 458 r = revlog.revlog(lambda fn: open(fn, 'rb'), file_)
459 459 for x in xrange(0, len(r), dist):
460 460 r.revision(r.node(x))
461 461
462 462 timer(d)
463 463 fm.end()
464 464
465 465 @command('perfrevset',
466 [('C', 'clear', False, 'clear volatile cache between each call.')]
466 [('C', 'clear', False, 'clear volatile cache between each call.'),
467 ('', 'contexts', False, 'obtain changectx for each revision')]
467 468 + formatteropts, "REVSET")
468 def perfrevset(ui, repo, expr, clear=False, **opts):
469 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
469 470 """benchmark the execution time of a revset
470 471
471 472 Use the --clean option if need to evaluate the impact of build volatile
472 473 revisions set cache on the revset execution. Volatile cache hold filtered
473 474 and obsolete related cache."""
474 475 timer, fm = gettimer(ui, opts)
475 476 def d():
476 477 if clear:
477 478 repo.invalidatevolatilesets()
478 for r in repo.revs(expr): pass
479 if contexts:
480 for ctx in repo.set(expr): pass
481 else:
482 for r in repo.revs(expr): pass
479 483 timer(d)
480 484 fm.end()
481 485
482 486 @command('perfvolatilesets', formatteropts)
483 487 def perfvolatilesets(ui, repo, *names, **opts):
484 488 """benchmark the computation of various volatile set
485 489
486 490 Volatile set computes element related to filtering and obsolescence."""
487 491 timer, fm = gettimer(ui, opts)
488 492 repo = repo.unfiltered()
489 493
490 494 def getobs(name):
491 495 def d():
492 496 repo.invalidatevolatilesets()
493 497 obsolete.getrevs(repo, name)
494 498 return d
495 499
496 500 allobs = sorted(obsolete.cachefuncs)
497 501 if names:
498 502 allobs = [n for n in allobs if n in names]
499 503
500 504 for name in allobs:
501 505 timer(getobs(name), title=name)
502 506
503 507 def getfiltered(name):
504 508 def d():
505 509 repo.invalidatevolatilesets()
506 510 repoview.filterrevs(repo, name)
507 511 return d
508 512
509 513 allfilter = sorted(repoview.filtertable)
510 514 if names:
511 515 allfilter = [n for n in allfilter if n in names]
512 516
513 517 for name in allfilter:
514 518 timer(getfiltered(name), title=name)
515 519 fm.end()
516 520
517 521 @command('perfbranchmap',
518 522 [('f', 'full', False,
519 523 'Includes build time of subset'),
520 524 ] + formatteropts)
521 525 def perfbranchmap(ui, repo, full=False, **opts):
522 526 """benchmark the update of a branchmap
523 527
524 528 This benchmarks the full repo.branchmap() call with read and write disabled
525 529 """
526 530 timer, fm = gettimer(ui, opts)
527 531 def getbranchmap(filtername):
528 532 """generate a benchmark function for the filtername"""
529 533 if filtername is None:
530 534 view = repo
531 535 else:
532 536 view = repo.filtered(filtername)
533 537 def d():
534 538 if full:
535 539 view._branchcaches.clear()
536 540 else:
537 541 view._branchcaches.pop(filtername, None)
538 542 view.branchmap()
539 543 return d
540 544 # add filter in smaller subset to bigger subset
541 545 possiblefilters = set(repoview.filtertable)
542 546 allfilters = []
543 547 while possiblefilters:
544 548 for name in possiblefilters:
545 549 subset = branchmap.subsettable.get(name)
546 550 if subset not in possiblefilters:
547 551 break
548 552 else:
549 553 assert False, 'subset cycle %s!' % possiblefilters
550 554 allfilters.append(name)
551 555 possiblefilters.remove(name)
552 556
553 557 # warm the cache
554 558 if not full:
555 559 for name in allfilters:
556 560 repo.filtered(name).branchmap()
557 561 # add unfiltered
558 562 allfilters.append(None)
559 563 oldread = branchmap.read
560 564 oldwrite = branchmap.branchcache.write
561 565 try:
562 566 branchmap.read = lambda repo: None
563 567 branchmap.write = lambda repo: None
564 568 for name in allfilters:
565 569 timer(getbranchmap(name), title=str(name))
566 570 finally:
567 571 branchmap.read = oldread
568 572 branchmap.branchcache.write = oldwrite
569 573 fm.end()
570 574
571 575 @command('perfloadmarkers')
572 576 def perfloadmarkers(ui, repo):
573 577 """benchmark the time to parse the on-disk markers for a repo
574 578
575 579 Result is the number of markers in the repo."""
576 580 timer, fm = gettimer(ui)
577 581 timer(lambda: len(obsolete.obsstore(repo.svfs)))
578 582 fm.end()
General Comments 0
You need to be logged in to leave comments. Login now