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