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