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