##// END OF EJS Templates
perfphases: add 'perfphases' command...
marmoute -
r32467:ad37c569 default
parent child Browse files
Show More
@@ -1,1345 +1,1356
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 gc
24 24 import os
25 25 import random
26 26 import sys
27 27 import time
28 28 from mercurial import (
29 29 changegroup,
30 30 cmdutil,
31 31 commands,
32 32 copies,
33 33 error,
34 34 extensions,
35 35 mdiff,
36 36 merge,
37 37 util,
38 38 )
39 39
40 40 # for "historical portability":
41 41 # try to import modules separately (in dict order), and ignore
42 42 # failure, because these aren't available with early Mercurial
43 43 try:
44 44 from mercurial import branchmap # since 2.5 (or bcee63733aad)
45 45 except ImportError:
46 46 pass
47 47 try:
48 48 from mercurial import obsolete # since 2.3 (or ad0d6c2b3279)
49 49 except ImportError:
50 50 pass
51 51 try:
52 52 from mercurial import registrar # since 3.7 (or 37d50250b696)
53 53 dir(registrar) # forcibly load it
54 54 except ImportError:
55 55 registrar = None
56 56 try:
57 57 from mercurial import repoview # since 2.5 (or 3a6ddacb7198)
58 58 except ImportError:
59 59 pass
60 60 try:
61 61 from mercurial import scmutil # since 1.9 (or 8b252e826c68)
62 62 except ImportError:
63 63 pass
64 64
65 65 # for "historical portability":
66 66 # define util.safehasattr forcibly, because util.safehasattr has been
67 67 # available since 1.9.3 (or 94b200a11cf7)
68 68 _undefined = object()
69 69 def safehasattr(thing, attr):
70 70 return getattr(thing, attr, _undefined) is not _undefined
71 71 setattr(util, 'safehasattr', safehasattr)
72 72
73 73 # for "historical portability":
74 74 # define util.timer forcibly, because util.timer has been available
75 75 # since ae5d60bb70c9
76 76 if safehasattr(time, 'perf_counter'):
77 77 util.timer = time.perf_counter
78 78 elif os.name == 'nt':
79 79 util.timer = time.clock
80 80 else:
81 81 util.timer = time.time
82 82
83 83 # for "historical portability":
84 84 # use locally defined empty option list, if formatteropts isn't
85 85 # available, because commands.formatteropts has been available since
86 86 # 3.2 (or 7a7eed5176a4), even though formatting itself has been
87 87 # available since 2.2 (or ae5f92e154d3)
88 88 formatteropts = getattr(cmdutil, "formatteropts",
89 89 getattr(commands, "formatteropts", []))
90 90
91 91 # for "historical portability":
92 92 # use locally defined option list, if debugrevlogopts isn't available,
93 93 # because commands.debugrevlogopts has been available since 3.7 (or
94 94 # 5606f7d0d063), even though cmdutil.openrevlog() has been available
95 95 # since 1.9 (or a79fea6b3e77).
96 96 revlogopts = getattr(cmdutil, "debugrevlogopts",
97 97 getattr(commands, "debugrevlogopts", [
98 98 ('c', 'changelog', False, ('open changelog')),
99 99 ('m', 'manifest', False, ('open manifest')),
100 100 ('', 'dir', False, ('open directory manifest')),
101 101 ]))
102 102
103 103 cmdtable = {}
104 104
105 105 # for "historical portability":
106 106 # define parsealiases locally, because cmdutil.parsealiases has been
107 107 # available since 1.5 (or 6252852b4332)
108 108 def parsealiases(cmd):
109 109 return cmd.lstrip("^").split("|")
110 110
111 111 if safehasattr(registrar, 'command'):
112 112 command = registrar.command(cmdtable)
113 113 elif safehasattr(cmdutil, 'command'):
114 114 import inspect
115 115 command = cmdutil.command(cmdtable)
116 116 if 'norepo' not in inspect.getargspec(command)[0]:
117 117 # for "historical portability":
118 118 # wrap original cmdutil.command, because "norepo" option has
119 119 # been available since 3.1 (or 75a96326cecb)
120 120 _command = command
121 121 def command(name, options=(), synopsis=None, norepo=False):
122 122 if norepo:
123 123 commands.norepo += ' %s' % ' '.join(parsealiases(name))
124 124 return _command(name, list(options), synopsis)
125 125 else:
126 126 # for "historical portability":
127 127 # define "@command" annotation locally, because cmdutil.command
128 128 # has been available since 1.9 (or 2daa5179e73f)
129 129 def command(name, options=(), synopsis=None, norepo=False):
130 130 def decorator(func):
131 131 if synopsis:
132 132 cmdtable[name] = func, list(options), synopsis
133 133 else:
134 134 cmdtable[name] = func, list(options)
135 135 if norepo:
136 136 commands.norepo += ' %s' % ' '.join(parsealiases(name))
137 137 return func
138 138 return decorator
139 139
140 140 def getlen(ui):
141 141 if ui.configbool("perf", "stub"):
142 142 return lambda x: 1
143 143 return len
144 144
145 145 def gettimer(ui, opts=None):
146 146 """return a timer function and formatter: (timer, formatter)
147 147
148 148 This function exists to gather the creation of formatter in a single
149 149 place instead of duplicating it in all performance commands."""
150 150
151 151 # enforce an idle period before execution to counteract power management
152 152 # experimental config: perf.presleep
153 153 time.sleep(getint(ui, "perf", "presleep", 1))
154 154
155 155 if opts is None:
156 156 opts = {}
157 157 # redirect all to stderr unless buffer api is in use
158 158 if not ui._buffers:
159 159 ui = ui.copy()
160 160 uifout = safeattrsetter(ui, 'fout', ignoremissing=True)
161 161 if uifout:
162 162 # for "historical portability":
163 163 # ui.fout/ferr have been available since 1.9 (or 4e1ccd4c2b6d)
164 164 uifout.set(ui.ferr)
165 165
166 166 # get a formatter
167 167 uiformatter = getattr(ui, 'formatter', None)
168 168 if uiformatter:
169 169 fm = uiformatter('perf', opts)
170 170 else:
171 171 # for "historical portability":
172 172 # define formatter locally, because ui.formatter has been
173 173 # available since 2.2 (or ae5f92e154d3)
174 174 from mercurial import node
175 175 class defaultformatter(object):
176 176 """Minimized composition of baseformatter and plainformatter
177 177 """
178 178 def __init__(self, ui, topic, opts):
179 179 self._ui = ui
180 180 if ui.debugflag:
181 181 self.hexfunc = node.hex
182 182 else:
183 183 self.hexfunc = node.short
184 184 def __nonzero__(self):
185 185 return False
186 186 __bool__ = __nonzero__
187 187 def startitem(self):
188 188 pass
189 189 def data(self, **data):
190 190 pass
191 191 def write(self, fields, deftext, *fielddata, **opts):
192 192 self._ui.write(deftext % fielddata, **opts)
193 193 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
194 194 if cond:
195 195 self._ui.write(deftext % fielddata, **opts)
196 196 def plain(self, text, **opts):
197 197 self._ui.write(text, **opts)
198 198 def end(self):
199 199 pass
200 200 fm = defaultformatter(ui, 'perf', opts)
201 201
202 202 # stub function, runs code only once instead of in a loop
203 203 # experimental config: perf.stub
204 204 if ui.configbool("perf", "stub"):
205 205 return functools.partial(stub_timer, fm), fm
206 206 return functools.partial(_timer, fm), fm
207 207
208 208 def stub_timer(fm, func, title=None):
209 209 func()
210 210
211 211 def _timer(fm, func, title=None):
212 212 gc.collect()
213 213 results = []
214 214 begin = util.timer()
215 215 count = 0
216 216 while True:
217 217 ostart = os.times()
218 218 cstart = util.timer()
219 219 r = func()
220 220 cstop = util.timer()
221 221 ostop = os.times()
222 222 count += 1
223 223 a, b = ostart, ostop
224 224 results.append((cstop - cstart, b[0] - a[0], b[1]-a[1]))
225 225 if cstop - begin > 3 and count >= 100:
226 226 break
227 227 if cstop - begin > 10 and count >= 3:
228 228 break
229 229
230 230 fm.startitem()
231 231
232 232 if title:
233 233 fm.write('title', '! %s\n', title)
234 234 if r:
235 235 fm.write('result', '! result: %s\n', r)
236 236 m = min(results)
237 237 fm.plain('!')
238 238 fm.write('wall', ' wall %f', m[0])
239 239 fm.write('comb', ' comb %f', m[1] + m[2])
240 240 fm.write('user', ' user %f', m[1])
241 241 fm.write('sys', ' sys %f', m[2])
242 242 fm.write('count', ' (best of %d)', count)
243 243 fm.plain('\n')
244 244
245 245 # utilities for historical portability
246 246
247 247 def getint(ui, section, name, default):
248 248 # for "historical portability":
249 249 # ui.configint has been available since 1.9 (or fa2b596db182)
250 250 v = ui.config(section, name, None)
251 251 if v is None:
252 252 return default
253 253 try:
254 254 return int(v)
255 255 except ValueError:
256 256 raise error.ConfigError(("%s.%s is not an integer ('%s')")
257 257 % (section, name, v))
258 258
259 259 def safeattrsetter(obj, name, ignoremissing=False):
260 260 """Ensure that 'obj' has 'name' attribute before subsequent setattr
261 261
262 262 This function is aborted, if 'obj' doesn't have 'name' attribute
263 263 at runtime. This avoids overlooking removal of an attribute, which
264 264 breaks assumption of performance measurement, in the future.
265 265
266 266 This function returns the object to (1) assign a new value, and
267 267 (2) restore an original value to the attribute.
268 268
269 269 If 'ignoremissing' is true, missing 'name' attribute doesn't cause
270 270 abortion, and this function returns None. This is useful to
271 271 examine an attribute, which isn't ensured in all Mercurial
272 272 versions.
273 273 """
274 274 if not util.safehasattr(obj, name):
275 275 if ignoremissing:
276 276 return None
277 277 raise error.Abort(("missing attribute %s of %s might break assumption"
278 278 " of performance measurement") % (name, obj))
279 279
280 280 origvalue = getattr(obj, name)
281 281 class attrutil(object):
282 282 def set(self, newvalue):
283 283 setattr(obj, name, newvalue)
284 284 def restore(self):
285 285 setattr(obj, name, origvalue)
286 286
287 287 return attrutil()
288 288
289 289 # utilities to examine each internal API changes
290 290
291 291 def getbranchmapsubsettable():
292 292 # for "historical portability":
293 293 # subsettable is defined in:
294 294 # - branchmap since 2.9 (or 175c6fd8cacc)
295 295 # - repoview since 2.5 (or 59a9f18d4587)
296 296 for mod in (branchmap, repoview):
297 297 subsettable = getattr(mod, 'subsettable', None)
298 298 if subsettable:
299 299 return subsettable
300 300
301 301 # bisecting in bcee63733aad::59a9f18d4587 can reach here (both
302 302 # branchmap and repoview modules exist, but subsettable attribute
303 303 # doesn't)
304 304 raise error.Abort(("perfbranchmap not available with this Mercurial"),
305 305 hint="use 2.5 or later")
306 306
307 307 def getsvfs(repo):
308 308 """Return appropriate object to access files under .hg/store
309 309 """
310 310 # for "historical portability":
311 311 # repo.svfs has been available since 2.3 (or 7034365089bf)
312 312 svfs = getattr(repo, 'svfs', None)
313 313 if svfs:
314 314 return svfs
315 315 else:
316 316 return getattr(repo, 'sopener')
317 317
318 318 def getvfs(repo):
319 319 """Return appropriate object to access files under .hg
320 320 """
321 321 # for "historical portability":
322 322 # repo.vfs has been available since 2.3 (or 7034365089bf)
323 323 vfs = getattr(repo, 'vfs', None)
324 324 if vfs:
325 325 return vfs
326 326 else:
327 327 return getattr(repo, 'opener')
328 328
329 329 def repocleartagscachefunc(repo):
330 330 """Return the function to clear tags cache according to repo internal API
331 331 """
332 332 if util.safehasattr(repo, '_tagscache'): # since 2.0 (or 9dca7653b525)
333 333 # in this case, setattr(repo, '_tagscache', None) or so isn't
334 334 # correct way to clear tags cache, because existing code paths
335 335 # expect _tagscache to be a structured object.
336 336 def clearcache():
337 337 # _tagscache has been filteredpropertycache since 2.5 (or
338 338 # 98c867ac1330), and delattr() can't work in such case
339 339 if '_tagscache' in vars(repo):
340 340 del repo.__dict__['_tagscache']
341 341 return clearcache
342 342
343 343 repotags = safeattrsetter(repo, '_tags', ignoremissing=True)
344 344 if repotags: # since 1.4 (or 5614a628d173)
345 345 return lambda : repotags.set(None)
346 346
347 347 repotagscache = safeattrsetter(repo, 'tagscache', ignoremissing=True)
348 348 if repotagscache: # since 0.6 (or d7df759d0e97)
349 349 return lambda : repotagscache.set(None)
350 350
351 351 # Mercurial earlier than 0.6 (or d7df759d0e97) logically reaches
352 352 # this point, but it isn't so problematic, because:
353 353 # - repo.tags of such Mercurial isn't "callable", and repo.tags()
354 354 # in perftags() causes failure soon
355 355 # - perf.py itself has been available since 1.1 (or eb240755386d)
356 356 raise error.Abort(("tags API of this hg command is unknown"))
357 357
358 358 # perf commands
359 359
360 360 @command('perfwalk', formatteropts)
361 361 def perfwalk(ui, repo, *pats, **opts):
362 362 timer, fm = gettimer(ui, opts)
363 363 try:
364 364 m = scmutil.match(repo[None], pats, {})
365 365 timer(lambda: len(list(repo.dirstate.walk(m, [], True, False))))
366 366 except Exception:
367 367 try:
368 368 m = scmutil.match(repo[None], pats, {})
369 369 timer(lambda: len([b for a, b, c in repo.dirstate.statwalk([], m)]))
370 370 except Exception:
371 371 timer(lambda: len(list(cmdutil.walk(repo, pats, {}))))
372 372 fm.end()
373 373
374 374 @command('perfannotate', formatteropts)
375 375 def perfannotate(ui, repo, f, **opts):
376 376 timer, fm = gettimer(ui, opts)
377 377 fc = repo['.'][f]
378 378 timer(lambda: len(fc.annotate(True)))
379 379 fm.end()
380 380
381 381 @command('perfstatus',
382 382 [('u', 'unknown', False,
383 383 'ask status to look for unknown files')] + formatteropts)
384 384 def perfstatus(ui, repo, **opts):
385 385 #m = match.always(repo.root, repo.getcwd())
386 386 #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
387 387 # False))))
388 388 timer, fm = gettimer(ui, opts)
389 389 timer(lambda: sum(map(len, repo.status(unknown=opts['unknown']))))
390 390 fm.end()
391 391
392 392 @command('perfaddremove', formatteropts)
393 393 def perfaddremove(ui, repo, **opts):
394 394 timer, fm = gettimer(ui, opts)
395 395 try:
396 396 oldquiet = repo.ui.quiet
397 397 repo.ui.quiet = True
398 398 matcher = scmutil.match(repo[None])
399 399 timer(lambda: scmutil.addremove(repo, matcher, "", dry_run=True))
400 400 finally:
401 401 repo.ui.quiet = oldquiet
402 402 fm.end()
403 403
404 404 def clearcaches(cl):
405 405 # behave somewhat consistently across internal API changes
406 406 if util.safehasattr(cl, 'clearcaches'):
407 407 cl.clearcaches()
408 408 elif util.safehasattr(cl, '_nodecache'):
409 409 from mercurial.node import nullid, nullrev
410 410 cl._nodecache = {nullid: nullrev}
411 411 cl._nodepos = None
412 412
413 413 @command('perfheads', formatteropts)
414 414 def perfheads(ui, repo, **opts):
415 415 timer, fm = gettimer(ui, opts)
416 416 cl = repo.changelog
417 417 def d():
418 418 len(cl.headrevs())
419 419 clearcaches(cl)
420 420 timer(d)
421 421 fm.end()
422 422
423 423 @command('perftags', formatteropts)
424 424 def perftags(ui, repo, **opts):
425 425 import mercurial.changelog
426 426 import mercurial.manifest
427 427 timer, fm = gettimer(ui, opts)
428 428 svfs = getsvfs(repo)
429 429 repocleartagscache = repocleartagscachefunc(repo)
430 430 def t():
431 431 repo.changelog = mercurial.changelog.changelog(svfs)
432 432 repo.manifestlog = mercurial.manifest.manifestlog(svfs, repo)
433 433 repocleartagscache()
434 434 return len(repo.tags())
435 435 timer(t)
436 436 fm.end()
437 437
438 438 @command('perfancestors', formatteropts)
439 439 def perfancestors(ui, repo, **opts):
440 440 timer, fm = gettimer(ui, opts)
441 441 heads = repo.changelog.headrevs()
442 442 def d():
443 443 for a in repo.changelog.ancestors(heads):
444 444 pass
445 445 timer(d)
446 446 fm.end()
447 447
448 448 @command('perfancestorset', formatteropts)
449 449 def perfancestorset(ui, repo, revset, **opts):
450 450 timer, fm = gettimer(ui, opts)
451 451 revs = repo.revs(revset)
452 452 heads = repo.changelog.headrevs()
453 453 def d():
454 454 s = repo.changelog.ancestors(heads)
455 455 for rev in revs:
456 456 rev in s
457 457 timer(d)
458 458 fm.end()
459 459
460 460 @command('perfchangegroupchangelog', formatteropts +
461 461 [('', 'version', '02', 'changegroup version'),
462 462 ('r', 'rev', '', 'revisions to add to changegroup')])
463 463 def perfchangegroupchangelog(ui, repo, version='02', rev=None, **opts):
464 464 """Benchmark producing a changelog group for a changegroup.
465 465
466 466 This measures the time spent processing the changelog during a
467 467 bundle operation. This occurs during `hg bundle` and on a server
468 468 processing a `getbundle` wire protocol request (handles clones
469 469 and pull requests).
470 470
471 471 By default, all revisions are added to the changegroup.
472 472 """
473 473 cl = repo.changelog
474 474 revs = [cl.lookup(r) for r in repo.revs(rev or 'all()')]
475 475 bundler = changegroup.getbundler(version, repo)
476 476
477 477 def lookup(node):
478 478 # The real bundler reads the revision in order to access the
479 479 # manifest node and files list. Do that here.
480 480 cl.read(node)
481 481 return node
482 482
483 483 def d():
484 484 for chunk in bundler.group(revs, cl, lookup):
485 485 pass
486 486
487 487 timer, fm = gettimer(ui, opts)
488 488 timer(d)
489 489 fm.end()
490 490
491 491 @command('perfdirs', formatteropts)
492 492 def perfdirs(ui, repo, **opts):
493 493 timer, fm = gettimer(ui, opts)
494 494 dirstate = repo.dirstate
495 495 'a' in dirstate
496 496 def d():
497 497 dirstate.dirs()
498 498 del dirstate._dirs
499 499 timer(d)
500 500 fm.end()
501 501
502 502 @command('perfdirstate', formatteropts)
503 503 def perfdirstate(ui, repo, **opts):
504 504 timer, fm = gettimer(ui, opts)
505 505 "a" in repo.dirstate
506 506 def d():
507 507 repo.dirstate.invalidate()
508 508 "a" in repo.dirstate
509 509 timer(d)
510 510 fm.end()
511 511
512 512 @command('perfdirstatedirs', formatteropts)
513 513 def perfdirstatedirs(ui, repo, **opts):
514 514 timer, fm = gettimer(ui, opts)
515 515 "a" in repo.dirstate
516 516 def d():
517 517 "a" in repo.dirstate._dirs
518 518 del repo.dirstate._dirs
519 519 timer(d)
520 520 fm.end()
521 521
522 522 @command('perfdirstatefoldmap', formatteropts)
523 523 def perfdirstatefoldmap(ui, repo, **opts):
524 524 timer, fm = gettimer(ui, opts)
525 525 dirstate = repo.dirstate
526 526 'a' in dirstate
527 527 def d():
528 528 dirstate._filefoldmap.get('a')
529 529 del dirstate._filefoldmap
530 530 timer(d)
531 531 fm.end()
532 532
533 533 @command('perfdirfoldmap', formatteropts)
534 534 def perfdirfoldmap(ui, repo, **opts):
535 535 timer, fm = gettimer(ui, opts)
536 536 dirstate = repo.dirstate
537 537 'a' in dirstate
538 538 def d():
539 539 dirstate._dirfoldmap.get('a')
540 540 del dirstate._dirfoldmap
541 541 del dirstate._dirs
542 542 timer(d)
543 543 fm.end()
544 544
545 545 @command('perfdirstatewrite', formatteropts)
546 546 def perfdirstatewrite(ui, repo, **opts):
547 547 timer, fm = gettimer(ui, opts)
548 548 ds = repo.dirstate
549 549 "a" in ds
550 550 def d():
551 551 ds._dirty = True
552 552 ds.write(repo.currenttransaction())
553 553 timer(d)
554 554 fm.end()
555 555
556 556 @command('perfmergecalculate',
557 557 [('r', 'rev', '.', 'rev to merge against')] + formatteropts)
558 558 def perfmergecalculate(ui, repo, rev, **opts):
559 559 timer, fm = gettimer(ui, opts)
560 560 wctx = repo[None]
561 561 rctx = scmutil.revsingle(repo, rev, rev)
562 562 ancestor = wctx.ancestor(rctx)
563 563 # we don't want working dir files to be stat'd in the benchmark, so prime
564 564 # that cache
565 565 wctx.dirty()
566 566 def d():
567 567 # acceptremote is True because we don't want prompts in the middle of
568 568 # our benchmark
569 569 merge.calculateupdates(repo, wctx, rctx, [ancestor], False, False,
570 570 acceptremote=True, followcopies=True)
571 571 timer(d)
572 572 fm.end()
573 573
574 574 @command('perfpathcopies', [], "REV REV")
575 575 def perfpathcopies(ui, repo, rev1, rev2, **opts):
576 576 timer, fm = gettimer(ui, opts)
577 577 ctx1 = scmutil.revsingle(repo, rev1, rev1)
578 578 ctx2 = scmutil.revsingle(repo, rev2, rev2)
579 579 def d():
580 580 copies.pathcopies(ctx1, ctx2)
581 581 timer(d)
582 582 fm.end()
583 583
584 @command('perfphases', [], "")
585 def perfphases(ui, repo, **opts):
586 """benchmark phasesets computation"""
587 timer, fm = gettimer(ui, opts)
588 phases = repo._phasecache
589 def d():
590 phases.invalidate()
591 phases.loadphaserevs(repo)
592 timer(d)
593 fm.end()
594
584 595 @command('perfmanifest', [], 'REV')
585 596 def perfmanifest(ui, repo, rev, **opts):
586 597 timer, fm = gettimer(ui, opts)
587 598 ctx = scmutil.revsingle(repo, rev, rev)
588 599 t = ctx.manifestnode()
589 600 def d():
590 601 repo.manifestlog.clearcaches()
591 602 repo.manifestlog[t].read()
592 603 timer(d)
593 604 fm.end()
594 605
595 606 @command('perfchangeset', formatteropts)
596 607 def perfchangeset(ui, repo, rev, **opts):
597 608 timer, fm = gettimer(ui, opts)
598 609 n = repo[rev].node()
599 610 def d():
600 611 repo.changelog.read(n)
601 612 #repo.changelog._cache = None
602 613 timer(d)
603 614 fm.end()
604 615
605 616 @command('perfindex', formatteropts)
606 617 def perfindex(ui, repo, **opts):
607 618 import mercurial.revlog
608 619 timer, fm = gettimer(ui, opts)
609 620 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
610 621 n = repo["tip"].node()
611 622 svfs = getsvfs(repo)
612 623 def d():
613 624 cl = mercurial.revlog.revlog(svfs, "00changelog.i")
614 625 cl.rev(n)
615 626 timer(d)
616 627 fm.end()
617 628
618 629 @command('perfstartup', formatteropts)
619 630 def perfstartup(ui, repo, **opts):
620 631 timer, fm = gettimer(ui, opts)
621 632 cmd = sys.argv[0]
622 633 def d():
623 634 if os.name != 'nt':
624 635 os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
625 636 else:
626 637 os.environ['HGRCPATH'] = ''
627 638 os.system("%s version -q > NUL" % cmd)
628 639 timer(d)
629 640 fm.end()
630 641
631 642 @command('perfparents', formatteropts)
632 643 def perfparents(ui, repo, **opts):
633 644 timer, fm = gettimer(ui, opts)
634 645 # control the number of commits perfparents iterates over
635 646 # experimental config: perf.parentscount
636 647 count = getint(ui, "perf", "parentscount", 1000)
637 648 if len(repo.changelog) < count:
638 649 raise error.Abort("repo needs %d commits for this test" % count)
639 650 repo = repo.unfiltered()
640 651 nl = [repo.changelog.node(i) for i in xrange(count)]
641 652 def d():
642 653 for n in nl:
643 654 repo.changelog.parents(n)
644 655 timer(d)
645 656 fm.end()
646 657
647 658 @command('perfctxfiles', formatteropts)
648 659 def perfctxfiles(ui, repo, x, **opts):
649 660 x = int(x)
650 661 timer, fm = gettimer(ui, opts)
651 662 def d():
652 663 len(repo[x].files())
653 664 timer(d)
654 665 fm.end()
655 666
656 667 @command('perfrawfiles', formatteropts)
657 668 def perfrawfiles(ui, repo, x, **opts):
658 669 x = int(x)
659 670 timer, fm = gettimer(ui, opts)
660 671 cl = repo.changelog
661 672 def d():
662 673 len(cl.read(x)[3])
663 674 timer(d)
664 675 fm.end()
665 676
666 677 @command('perflookup', formatteropts)
667 678 def perflookup(ui, repo, rev, **opts):
668 679 timer, fm = gettimer(ui, opts)
669 680 timer(lambda: len(repo.lookup(rev)))
670 681 fm.end()
671 682
672 683 @command('perfrevrange', formatteropts)
673 684 def perfrevrange(ui, repo, *specs, **opts):
674 685 timer, fm = gettimer(ui, opts)
675 686 revrange = scmutil.revrange
676 687 timer(lambda: len(revrange(repo, specs)))
677 688 fm.end()
678 689
679 690 @command('perfnodelookup', formatteropts)
680 691 def perfnodelookup(ui, repo, rev, **opts):
681 692 timer, fm = gettimer(ui, opts)
682 693 import mercurial.revlog
683 694 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
684 695 n = repo[rev].node()
685 696 cl = mercurial.revlog.revlog(getsvfs(repo), "00changelog.i")
686 697 def d():
687 698 cl.rev(n)
688 699 clearcaches(cl)
689 700 timer(d)
690 701 fm.end()
691 702
692 703 @command('perflog',
693 704 [('', 'rename', False, 'ask log to follow renames')] + formatteropts)
694 705 def perflog(ui, repo, rev=None, **opts):
695 706 if rev is None:
696 707 rev=[]
697 708 timer, fm = gettimer(ui, opts)
698 709 ui.pushbuffer()
699 710 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
700 711 copies=opts.get('rename')))
701 712 ui.popbuffer()
702 713 fm.end()
703 714
704 715 @command('perfmoonwalk', formatteropts)
705 716 def perfmoonwalk(ui, repo, **opts):
706 717 """benchmark walking the changelog backwards
707 718
708 719 This also loads the changelog data for each revision in the changelog.
709 720 """
710 721 timer, fm = gettimer(ui, opts)
711 722 def moonwalk():
712 723 for i in xrange(len(repo), -1, -1):
713 724 ctx = repo[i]
714 725 ctx.branch() # read changelog data (in addition to the index)
715 726 timer(moonwalk)
716 727 fm.end()
717 728
718 729 @command('perftemplating', formatteropts)
719 730 def perftemplating(ui, repo, rev=None, **opts):
720 731 if rev is None:
721 732 rev=[]
722 733 timer, fm = gettimer(ui, opts)
723 734 ui.pushbuffer()
724 735 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
725 736 template='{date|shortdate} [{rev}:{node|short}]'
726 737 ' {author|person}: {desc|firstline}\n'))
727 738 ui.popbuffer()
728 739 fm.end()
729 740
730 741 @command('perfcca', formatteropts)
731 742 def perfcca(ui, repo, **opts):
732 743 timer, fm = gettimer(ui, opts)
733 744 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
734 745 fm.end()
735 746
736 747 @command('perffncacheload', formatteropts)
737 748 def perffncacheload(ui, repo, **opts):
738 749 timer, fm = gettimer(ui, opts)
739 750 s = repo.store
740 751 def d():
741 752 s.fncache._load()
742 753 timer(d)
743 754 fm.end()
744 755
745 756 @command('perffncachewrite', formatteropts)
746 757 def perffncachewrite(ui, repo, **opts):
747 758 timer, fm = gettimer(ui, opts)
748 759 s = repo.store
749 760 s.fncache._load()
750 761 lock = repo.lock()
751 762 tr = repo.transaction('perffncachewrite')
752 763 def d():
753 764 s.fncache._dirty = True
754 765 s.fncache.write(tr)
755 766 timer(d)
756 767 tr.close()
757 768 lock.release()
758 769 fm.end()
759 770
760 771 @command('perffncacheencode', formatteropts)
761 772 def perffncacheencode(ui, repo, **opts):
762 773 timer, fm = gettimer(ui, opts)
763 774 s = repo.store
764 775 s.fncache._load()
765 776 def d():
766 777 for p in s.fncache.entries:
767 778 s.encode(p)
768 779 timer(d)
769 780 fm.end()
770 781
771 782 @command('perfbdiff', revlogopts + formatteropts + [
772 783 ('', 'count', 1, 'number of revisions to test (when using --startrev)'),
773 784 ('', 'alldata', False, 'test bdiffs for all associated revisions')],
774 785 '-c|-m|FILE REV')
775 786 def perfbdiff(ui, repo, file_, rev=None, count=None, **opts):
776 787 """benchmark a bdiff between revisions
777 788
778 789 By default, benchmark a bdiff between its delta parent and itself.
779 790
780 791 With ``--count``, benchmark bdiffs between delta parents and self for N
781 792 revisions starting at the specified revision.
782 793
783 794 With ``--alldata``, assume the requested revision is a changeset and
784 795 measure bdiffs for all changes related to that changeset (manifest
785 796 and filelogs).
786 797 """
787 798 if opts['alldata']:
788 799 opts['changelog'] = True
789 800
790 801 if opts.get('changelog') or opts.get('manifest'):
791 802 file_, rev = None, file_
792 803 elif rev is None:
793 804 raise error.CommandError('perfbdiff', 'invalid arguments')
794 805
795 806 textpairs = []
796 807
797 808 r = cmdutil.openrevlog(repo, 'perfbdiff', file_, opts)
798 809
799 810 startrev = r.rev(r.lookup(rev))
800 811 for rev in range(startrev, min(startrev + count, len(r) - 1)):
801 812 if opts['alldata']:
802 813 # Load revisions associated with changeset.
803 814 ctx = repo[rev]
804 815 mtext = repo.manifestlog._revlog.revision(ctx.manifestnode())
805 816 for pctx in ctx.parents():
806 817 pman = repo.manifestlog._revlog.revision(pctx.manifestnode())
807 818 textpairs.append((pman, mtext))
808 819
809 820 # Load filelog revisions by iterating manifest delta.
810 821 man = ctx.manifest()
811 822 pman = ctx.p1().manifest()
812 823 for filename, change in pman.diff(man).items():
813 824 fctx = repo.file(filename)
814 825 f1 = fctx.revision(change[0][0] or -1)
815 826 f2 = fctx.revision(change[1][0] or -1)
816 827 textpairs.append((f1, f2))
817 828 else:
818 829 dp = r.deltaparent(rev)
819 830 textpairs.append((r.revision(dp), r.revision(rev)))
820 831
821 832 def d():
822 833 for pair in textpairs:
823 834 mdiff.textdiff(*pair)
824 835
825 836 timer, fm = gettimer(ui, opts)
826 837 timer(d)
827 838 fm.end()
828 839
829 840 @command('perfdiffwd', formatteropts)
830 841 def perfdiffwd(ui, repo, **opts):
831 842 """Profile diff of working directory changes"""
832 843 timer, fm = gettimer(ui, opts)
833 844 options = {
834 845 'w': 'ignore_all_space',
835 846 'b': 'ignore_space_change',
836 847 'B': 'ignore_blank_lines',
837 848 }
838 849
839 850 for diffopt in ('', 'w', 'b', 'B', 'wB'):
840 851 opts = dict((options[c], '1') for c in diffopt)
841 852 def d():
842 853 ui.pushbuffer()
843 854 commands.diff(ui, repo, **opts)
844 855 ui.popbuffer()
845 856 title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
846 857 timer(d, title)
847 858 fm.end()
848 859
849 860 @command('perfrevlog', revlogopts + formatteropts +
850 861 [('d', 'dist', 100, 'distance between the revisions'),
851 862 ('s', 'startrev', 0, 'revision to start reading at'),
852 863 ('', 'reverse', False, 'read in reverse')],
853 864 '-c|-m|FILE')
854 865 def perfrevlog(ui, repo, file_=None, startrev=0, reverse=False, **opts):
855 866 """Benchmark reading a series of revisions from a revlog.
856 867
857 868 By default, we read every ``-d/--dist`` revision from 0 to tip of
858 869 the specified revlog.
859 870
860 871 The start revision can be defined via ``-s/--startrev``.
861 872 """
862 873 rl = cmdutil.openrevlog(repo, 'perfrevlog', file_, opts)
863 874 rllen = getlen(ui)(rl)
864 875
865 876 def d():
866 877 rl.clearcaches()
867 878
868 879 beginrev = startrev
869 880 endrev = rllen
870 881 dist = opts['dist']
871 882
872 883 if reverse:
873 884 beginrev, endrev = endrev, beginrev
874 885 dist = -1 * dist
875 886
876 887 for x in xrange(beginrev, endrev, dist):
877 888 # Old revisions don't support passing int.
878 889 n = rl.node(x)
879 890 rl.revision(n)
880 891
881 892 timer, fm = gettimer(ui, opts)
882 893 timer(d)
883 894 fm.end()
884 895
885 896 @command('perfrevlogchunks', revlogopts + formatteropts +
886 897 [('e', 'engines', '', 'compression engines to use'),
887 898 ('s', 'startrev', 0, 'revision to start at')],
888 899 '-c|-m|FILE')
889 900 def perfrevlogchunks(ui, repo, file_=None, engines=None, startrev=0, **opts):
890 901 """Benchmark operations on revlog chunks.
891 902
892 903 Logically, each revlog is a collection of fulltext revisions. However,
893 904 stored within each revlog are "chunks" of possibly compressed data. This
894 905 data needs to be read and decompressed or compressed and written.
895 906
896 907 This command measures the time it takes to read+decompress and recompress
897 908 chunks in a revlog. It effectively isolates I/O and compression performance.
898 909 For measurements of higher-level operations like resolving revisions,
899 910 see ``perfrevlog`` and ``perfrevlogrevision``.
900 911 """
901 912 rl = cmdutil.openrevlog(repo, 'perfrevlogchunks', file_, opts)
902 913
903 914 # _chunkraw was renamed to _getsegmentforrevs.
904 915 try:
905 916 segmentforrevs = rl._getsegmentforrevs
906 917 except AttributeError:
907 918 segmentforrevs = rl._chunkraw
908 919
909 920 # Verify engines argument.
910 921 if engines:
911 922 engines = set(e.strip() for e in engines.split(','))
912 923 for engine in engines:
913 924 try:
914 925 util.compressionengines[engine]
915 926 except KeyError:
916 927 raise error.Abort('unknown compression engine: %s' % engine)
917 928 else:
918 929 engines = []
919 930 for e in util.compengines:
920 931 engine = util.compengines[e]
921 932 try:
922 933 if engine.available():
923 934 engine.revlogcompressor().compress('dummy')
924 935 engines.append(e)
925 936 except NotImplementedError:
926 937 pass
927 938
928 939 revs = list(rl.revs(startrev, len(rl) - 1))
929 940
930 941 def rlfh(rl):
931 942 if rl._inline:
932 943 return getsvfs(repo)(rl.indexfile)
933 944 else:
934 945 return getsvfs(repo)(rl.datafile)
935 946
936 947 def doread():
937 948 rl.clearcaches()
938 949 for rev in revs:
939 950 segmentforrevs(rev, rev)
940 951
941 952 def doreadcachedfh():
942 953 rl.clearcaches()
943 954 fh = rlfh(rl)
944 955 for rev in revs:
945 956 segmentforrevs(rev, rev, df=fh)
946 957
947 958 def doreadbatch():
948 959 rl.clearcaches()
949 960 segmentforrevs(revs[0], revs[-1])
950 961
951 962 def doreadbatchcachedfh():
952 963 rl.clearcaches()
953 964 fh = rlfh(rl)
954 965 segmentforrevs(revs[0], revs[-1], df=fh)
955 966
956 967 def dochunk():
957 968 rl.clearcaches()
958 969 fh = rlfh(rl)
959 970 for rev in revs:
960 971 rl._chunk(rev, df=fh)
961 972
962 973 chunks = [None]
963 974
964 975 def dochunkbatch():
965 976 rl.clearcaches()
966 977 fh = rlfh(rl)
967 978 # Save chunks as a side-effect.
968 979 chunks[0] = rl._chunks(revs, df=fh)
969 980
970 981 def docompress(compressor):
971 982 rl.clearcaches()
972 983
973 984 try:
974 985 # Swap in the requested compression engine.
975 986 oldcompressor = rl._compressor
976 987 rl._compressor = compressor
977 988 for chunk in chunks[0]:
978 989 rl.compress(chunk)
979 990 finally:
980 991 rl._compressor = oldcompressor
981 992
982 993 benches = [
983 994 (lambda: doread(), 'read'),
984 995 (lambda: doreadcachedfh(), 'read w/ reused fd'),
985 996 (lambda: doreadbatch(), 'read batch'),
986 997 (lambda: doreadbatchcachedfh(), 'read batch w/ reused fd'),
987 998 (lambda: dochunk(), 'chunk'),
988 999 (lambda: dochunkbatch(), 'chunk batch'),
989 1000 ]
990 1001
991 1002 for engine in sorted(engines):
992 1003 compressor = util.compengines[engine].revlogcompressor()
993 1004 benches.append((functools.partial(docompress, compressor),
994 1005 'compress w/ %s' % engine))
995 1006
996 1007 for fn, title in benches:
997 1008 timer, fm = gettimer(ui, opts)
998 1009 timer(fn, title=title)
999 1010 fm.end()
1000 1011
1001 1012 @command('perfrevlogrevision', revlogopts + formatteropts +
1002 1013 [('', 'cache', False, 'use caches instead of clearing')],
1003 1014 '-c|-m|FILE REV')
1004 1015 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
1005 1016 """Benchmark obtaining a revlog revision.
1006 1017
1007 1018 Obtaining a revlog revision consists of roughly the following steps:
1008 1019
1009 1020 1. Compute the delta chain
1010 1021 2. Obtain the raw chunks for that delta chain
1011 1022 3. Decompress each raw chunk
1012 1023 4. Apply binary patches to obtain fulltext
1013 1024 5. Verify hash of fulltext
1014 1025
1015 1026 This command measures the time spent in each of these phases.
1016 1027 """
1017 1028 if opts.get('changelog') or opts.get('manifest'):
1018 1029 file_, rev = None, file_
1019 1030 elif rev is None:
1020 1031 raise error.CommandError('perfrevlogrevision', 'invalid arguments')
1021 1032
1022 1033 r = cmdutil.openrevlog(repo, 'perfrevlogrevision', file_, opts)
1023 1034
1024 1035 # _chunkraw was renamed to _getsegmentforrevs.
1025 1036 try:
1026 1037 segmentforrevs = r._getsegmentforrevs
1027 1038 except AttributeError:
1028 1039 segmentforrevs = r._chunkraw
1029 1040
1030 1041 node = r.lookup(rev)
1031 1042 rev = r.rev(node)
1032 1043
1033 1044 def getrawchunks(data, chain):
1034 1045 start = r.start
1035 1046 length = r.length
1036 1047 inline = r._inline
1037 1048 iosize = r._io.size
1038 1049 buffer = util.buffer
1039 1050 offset = start(chain[0])
1040 1051
1041 1052 chunks = []
1042 1053 ladd = chunks.append
1043 1054
1044 1055 for rev in chain:
1045 1056 chunkstart = start(rev)
1046 1057 if inline:
1047 1058 chunkstart += (rev + 1) * iosize
1048 1059 chunklength = length(rev)
1049 1060 ladd(buffer(data, chunkstart - offset, chunklength))
1050 1061
1051 1062 return chunks
1052 1063
1053 1064 def dodeltachain(rev):
1054 1065 if not cache:
1055 1066 r.clearcaches()
1056 1067 r._deltachain(rev)
1057 1068
1058 1069 def doread(chain):
1059 1070 if not cache:
1060 1071 r.clearcaches()
1061 1072 segmentforrevs(chain[0], chain[-1])
1062 1073
1063 1074 def dorawchunks(data, chain):
1064 1075 if not cache:
1065 1076 r.clearcaches()
1066 1077 getrawchunks(data, chain)
1067 1078
1068 1079 def dodecompress(chunks):
1069 1080 decomp = r.decompress
1070 1081 for chunk in chunks:
1071 1082 decomp(chunk)
1072 1083
1073 1084 def dopatch(text, bins):
1074 1085 if not cache:
1075 1086 r.clearcaches()
1076 1087 mdiff.patches(text, bins)
1077 1088
1078 1089 def dohash(text):
1079 1090 if not cache:
1080 1091 r.clearcaches()
1081 1092 r.checkhash(text, node, rev=rev)
1082 1093
1083 1094 def dorevision():
1084 1095 if not cache:
1085 1096 r.clearcaches()
1086 1097 r.revision(node)
1087 1098
1088 1099 chain = r._deltachain(rev)[0]
1089 1100 data = segmentforrevs(chain[0], chain[-1])[1]
1090 1101 rawchunks = getrawchunks(data, chain)
1091 1102 bins = r._chunks(chain)
1092 1103 text = str(bins[0])
1093 1104 bins = bins[1:]
1094 1105 text = mdiff.patches(text, bins)
1095 1106
1096 1107 benches = [
1097 1108 (lambda: dorevision(), 'full'),
1098 1109 (lambda: dodeltachain(rev), 'deltachain'),
1099 1110 (lambda: doread(chain), 'read'),
1100 1111 (lambda: dorawchunks(data, chain), 'rawchunks'),
1101 1112 (lambda: dodecompress(rawchunks), 'decompress'),
1102 1113 (lambda: dopatch(text, bins), 'patch'),
1103 1114 (lambda: dohash(text), 'hash'),
1104 1115 ]
1105 1116
1106 1117 for fn, title in benches:
1107 1118 timer, fm = gettimer(ui, opts)
1108 1119 timer(fn, title=title)
1109 1120 fm.end()
1110 1121
1111 1122 @command('perfrevset',
1112 1123 [('C', 'clear', False, 'clear volatile cache between each call.'),
1113 1124 ('', 'contexts', False, 'obtain changectx for each revision')]
1114 1125 + formatteropts, "REVSET")
1115 1126 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
1116 1127 """benchmark the execution time of a revset
1117 1128
1118 1129 Use the --clean option if need to evaluate the impact of build volatile
1119 1130 revisions set cache on the revset execution. Volatile cache hold filtered
1120 1131 and obsolete related cache."""
1121 1132 timer, fm = gettimer(ui, opts)
1122 1133 def d():
1123 1134 if clear:
1124 1135 repo.invalidatevolatilesets()
1125 1136 if contexts:
1126 1137 for ctx in repo.set(expr): pass
1127 1138 else:
1128 1139 for r in repo.revs(expr): pass
1129 1140 timer(d)
1130 1141 fm.end()
1131 1142
1132 1143 def _clearobsstore(repo):
1133 1144 unfi = repo.unfiltered()
1134 1145 if 'obsstore' in vars(unfi):
1135 1146 del unfi.obsstore
1136 1147 del unfi._filecache['obsstore']
1137 1148
1138 1149 @command('perfvolatilesets',
1139 1150 [('', 'clear-obsstore', False, 'drop obsstore between each call.'),
1140 1151 ] + formatteropts)
1141 1152 def perfvolatilesets(ui, repo, *names, **opts):
1142 1153 """benchmark the computation of various volatile set
1143 1154
1144 1155 Volatile set computes element related to filtering and obsolescence."""
1145 1156 timer, fm = gettimer(ui, opts)
1146 1157 repo = repo.unfiltered()
1147 1158
1148 1159 def getobs(name):
1149 1160 def d():
1150 1161 repo.invalidatevolatilesets()
1151 1162 if opts['clear_obsstore']:
1152 1163 _clearobsstore(repo)
1153 1164 obsolete.getrevs(repo, name)
1154 1165 return d
1155 1166
1156 1167 allobs = sorted(obsolete.cachefuncs)
1157 1168 if names:
1158 1169 allobs = [n for n in allobs if n in names]
1159 1170
1160 1171 for name in allobs:
1161 1172 timer(getobs(name), title=name)
1162 1173
1163 1174 def getfiltered(name):
1164 1175 def d():
1165 1176 repo.invalidatevolatilesets()
1166 1177 if opts['clear_obsstore']:
1167 1178 _clearobsstore(repo)
1168 1179 repoview.filterrevs(repo, name)
1169 1180 return d
1170 1181
1171 1182 allfilter = sorted(repoview.filtertable)
1172 1183 if names:
1173 1184 allfilter = [n for n in allfilter if n in names]
1174 1185
1175 1186 for name in allfilter:
1176 1187 timer(getfiltered(name), title=name)
1177 1188 fm.end()
1178 1189
1179 1190 @command('perfbranchmap',
1180 1191 [('f', 'full', False,
1181 1192 'Includes build time of subset'),
1182 1193 ] + formatteropts)
1183 1194 def perfbranchmap(ui, repo, full=False, **opts):
1184 1195 """benchmark the update of a branchmap
1185 1196
1186 1197 This benchmarks the full repo.branchmap() call with read and write disabled
1187 1198 """
1188 1199 timer, fm = gettimer(ui, opts)
1189 1200 def getbranchmap(filtername):
1190 1201 """generate a benchmark function for the filtername"""
1191 1202 if filtername is None:
1192 1203 view = repo
1193 1204 else:
1194 1205 view = repo.filtered(filtername)
1195 1206 def d():
1196 1207 if full:
1197 1208 view._branchcaches.clear()
1198 1209 else:
1199 1210 view._branchcaches.pop(filtername, None)
1200 1211 view.branchmap()
1201 1212 return d
1202 1213 # add filter in smaller subset to bigger subset
1203 1214 possiblefilters = set(repoview.filtertable)
1204 1215 subsettable = getbranchmapsubsettable()
1205 1216 allfilters = []
1206 1217 while possiblefilters:
1207 1218 for name in possiblefilters:
1208 1219 subset = subsettable.get(name)
1209 1220 if subset not in possiblefilters:
1210 1221 break
1211 1222 else:
1212 1223 assert False, 'subset cycle %s!' % possiblefilters
1213 1224 allfilters.append(name)
1214 1225 possiblefilters.remove(name)
1215 1226
1216 1227 # warm the cache
1217 1228 if not full:
1218 1229 for name in allfilters:
1219 1230 repo.filtered(name).branchmap()
1220 1231 # add unfiltered
1221 1232 allfilters.append(None)
1222 1233
1223 1234 branchcacheread = safeattrsetter(branchmap, 'read')
1224 1235 branchcachewrite = safeattrsetter(branchmap.branchcache, 'write')
1225 1236 branchcacheread.set(lambda repo: None)
1226 1237 branchcachewrite.set(lambda bc, repo: None)
1227 1238 try:
1228 1239 for name in allfilters:
1229 1240 timer(getbranchmap(name), title=str(name))
1230 1241 finally:
1231 1242 branchcacheread.restore()
1232 1243 branchcachewrite.restore()
1233 1244 fm.end()
1234 1245
1235 1246 @command('perfloadmarkers')
1236 1247 def perfloadmarkers(ui, repo):
1237 1248 """benchmark the time to parse the on-disk markers for a repo
1238 1249
1239 1250 Result is the number of markers in the repo."""
1240 1251 timer, fm = gettimer(ui)
1241 1252 svfs = getsvfs(repo)
1242 1253 timer(lambda: len(obsolete.obsstore(svfs)))
1243 1254 fm.end()
1244 1255
1245 1256 @command('perflrucachedict', formatteropts +
1246 1257 [('', 'size', 4, 'size of cache'),
1247 1258 ('', 'gets', 10000, 'number of key lookups'),
1248 1259 ('', 'sets', 10000, 'number of key sets'),
1249 1260 ('', 'mixed', 10000, 'number of mixed mode operations'),
1250 1261 ('', 'mixedgetfreq', 50, 'frequency of get vs set ops in mixed mode')],
1251 1262 norepo=True)
1252 1263 def perflrucache(ui, size=4, gets=10000, sets=10000, mixed=10000,
1253 1264 mixedgetfreq=50, **opts):
1254 1265 def doinit():
1255 1266 for i in xrange(10000):
1256 1267 util.lrucachedict(size)
1257 1268
1258 1269 values = []
1259 1270 for i in xrange(size):
1260 1271 values.append(random.randint(0, sys.maxint))
1261 1272
1262 1273 # Get mode fills the cache and tests raw lookup performance with no
1263 1274 # eviction.
1264 1275 getseq = []
1265 1276 for i in xrange(gets):
1266 1277 getseq.append(random.choice(values))
1267 1278
1268 1279 def dogets():
1269 1280 d = util.lrucachedict(size)
1270 1281 for v in values:
1271 1282 d[v] = v
1272 1283 for key in getseq:
1273 1284 value = d[key]
1274 1285 value # silence pyflakes warning
1275 1286
1276 1287 # Set mode tests insertion speed with cache eviction.
1277 1288 setseq = []
1278 1289 for i in xrange(sets):
1279 1290 setseq.append(random.randint(0, sys.maxint))
1280 1291
1281 1292 def dosets():
1282 1293 d = util.lrucachedict(size)
1283 1294 for v in setseq:
1284 1295 d[v] = v
1285 1296
1286 1297 # Mixed mode randomly performs gets and sets with eviction.
1287 1298 mixedops = []
1288 1299 for i in xrange(mixed):
1289 1300 r = random.randint(0, 100)
1290 1301 if r < mixedgetfreq:
1291 1302 op = 0
1292 1303 else:
1293 1304 op = 1
1294 1305
1295 1306 mixedops.append((op, random.randint(0, size * 2)))
1296 1307
1297 1308 def domixed():
1298 1309 d = util.lrucachedict(size)
1299 1310
1300 1311 for op, v in mixedops:
1301 1312 if op == 0:
1302 1313 try:
1303 1314 d[v]
1304 1315 except KeyError:
1305 1316 pass
1306 1317 else:
1307 1318 d[v] = v
1308 1319
1309 1320 benches = [
1310 1321 (doinit, 'init'),
1311 1322 (dogets, 'gets'),
1312 1323 (dosets, 'sets'),
1313 1324 (domixed, 'mixed')
1314 1325 ]
1315 1326
1316 1327 for fn, title in benches:
1317 1328 timer, fm = gettimer(ui, opts)
1318 1329 timer(fn, title=title)
1319 1330 fm.end()
1320 1331
1321 1332 @command('perfwrite', formatteropts)
1322 1333 def perfwrite(ui, repo, **opts):
1323 1334 """microbenchmark ui.write
1324 1335 """
1325 1336 timer, fm = gettimer(ui, opts)
1326 1337 def write():
1327 1338 for i in range(100000):
1328 1339 ui.write(('Testing write performance\n'))
1329 1340 timer(write)
1330 1341 fm.end()
1331 1342
1332 1343 def uisetup(ui):
1333 1344 if (util.safehasattr(cmdutil, 'openrevlog') and
1334 1345 not util.safehasattr(commands, 'debugrevlogopts')):
1335 1346 # for "historical portability":
1336 1347 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
1337 1348 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
1338 1349 # openrevlog() should cause failure, because it has been
1339 1350 # available since 3.5 (or 49c583ca48c4).
1340 1351 def openrevlog(orig, repo, cmd, file_, opts):
1341 1352 if opts.get('dir') and not util.safehasattr(repo, 'dirlog'):
1342 1353 raise error.Abort("This version doesn't support --dir option",
1343 1354 hint="use 3.5 or later")
1344 1355 return orig(repo, cmd, file_, opts)
1345 1356 extensions.wrapfunction(cmdutil, 'openrevlog', openrevlog)
@@ -1,167 +1,168
1 1 #require test-repo
2 2
3 3 Set vars:
4 4
5 5 $ . "$TESTDIR/helpers-testrepo.sh"
6 6 $ CONTRIBDIR="$TESTDIR/../contrib"
7 7
8 8 Prepare repo:
9 9
10 10 $ hg init
11 11
12 12 $ echo this is file a > a
13 13 $ hg add a
14 14 $ hg commit -m first
15 15
16 16 $ echo adding to file a >> a
17 17 $ hg commit -m second
18 18
19 19 $ echo adding more to file a >> a
20 20 $ hg commit -m third
21 21
22 22 $ hg up -r 0
23 23 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
24 24 $ echo merge-this >> a
25 25 $ hg commit -m merge-able
26 26 created new head
27 27
28 28 $ hg up -r 2
29 29 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
30 30
31 31 perfstatus
32 32
33 33 $ cat >> $HGRCPATH << EOF
34 34 > [extensions]
35 35 > perfstatusext=$CONTRIBDIR/perf.py
36 36 > [perf]
37 37 > presleep=0
38 38 > stub=on
39 39 > parentscount=1
40 40 > EOF
41 41 $ hg help perfstatusext
42 42 perfstatusext extension - helper extension to measure performance
43 43
44 44 list of commands:
45 45
46 46 perfaddremove
47 47 (no help text available)
48 48 perfancestors
49 49 (no help text available)
50 50 perfancestorset
51 51 (no help text available)
52 52 perfannotate (no help text available)
53 53 perfbdiff benchmark a bdiff between revisions
54 54 perfbranchmap
55 55 benchmark the update of a branchmap
56 56 perfcca (no help text available)
57 57 perfchangegroupchangelog
58 58 Benchmark producing a changelog group for a changegroup.
59 59 perfchangeset
60 60 (no help text available)
61 61 perfctxfiles (no help text available)
62 62 perfdiffwd Profile diff of working directory changes
63 63 perfdirfoldmap
64 64 (no help text available)
65 65 perfdirs (no help text available)
66 66 perfdirstate (no help text available)
67 67 perfdirstatedirs
68 68 (no help text available)
69 69 perfdirstatefoldmap
70 70 (no help text available)
71 71 perfdirstatewrite
72 72 (no help text available)
73 73 perffncacheencode
74 74 (no help text available)
75 75 perffncacheload
76 76 (no help text available)
77 77 perffncachewrite
78 78 (no help text available)
79 79 perfheads (no help text available)
80 80 perfindex (no help text available)
81 81 perfloadmarkers
82 82 benchmark the time to parse the on-disk markers for a repo
83 83 perflog (no help text available)
84 84 perflookup (no help text available)
85 85 perflrucachedict
86 86 (no help text available)
87 87 perfmanifest (no help text available)
88 88 perfmergecalculate
89 89 (no help text available)
90 90 perfmoonwalk benchmark walking the changelog backwards
91 91 perfnodelookup
92 92 (no help text available)
93 93 perfparents (no help text available)
94 94 perfpathcopies
95 95 (no help text available)
96 perfphases benchmark phasesets computation
96 97 perfrawfiles (no help text available)
97 98 perfrevlog Benchmark reading a series of revisions from a revlog.
98 99 perfrevlogchunks
99 100 Benchmark operations on revlog chunks.
100 101 perfrevlogrevision
101 102 Benchmark obtaining a revlog revision.
102 103 perfrevrange (no help text available)
103 104 perfrevset benchmark the execution time of a revset
104 105 perfstartup (no help text available)
105 106 perfstatus (no help text available)
106 107 perftags (no help text available)
107 108 perftemplating
108 109 (no help text available)
109 110 perfvolatilesets
110 111 benchmark the computation of various volatile set
111 112 perfwalk (no help text available)
112 113 perfwrite microbenchmark ui.write
113 114
114 115 (use 'hg help -v perfstatusext' to show built-in aliases and global options)
115 116 $ hg perfaddremove
116 117 $ hg perfancestors
117 118 $ hg perfancestorset 2
118 119 $ hg perfannotate a
119 120 $ hg perfbdiff -c 1
120 121 $ hg perfbdiff --alldata 1
121 122 $ hg perfbranchmap
122 123 $ hg perfcca
123 124 $ hg perfchangegroupchangelog
124 125 $ hg perfchangeset 2
125 126 $ hg perfctxfiles 2
126 127 $ hg perfdiffwd
127 128 $ hg perfdirfoldmap
128 129 $ hg perfdirs
129 130 $ hg perfdirstate
130 131 $ hg perfdirstatedirs
131 132 $ hg perfdirstatefoldmap
132 133 $ hg perfdirstatewrite
133 134 $ hg perffncacheencode
134 135 $ hg perffncacheload
135 136 $ hg perffncachewrite
136 137 $ hg perfheads
137 138 $ hg perfindex
138 139 $ hg perfloadmarkers
139 140 $ hg perflog
140 141 $ hg perflookup 2
141 142 $ hg perflrucache
142 143 $ hg perfmanifest 2
143 144 $ hg perfmergecalculate -r 3
144 145 $ hg perfmoonwalk
145 146 $ hg perfnodelookup 2
146 147 $ hg perfpathcopies 1 2
147 148 $ hg perfrawfiles 2
148 149 $ hg perfrevlog .hg/store/data/a.i
149 150 $ hg perfrevlogrevision -m 0
150 151 $ hg perfrevlogchunks -c
151 152 $ hg perfrevrange
152 153 $ hg perfrevset 'all()'
153 154 $ hg perfstartup
154 155 $ hg perfstatus
155 156 $ hg perftags
156 157 $ hg perftemplating
157 158 $ hg perfvolatilesets
158 159 $ hg perfwalk
159 160 $ hg perfparents
160 161
161 162 Check perf.py for historical portability
162 163
163 164 $ cd "$TESTDIR/.."
164 165
165 166 $ (hg files -r 1.2 glob:mercurial/*.c glob:mercurial/*.py;
166 167 > hg files -r tip glob:mercurial/*.c glob:mercurial/*.py) |
167 168 > "$TESTDIR"/check-perf-code.py contrib/perf.py
General Comments 0
You need to be logged in to leave comments. Login now