##// END OF EJS Templates
perf: add historical portability policy for future reference
FUJIWARA Katsunori -
r29493:4533f5b4 default
parent child Browse files
Show More
@@ -1,798 +1,815
1 1 # perf.py - performance test routines
2 2 '''helper extension to measure performance'''
3 3
4 # "historical portability" policy of perf.py:
5 #
6 # We have to do:
7 # - make perf.py "loadable" with as wide Mercurial version as possible
8 # This doesn't mean that perf commands work correctly with that Mercurial.
9 # BTW, perf.py itself has been available since 1.1 (or eb240755386d).
10 # - make historical perf command work correctly with as wide Mercurial
11 # version as possible
12 #
13 # We have to do, if possible with reasonable cost:
14 # - make recent perf command for historical feature work correctly
15 # with early Mercurial
16 #
17 # We don't have to do:
18 # - make perf command for recent feature work correctly with early
19 # Mercurial
20
4 21 from __future__ import absolute_import
5 22 import functools
6 23 import os
7 24 import random
8 25 import sys
9 26 import time
10 27 from mercurial import (
11 28 branchmap,
12 29 cmdutil,
13 30 commands,
14 31 copies,
15 32 error,
16 33 mdiff,
17 34 merge,
18 35 obsolete,
19 36 repoview,
20 37 revlog,
21 38 scmutil,
22 39 util,
23 40 )
24 41
25 42 formatteropts = commands.formatteropts
26 43 revlogopts = commands.debugrevlogopts
27 44
28 45 cmdtable = {}
29 46 command = cmdutil.command(cmdtable)
30 47
31 48 def getlen(ui):
32 49 if ui.configbool("perf", "stub"):
33 50 return lambda x: 1
34 51 return len
35 52
36 53 def gettimer(ui, opts=None):
37 54 """return a timer function and formatter: (timer, formatter)
38 55
39 56 This function exists to gather the creation of formatter in a single
40 57 place instead of duplicating it in all performance commands."""
41 58
42 59 # enforce an idle period before execution to counteract power management
43 60 # experimental config: perf.presleep
44 61 time.sleep(ui.configint("perf", "presleep", 1))
45 62
46 63 if opts is None:
47 64 opts = {}
48 65 # redirect all to stderr
49 66 ui = ui.copy()
50 67 ui.fout = ui.ferr
51 68 # get a formatter
52 69 fm = ui.formatter('perf', opts)
53 70 # stub function, runs code only once instead of in a loop
54 71 # experimental config: perf.stub
55 72 if ui.configbool("perf", "stub"):
56 73 return functools.partial(stub_timer, fm), fm
57 74 return functools.partial(_timer, fm), fm
58 75
59 76 def stub_timer(fm, func, title=None):
60 77 func()
61 78
62 79 def _timer(fm, func, title=None):
63 80 results = []
64 81 begin = time.time()
65 82 count = 0
66 83 while True:
67 84 ostart = os.times()
68 85 cstart = time.time()
69 86 r = func()
70 87 cstop = time.time()
71 88 ostop = os.times()
72 89 count += 1
73 90 a, b = ostart, ostop
74 91 results.append((cstop - cstart, b[0] - a[0], b[1]-a[1]))
75 92 if cstop - begin > 3 and count >= 100:
76 93 break
77 94 if cstop - begin > 10 and count >= 3:
78 95 break
79 96
80 97 fm.startitem()
81 98
82 99 if title:
83 100 fm.write('title', '! %s\n', title)
84 101 if r:
85 102 fm.write('result', '! result: %s\n', r)
86 103 m = min(results)
87 104 fm.plain('!')
88 105 fm.write('wall', ' wall %f', m[0])
89 106 fm.write('comb', ' comb %f', m[1] + m[2])
90 107 fm.write('user', ' user %f', m[1])
91 108 fm.write('sys', ' sys %f', m[2])
92 109 fm.write('count', ' (best of %d)', count)
93 110 fm.plain('\n')
94 111
95 112 @command('perfwalk', formatteropts)
96 113 def perfwalk(ui, repo, *pats, **opts):
97 114 timer, fm = gettimer(ui, opts)
98 115 try:
99 116 m = scmutil.match(repo[None], pats, {})
100 117 timer(lambda: len(list(repo.dirstate.walk(m, [], True, False))))
101 118 except Exception:
102 119 try:
103 120 m = scmutil.match(repo[None], pats, {})
104 121 timer(lambda: len([b for a, b, c in repo.dirstate.statwalk([], m)]))
105 122 except Exception:
106 123 timer(lambda: len(list(cmdutil.walk(repo, pats, {}))))
107 124 fm.end()
108 125
109 126 @command('perfannotate', formatteropts)
110 127 def perfannotate(ui, repo, f, **opts):
111 128 timer, fm = gettimer(ui, opts)
112 129 fc = repo['.'][f]
113 130 timer(lambda: len(fc.annotate(True)))
114 131 fm.end()
115 132
116 133 @command('perfstatus',
117 134 [('u', 'unknown', False,
118 135 'ask status to look for unknown files')] + formatteropts)
119 136 def perfstatus(ui, repo, **opts):
120 137 #m = match.always(repo.root, repo.getcwd())
121 138 #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
122 139 # False))))
123 140 timer, fm = gettimer(ui, opts)
124 141 timer(lambda: sum(map(len, repo.status(unknown=opts['unknown']))))
125 142 fm.end()
126 143
127 144 @command('perfaddremove', formatteropts)
128 145 def perfaddremove(ui, repo, **opts):
129 146 timer, fm = gettimer(ui, opts)
130 147 try:
131 148 oldquiet = repo.ui.quiet
132 149 repo.ui.quiet = True
133 150 matcher = scmutil.match(repo[None])
134 151 timer(lambda: scmutil.addremove(repo, matcher, "", dry_run=True))
135 152 finally:
136 153 repo.ui.quiet = oldquiet
137 154 fm.end()
138 155
139 156 def clearcaches(cl):
140 157 # behave somewhat consistently across internal API changes
141 158 if util.safehasattr(cl, 'clearcaches'):
142 159 cl.clearcaches()
143 160 elif util.safehasattr(cl, '_nodecache'):
144 161 from mercurial.node import nullid, nullrev
145 162 cl._nodecache = {nullid: nullrev}
146 163 cl._nodepos = None
147 164
148 165 @command('perfheads', formatteropts)
149 166 def perfheads(ui, repo, **opts):
150 167 timer, fm = gettimer(ui, opts)
151 168 cl = repo.changelog
152 169 def d():
153 170 len(cl.headrevs())
154 171 clearcaches(cl)
155 172 timer(d)
156 173 fm.end()
157 174
158 175 @command('perftags', formatteropts)
159 176 def perftags(ui, repo, **opts):
160 177 import mercurial.changelog
161 178 import mercurial.manifest
162 179 timer, fm = gettimer(ui, opts)
163 180 def t():
164 181 repo.changelog = mercurial.changelog.changelog(repo.svfs)
165 182 repo.manifest = mercurial.manifest.manifest(repo.svfs)
166 183 repo._tags = None
167 184 return len(repo.tags())
168 185 timer(t)
169 186 fm.end()
170 187
171 188 @command('perfancestors', formatteropts)
172 189 def perfancestors(ui, repo, **opts):
173 190 timer, fm = gettimer(ui, opts)
174 191 heads = repo.changelog.headrevs()
175 192 def d():
176 193 for a in repo.changelog.ancestors(heads):
177 194 pass
178 195 timer(d)
179 196 fm.end()
180 197
181 198 @command('perfancestorset', formatteropts)
182 199 def perfancestorset(ui, repo, revset, **opts):
183 200 timer, fm = gettimer(ui, opts)
184 201 revs = repo.revs(revset)
185 202 heads = repo.changelog.headrevs()
186 203 def d():
187 204 s = repo.changelog.ancestors(heads)
188 205 for rev in revs:
189 206 rev in s
190 207 timer(d)
191 208 fm.end()
192 209
193 210 @command('perfdirs', formatteropts)
194 211 def perfdirs(ui, repo, **opts):
195 212 timer, fm = gettimer(ui, opts)
196 213 dirstate = repo.dirstate
197 214 'a' in dirstate
198 215 def d():
199 216 dirstate.dirs()
200 217 del dirstate._dirs
201 218 timer(d)
202 219 fm.end()
203 220
204 221 @command('perfdirstate', formatteropts)
205 222 def perfdirstate(ui, repo, **opts):
206 223 timer, fm = gettimer(ui, opts)
207 224 "a" in repo.dirstate
208 225 def d():
209 226 repo.dirstate.invalidate()
210 227 "a" in repo.dirstate
211 228 timer(d)
212 229 fm.end()
213 230
214 231 @command('perfdirstatedirs', formatteropts)
215 232 def perfdirstatedirs(ui, repo, **opts):
216 233 timer, fm = gettimer(ui, opts)
217 234 "a" in repo.dirstate
218 235 def d():
219 236 "a" in repo.dirstate._dirs
220 237 del repo.dirstate._dirs
221 238 timer(d)
222 239 fm.end()
223 240
224 241 @command('perfdirstatefoldmap', formatteropts)
225 242 def perfdirstatefoldmap(ui, repo, **opts):
226 243 timer, fm = gettimer(ui, opts)
227 244 dirstate = repo.dirstate
228 245 'a' in dirstate
229 246 def d():
230 247 dirstate._filefoldmap.get('a')
231 248 del dirstate._filefoldmap
232 249 timer(d)
233 250 fm.end()
234 251
235 252 @command('perfdirfoldmap', formatteropts)
236 253 def perfdirfoldmap(ui, repo, **opts):
237 254 timer, fm = gettimer(ui, opts)
238 255 dirstate = repo.dirstate
239 256 'a' in dirstate
240 257 def d():
241 258 dirstate._dirfoldmap.get('a')
242 259 del dirstate._dirfoldmap
243 260 del dirstate._dirs
244 261 timer(d)
245 262 fm.end()
246 263
247 264 @command('perfdirstatewrite', formatteropts)
248 265 def perfdirstatewrite(ui, repo, **opts):
249 266 timer, fm = gettimer(ui, opts)
250 267 ds = repo.dirstate
251 268 "a" in ds
252 269 def d():
253 270 ds._dirty = True
254 271 ds.write(repo.currenttransaction())
255 272 timer(d)
256 273 fm.end()
257 274
258 275 @command('perfmergecalculate',
259 276 [('r', 'rev', '.', 'rev to merge against')] + formatteropts)
260 277 def perfmergecalculate(ui, repo, rev, **opts):
261 278 timer, fm = gettimer(ui, opts)
262 279 wctx = repo[None]
263 280 rctx = scmutil.revsingle(repo, rev, rev)
264 281 ancestor = wctx.ancestor(rctx)
265 282 # we don't want working dir files to be stat'd in the benchmark, so prime
266 283 # that cache
267 284 wctx.dirty()
268 285 def d():
269 286 # acceptremote is True because we don't want prompts in the middle of
270 287 # our benchmark
271 288 merge.calculateupdates(repo, wctx, rctx, [ancestor], False, False,
272 289 acceptremote=True, followcopies=True)
273 290 timer(d)
274 291 fm.end()
275 292
276 293 @command('perfpathcopies', [], "REV REV")
277 294 def perfpathcopies(ui, repo, rev1, rev2, **opts):
278 295 timer, fm = gettimer(ui, opts)
279 296 ctx1 = scmutil.revsingle(repo, rev1, rev1)
280 297 ctx2 = scmutil.revsingle(repo, rev2, rev2)
281 298 def d():
282 299 copies.pathcopies(ctx1, ctx2)
283 300 timer(d)
284 301 fm.end()
285 302
286 303 @command('perfmanifest', [], 'REV')
287 304 def perfmanifest(ui, repo, rev, **opts):
288 305 timer, fm = gettimer(ui, opts)
289 306 ctx = scmutil.revsingle(repo, rev, rev)
290 307 t = ctx.manifestnode()
291 308 def d():
292 309 repo.manifest.clearcaches()
293 310 repo.manifest.read(t)
294 311 timer(d)
295 312 fm.end()
296 313
297 314 @command('perfchangeset', formatteropts)
298 315 def perfchangeset(ui, repo, rev, **opts):
299 316 timer, fm = gettimer(ui, opts)
300 317 n = repo[rev].node()
301 318 def d():
302 319 repo.changelog.read(n)
303 320 #repo.changelog._cache = None
304 321 timer(d)
305 322 fm.end()
306 323
307 324 @command('perfindex', formatteropts)
308 325 def perfindex(ui, repo, **opts):
309 326 import mercurial.revlog
310 327 timer, fm = gettimer(ui, opts)
311 328 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
312 329 n = repo["tip"].node()
313 330 def d():
314 331 cl = mercurial.revlog.revlog(repo.svfs, "00changelog.i")
315 332 cl.rev(n)
316 333 timer(d)
317 334 fm.end()
318 335
319 336 @command('perfstartup', formatteropts)
320 337 def perfstartup(ui, repo, **opts):
321 338 timer, fm = gettimer(ui, opts)
322 339 cmd = sys.argv[0]
323 340 def d():
324 341 if os.name != 'nt':
325 342 os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
326 343 else:
327 344 os.environ['HGRCPATH'] = ''
328 345 os.system("%s version -q > NUL" % cmd)
329 346 timer(d)
330 347 fm.end()
331 348
332 349 @command('perfparents', formatteropts)
333 350 def perfparents(ui, repo, **opts):
334 351 timer, fm = gettimer(ui, opts)
335 352 # control the number of commits perfparents iterates over
336 353 # experimental config: perf.parentscount
337 354 count = ui.configint("perf", "parentscount", 1000)
338 355 if len(repo.changelog) < count:
339 356 raise error.Abort("repo needs %d commits for this test" % count)
340 357 repo = repo.unfiltered()
341 358 nl = [repo.changelog.node(i) for i in xrange(count)]
342 359 def d():
343 360 for n in nl:
344 361 repo.changelog.parents(n)
345 362 timer(d)
346 363 fm.end()
347 364
348 365 @command('perfctxfiles', formatteropts)
349 366 def perfctxfiles(ui, repo, x, **opts):
350 367 x = int(x)
351 368 timer, fm = gettimer(ui, opts)
352 369 def d():
353 370 len(repo[x].files())
354 371 timer(d)
355 372 fm.end()
356 373
357 374 @command('perfrawfiles', formatteropts)
358 375 def perfrawfiles(ui, repo, x, **opts):
359 376 x = int(x)
360 377 timer, fm = gettimer(ui, opts)
361 378 cl = repo.changelog
362 379 def d():
363 380 len(cl.read(x)[3])
364 381 timer(d)
365 382 fm.end()
366 383
367 384 @command('perflookup', formatteropts)
368 385 def perflookup(ui, repo, rev, **opts):
369 386 timer, fm = gettimer(ui, opts)
370 387 timer(lambda: len(repo.lookup(rev)))
371 388 fm.end()
372 389
373 390 @command('perfrevrange', formatteropts)
374 391 def perfrevrange(ui, repo, *specs, **opts):
375 392 timer, fm = gettimer(ui, opts)
376 393 revrange = scmutil.revrange
377 394 timer(lambda: len(revrange(repo, specs)))
378 395 fm.end()
379 396
380 397 @command('perfnodelookup', formatteropts)
381 398 def perfnodelookup(ui, repo, rev, **opts):
382 399 timer, fm = gettimer(ui, opts)
383 400 import mercurial.revlog
384 401 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
385 402 n = repo[rev].node()
386 403 cl = mercurial.revlog.revlog(repo.svfs, "00changelog.i")
387 404 def d():
388 405 cl.rev(n)
389 406 clearcaches(cl)
390 407 timer(d)
391 408 fm.end()
392 409
393 410 @command('perflog',
394 411 [('', 'rename', False, 'ask log to follow renames')] + formatteropts)
395 412 def perflog(ui, repo, rev=None, **opts):
396 413 if rev is None:
397 414 rev=[]
398 415 timer, fm = gettimer(ui, opts)
399 416 ui.pushbuffer()
400 417 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
401 418 copies=opts.get('rename')))
402 419 ui.popbuffer()
403 420 fm.end()
404 421
405 422 @command('perfmoonwalk', formatteropts)
406 423 def perfmoonwalk(ui, repo, **opts):
407 424 """benchmark walking the changelog backwards
408 425
409 426 This also loads the changelog data for each revision in the changelog.
410 427 """
411 428 timer, fm = gettimer(ui, opts)
412 429 def moonwalk():
413 430 for i in xrange(len(repo), -1, -1):
414 431 ctx = repo[i]
415 432 ctx.branch() # read changelog data (in addition to the index)
416 433 timer(moonwalk)
417 434 fm.end()
418 435
419 436 @command('perftemplating', formatteropts)
420 437 def perftemplating(ui, repo, rev=None, **opts):
421 438 if rev is None:
422 439 rev=[]
423 440 timer, fm = gettimer(ui, opts)
424 441 ui.pushbuffer()
425 442 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
426 443 template='{date|shortdate} [{rev}:{node|short}]'
427 444 ' {author|person}: {desc|firstline}\n'))
428 445 ui.popbuffer()
429 446 fm.end()
430 447
431 448 @command('perfcca', formatteropts)
432 449 def perfcca(ui, repo, **opts):
433 450 timer, fm = gettimer(ui, opts)
434 451 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
435 452 fm.end()
436 453
437 454 @command('perffncacheload', formatteropts)
438 455 def perffncacheload(ui, repo, **opts):
439 456 timer, fm = gettimer(ui, opts)
440 457 s = repo.store
441 458 def d():
442 459 s.fncache._load()
443 460 timer(d)
444 461 fm.end()
445 462
446 463 @command('perffncachewrite', formatteropts)
447 464 def perffncachewrite(ui, repo, **opts):
448 465 timer, fm = gettimer(ui, opts)
449 466 s = repo.store
450 467 s.fncache._load()
451 468 lock = repo.lock()
452 469 tr = repo.transaction('perffncachewrite')
453 470 def d():
454 471 s.fncache._dirty = True
455 472 s.fncache.write(tr)
456 473 timer(d)
457 474 lock.release()
458 475 tr.close()
459 476 fm.end()
460 477
461 478 @command('perffncacheencode', formatteropts)
462 479 def perffncacheencode(ui, repo, **opts):
463 480 timer, fm = gettimer(ui, opts)
464 481 s = repo.store
465 482 s.fncache._load()
466 483 def d():
467 484 for p in s.fncache.entries:
468 485 s.encode(p)
469 486 timer(d)
470 487 fm.end()
471 488
472 489 @command('perfdiffwd', formatteropts)
473 490 def perfdiffwd(ui, repo, **opts):
474 491 """Profile diff of working directory changes"""
475 492 timer, fm = gettimer(ui, opts)
476 493 options = {
477 494 'w': 'ignore_all_space',
478 495 'b': 'ignore_space_change',
479 496 'B': 'ignore_blank_lines',
480 497 }
481 498
482 499 for diffopt in ('', 'w', 'b', 'B', 'wB'):
483 500 opts = dict((options[c], '1') for c in diffopt)
484 501 def d():
485 502 ui.pushbuffer()
486 503 commands.diff(ui, repo, **opts)
487 504 ui.popbuffer()
488 505 title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
489 506 timer(d, title)
490 507 fm.end()
491 508
492 509 @command('perfrevlog', revlogopts + formatteropts +
493 510 [('d', 'dist', 100, 'distance between the revisions'),
494 511 ('s', 'startrev', 0, 'revision to start reading at')],
495 512 '-c|-m|FILE')
496 513 def perfrevlog(ui, repo, file_=None, startrev=0, **opts):
497 514 """Benchmark reading a series of revisions from a revlog.
498 515
499 516 By default, we read every ``-d/--dist`` revision from 0 to tip of
500 517 the specified revlog.
501 518
502 519 The start revision can be defined via ``-s/--startrev``.
503 520 """
504 521 timer, fm = gettimer(ui, opts)
505 522 dist = opts['dist']
506 523 _len = getlen(ui)
507 524 def d():
508 525 r = cmdutil.openrevlog(repo, 'perfrevlog', file_, opts)
509 526 for x in xrange(startrev, _len(r), dist):
510 527 r.revision(r.node(x))
511 528
512 529 timer(d)
513 530 fm.end()
514 531
515 532 @command('perfrevlogrevision', revlogopts + formatteropts +
516 533 [('', 'cache', False, 'use caches instead of clearing')],
517 534 '-c|-m|FILE REV')
518 535 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
519 536 """Benchmark obtaining a revlog revision.
520 537
521 538 Obtaining a revlog revision consists of roughly the following steps:
522 539
523 540 1. Compute the delta chain
524 541 2. Obtain the raw chunks for that delta chain
525 542 3. Decompress each raw chunk
526 543 4. Apply binary patches to obtain fulltext
527 544 5. Verify hash of fulltext
528 545
529 546 This command measures the time spent in each of these phases.
530 547 """
531 548 if opts.get('changelog') or opts.get('manifest'):
532 549 file_, rev = None, file_
533 550 elif rev is None:
534 551 raise error.CommandError('perfrevlogrevision', 'invalid arguments')
535 552
536 553 r = cmdutil.openrevlog(repo, 'perfrevlogrevision', file_, opts)
537 554 node = r.lookup(rev)
538 555 rev = r.rev(node)
539 556
540 557 def dodeltachain(rev):
541 558 if not cache:
542 559 r.clearcaches()
543 560 r._deltachain(rev)
544 561
545 562 def doread(chain):
546 563 if not cache:
547 564 r.clearcaches()
548 565 r._chunkraw(chain[0], chain[-1])
549 566
550 567 def dodecompress(data, chain):
551 568 if not cache:
552 569 r.clearcaches()
553 570
554 571 start = r.start
555 572 length = r.length
556 573 inline = r._inline
557 574 iosize = r._io.size
558 575 buffer = util.buffer
559 576 offset = start(chain[0])
560 577
561 578 for rev in chain:
562 579 chunkstart = start(rev)
563 580 if inline:
564 581 chunkstart += (rev + 1) * iosize
565 582 chunklength = length(rev)
566 583 b = buffer(data, chunkstart - offset, chunklength)
567 584 revlog.decompress(b)
568 585
569 586 def dopatch(text, bins):
570 587 if not cache:
571 588 r.clearcaches()
572 589 mdiff.patches(text, bins)
573 590
574 591 def dohash(text):
575 592 if not cache:
576 593 r.clearcaches()
577 594 r._checkhash(text, node, rev)
578 595
579 596 def dorevision():
580 597 if not cache:
581 598 r.clearcaches()
582 599 r.revision(node)
583 600
584 601 chain = r._deltachain(rev)[0]
585 602 data = r._chunkraw(chain[0], chain[-1])[1]
586 603 bins = r._chunks(chain)
587 604 text = str(bins[0])
588 605 bins = bins[1:]
589 606 text = mdiff.patches(text, bins)
590 607
591 608 benches = [
592 609 (lambda: dorevision(), 'full'),
593 610 (lambda: dodeltachain(rev), 'deltachain'),
594 611 (lambda: doread(chain), 'read'),
595 612 (lambda: dodecompress(data, chain), 'decompress'),
596 613 (lambda: dopatch(text, bins), 'patch'),
597 614 (lambda: dohash(text), 'hash'),
598 615 ]
599 616
600 617 for fn, title in benches:
601 618 timer, fm = gettimer(ui, opts)
602 619 timer(fn, title=title)
603 620 fm.end()
604 621
605 622 @command('perfrevset',
606 623 [('C', 'clear', False, 'clear volatile cache between each call.'),
607 624 ('', 'contexts', False, 'obtain changectx for each revision')]
608 625 + formatteropts, "REVSET")
609 626 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
610 627 """benchmark the execution time of a revset
611 628
612 629 Use the --clean option if need to evaluate the impact of build volatile
613 630 revisions set cache on the revset execution. Volatile cache hold filtered
614 631 and obsolete related cache."""
615 632 timer, fm = gettimer(ui, opts)
616 633 def d():
617 634 if clear:
618 635 repo.invalidatevolatilesets()
619 636 if contexts:
620 637 for ctx in repo.set(expr): pass
621 638 else:
622 639 for r in repo.revs(expr): pass
623 640 timer(d)
624 641 fm.end()
625 642
626 643 @command('perfvolatilesets', formatteropts)
627 644 def perfvolatilesets(ui, repo, *names, **opts):
628 645 """benchmark the computation of various volatile set
629 646
630 647 Volatile set computes element related to filtering and obsolescence."""
631 648 timer, fm = gettimer(ui, opts)
632 649 repo = repo.unfiltered()
633 650
634 651 def getobs(name):
635 652 def d():
636 653 repo.invalidatevolatilesets()
637 654 obsolete.getrevs(repo, name)
638 655 return d
639 656
640 657 allobs = sorted(obsolete.cachefuncs)
641 658 if names:
642 659 allobs = [n for n in allobs if n in names]
643 660
644 661 for name in allobs:
645 662 timer(getobs(name), title=name)
646 663
647 664 def getfiltered(name):
648 665 def d():
649 666 repo.invalidatevolatilesets()
650 667 repoview.filterrevs(repo, name)
651 668 return d
652 669
653 670 allfilter = sorted(repoview.filtertable)
654 671 if names:
655 672 allfilter = [n for n in allfilter if n in names]
656 673
657 674 for name in allfilter:
658 675 timer(getfiltered(name), title=name)
659 676 fm.end()
660 677
661 678 @command('perfbranchmap',
662 679 [('f', 'full', False,
663 680 'Includes build time of subset'),
664 681 ] + formatteropts)
665 682 def perfbranchmap(ui, repo, full=False, **opts):
666 683 """benchmark the update of a branchmap
667 684
668 685 This benchmarks the full repo.branchmap() call with read and write disabled
669 686 """
670 687 timer, fm = gettimer(ui, opts)
671 688 def getbranchmap(filtername):
672 689 """generate a benchmark function for the filtername"""
673 690 if filtername is None:
674 691 view = repo
675 692 else:
676 693 view = repo.filtered(filtername)
677 694 def d():
678 695 if full:
679 696 view._branchcaches.clear()
680 697 else:
681 698 view._branchcaches.pop(filtername, None)
682 699 view.branchmap()
683 700 return d
684 701 # add filter in smaller subset to bigger subset
685 702 possiblefilters = set(repoview.filtertable)
686 703 allfilters = []
687 704 while possiblefilters:
688 705 for name in possiblefilters:
689 706 subset = branchmap.subsettable.get(name)
690 707 if subset not in possiblefilters:
691 708 break
692 709 else:
693 710 assert False, 'subset cycle %s!' % possiblefilters
694 711 allfilters.append(name)
695 712 possiblefilters.remove(name)
696 713
697 714 # warm the cache
698 715 if not full:
699 716 for name in allfilters:
700 717 repo.filtered(name).branchmap()
701 718 # add unfiltered
702 719 allfilters.append(None)
703 720 oldread = branchmap.read
704 721 oldwrite = branchmap.branchcache.write
705 722 try:
706 723 branchmap.read = lambda repo: None
707 724 branchmap.write = lambda repo: None
708 725 for name in allfilters:
709 726 timer(getbranchmap(name), title=str(name))
710 727 finally:
711 728 branchmap.read = oldread
712 729 branchmap.branchcache.write = oldwrite
713 730 fm.end()
714 731
715 732 @command('perfloadmarkers')
716 733 def perfloadmarkers(ui, repo):
717 734 """benchmark the time to parse the on-disk markers for a repo
718 735
719 736 Result is the number of markers in the repo."""
720 737 timer, fm = gettimer(ui)
721 738 timer(lambda: len(obsolete.obsstore(repo.svfs)))
722 739 fm.end()
723 740
724 741 @command('perflrucachedict', formatteropts +
725 742 [('', 'size', 4, 'size of cache'),
726 743 ('', 'gets', 10000, 'number of key lookups'),
727 744 ('', 'sets', 10000, 'number of key sets'),
728 745 ('', 'mixed', 10000, 'number of mixed mode operations'),
729 746 ('', 'mixedgetfreq', 50, 'frequency of get vs set ops in mixed mode')],
730 747 norepo=True)
731 748 def perflrucache(ui, size=4, gets=10000, sets=10000, mixed=10000,
732 749 mixedgetfreq=50, **opts):
733 750 def doinit():
734 751 for i in xrange(10000):
735 752 util.lrucachedict(size)
736 753
737 754 values = []
738 755 for i in xrange(size):
739 756 values.append(random.randint(0, sys.maxint))
740 757
741 758 # Get mode fills the cache and tests raw lookup performance with no
742 759 # eviction.
743 760 getseq = []
744 761 for i in xrange(gets):
745 762 getseq.append(random.choice(values))
746 763
747 764 def dogets():
748 765 d = util.lrucachedict(size)
749 766 for v in values:
750 767 d[v] = v
751 768 for key in getseq:
752 769 value = d[key]
753 770 value # silence pyflakes warning
754 771
755 772 # Set mode tests insertion speed with cache eviction.
756 773 setseq = []
757 774 for i in xrange(sets):
758 775 setseq.append(random.randint(0, sys.maxint))
759 776
760 777 def dosets():
761 778 d = util.lrucachedict(size)
762 779 for v in setseq:
763 780 d[v] = v
764 781
765 782 # Mixed mode randomly performs gets and sets with eviction.
766 783 mixedops = []
767 784 for i in xrange(mixed):
768 785 r = random.randint(0, 100)
769 786 if r < mixedgetfreq:
770 787 op = 0
771 788 else:
772 789 op = 1
773 790
774 791 mixedops.append((op, random.randint(0, size * 2)))
775 792
776 793 def domixed():
777 794 d = util.lrucachedict(size)
778 795
779 796 for op, v in mixedops:
780 797 if op == 0:
781 798 try:
782 799 d[v]
783 800 except KeyError:
784 801 pass
785 802 else:
786 803 d[v] = v
787 804
788 805 benches = [
789 806 (doinit, 'init'),
790 807 (dogets, 'gets'),
791 808 (dosets, 'sets'),
792 809 (domixed, 'mixed')
793 810 ]
794 811
795 812 for fn, title in benches:
796 813 timer, fm = gettimer(ui, opts)
797 814 timer(fn, title=title)
798 815 fm.end()
General Comments 0
You need to be logged in to leave comments. Login now