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