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