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