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