##// END OF EJS Templates
perf: release lock after transaction in perffncachewrite...
Pierre-Yves David -
r30069:98b9846a default
parent child Browse files
Show More
@@ -1,944 +1,944
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 changegroup,
29 29 cmdutil,
30 30 commands,
31 31 copies,
32 32 error,
33 33 extensions,
34 34 mdiff,
35 35 merge,
36 36 revlog,
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(ui.configint("perf", "presleep", 1))
135 135
136 136 if opts is None:
137 137 opts = {}
138 138 # redirect all to stderr
139 139 ui = ui.copy()
140 140 ui.fout = ui.ferr
141 141 # get a formatter
142 142 fm = ui.formatter('perf', opts)
143 143 # stub function, runs code only once instead of in a loop
144 144 # experimental config: perf.stub
145 145 if ui.configbool("perf", "stub"):
146 146 return functools.partial(stub_timer, fm), fm
147 147 return functools.partial(_timer, fm), fm
148 148
149 149 def stub_timer(fm, func, title=None):
150 150 func()
151 151
152 152 def _timer(fm, func, title=None):
153 153 results = []
154 154 begin = time.time()
155 155 count = 0
156 156 while True:
157 157 ostart = os.times()
158 158 cstart = time.time()
159 159 r = func()
160 160 cstop = time.time()
161 161 ostop = os.times()
162 162 count += 1
163 163 a, b = ostart, ostop
164 164 results.append((cstop - cstart, b[0] - a[0], b[1]-a[1]))
165 165 if cstop - begin > 3 and count >= 100:
166 166 break
167 167 if cstop - begin > 10 and count >= 3:
168 168 break
169 169
170 170 fm.startitem()
171 171
172 172 if title:
173 173 fm.write('title', '! %s\n', title)
174 174 if r:
175 175 fm.write('result', '! result: %s\n', r)
176 176 m = min(results)
177 177 fm.plain('!')
178 178 fm.write('wall', ' wall %f', m[0])
179 179 fm.write('comb', ' comb %f', m[1] + m[2])
180 180 fm.write('user', ' user %f', m[1])
181 181 fm.write('sys', ' sys %f', m[2])
182 182 fm.write('count', ' (best of %d)', count)
183 183 fm.plain('\n')
184 184
185 185 @command('perfwalk', formatteropts)
186 186 def perfwalk(ui, repo, *pats, **opts):
187 187 timer, fm = gettimer(ui, opts)
188 188 try:
189 189 m = scmutil.match(repo[None], pats, {})
190 190 timer(lambda: len(list(repo.dirstate.walk(m, [], True, False))))
191 191 except Exception:
192 192 try:
193 193 m = scmutil.match(repo[None], pats, {})
194 194 timer(lambda: len([b for a, b, c in repo.dirstate.statwalk([], m)]))
195 195 except Exception:
196 196 timer(lambda: len(list(cmdutil.walk(repo, pats, {}))))
197 197 fm.end()
198 198
199 199 @command('perfannotate', formatteropts)
200 200 def perfannotate(ui, repo, f, **opts):
201 201 timer, fm = gettimer(ui, opts)
202 202 fc = repo['.'][f]
203 203 timer(lambda: len(fc.annotate(True)))
204 204 fm.end()
205 205
206 206 @command('perfstatus',
207 207 [('u', 'unknown', False,
208 208 'ask status to look for unknown files')] + formatteropts)
209 209 def perfstatus(ui, repo, **opts):
210 210 #m = match.always(repo.root, repo.getcwd())
211 211 #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
212 212 # False))))
213 213 timer, fm = gettimer(ui, opts)
214 214 timer(lambda: sum(map(len, repo.status(unknown=opts['unknown']))))
215 215 fm.end()
216 216
217 217 @command('perfaddremove', formatteropts)
218 218 def perfaddremove(ui, repo, **opts):
219 219 timer, fm = gettimer(ui, opts)
220 220 try:
221 221 oldquiet = repo.ui.quiet
222 222 repo.ui.quiet = True
223 223 matcher = scmutil.match(repo[None])
224 224 timer(lambda: scmutil.addremove(repo, matcher, "", dry_run=True))
225 225 finally:
226 226 repo.ui.quiet = oldquiet
227 227 fm.end()
228 228
229 229 def clearcaches(cl):
230 230 # behave somewhat consistently across internal API changes
231 231 if util.safehasattr(cl, 'clearcaches'):
232 232 cl.clearcaches()
233 233 elif util.safehasattr(cl, '_nodecache'):
234 234 from mercurial.node import nullid, nullrev
235 235 cl._nodecache = {nullid: nullrev}
236 236 cl._nodepos = None
237 237
238 238 @command('perfheads', formatteropts)
239 239 def perfheads(ui, repo, **opts):
240 240 timer, fm = gettimer(ui, opts)
241 241 cl = repo.changelog
242 242 def d():
243 243 len(cl.headrevs())
244 244 clearcaches(cl)
245 245 timer(d)
246 246 fm.end()
247 247
248 248 @command('perftags', formatteropts)
249 249 def perftags(ui, repo, **opts):
250 250 import mercurial.changelog
251 251 import mercurial.manifest
252 252 timer, fm = gettimer(ui, opts)
253 253 def t():
254 254 repo.changelog = mercurial.changelog.changelog(repo.svfs)
255 255 repo.manifest = mercurial.manifest.manifest(repo.svfs)
256 256 repo._tags = None
257 257 return len(repo.tags())
258 258 timer(t)
259 259 fm.end()
260 260
261 261 @command('perfancestors', formatteropts)
262 262 def perfancestors(ui, repo, **opts):
263 263 timer, fm = gettimer(ui, opts)
264 264 heads = repo.changelog.headrevs()
265 265 def d():
266 266 for a in repo.changelog.ancestors(heads):
267 267 pass
268 268 timer(d)
269 269 fm.end()
270 270
271 271 @command('perfancestorset', formatteropts)
272 272 def perfancestorset(ui, repo, revset, **opts):
273 273 timer, fm = gettimer(ui, opts)
274 274 revs = repo.revs(revset)
275 275 heads = repo.changelog.headrevs()
276 276 def d():
277 277 s = repo.changelog.ancestors(heads)
278 278 for rev in revs:
279 279 rev in s
280 280 timer(d)
281 281 fm.end()
282 282
283 283 @command('perfchangegroupchangelog', formatteropts +
284 284 [('', 'version', '02', 'changegroup version'),
285 285 ('r', 'rev', '', 'revisions to add to changegroup')])
286 286 def perfchangegroupchangelog(ui, repo, version='02', rev=None, **opts):
287 287 """Benchmark producing a changelog group for a changegroup.
288 288
289 289 This measures the time spent processing the changelog during a
290 290 bundle operation. This occurs during `hg bundle` and on a server
291 291 processing a `getbundle` wire protocol request (handles clones
292 292 and pull requests).
293 293
294 294 By default, all revisions are added to the changegroup.
295 295 """
296 296 cl = repo.changelog
297 297 revs = [cl.lookup(r) for r in repo.revs(rev or 'all()')]
298 298 bundler = changegroup.getbundler(version, repo)
299 299
300 300 def lookup(node):
301 301 # The real bundler reads the revision in order to access the
302 302 # manifest node and files list. Do that here.
303 303 cl.read(node)
304 304 return node
305 305
306 306 def d():
307 307 for chunk in bundler.group(revs, cl, lookup):
308 308 pass
309 309
310 310 timer, fm = gettimer(ui, opts)
311 311 timer(d)
312 312 fm.end()
313 313
314 314 @command('perfdirs', formatteropts)
315 315 def perfdirs(ui, repo, **opts):
316 316 timer, fm = gettimer(ui, opts)
317 317 dirstate = repo.dirstate
318 318 'a' in dirstate
319 319 def d():
320 320 dirstate.dirs()
321 321 del dirstate._dirs
322 322 timer(d)
323 323 fm.end()
324 324
325 325 @command('perfdirstate', formatteropts)
326 326 def perfdirstate(ui, repo, **opts):
327 327 timer, fm = gettimer(ui, opts)
328 328 "a" in repo.dirstate
329 329 def d():
330 330 repo.dirstate.invalidate()
331 331 "a" in repo.dirstate
332 332 timer(d)
333 333 fm.end()
334 334
335 335 @command('perfdirstatedirs', formatteropts)
336 336 def perfdirstatedirs(ui, repo, **opts):
337 337 timer, fm = gettimer(ui, opts)
338 338 "a" in repo.dirstate
339 339 def d():
340 340 "a" in repo.dirstate._dirs
341 341 del repo.dirstate._dirs
342 342 timer(d)
343 343 fm.end()
344 344
345 345 @command('perfdirstatefoldmap', formatteropts)
346 346 def perfdirstatefoldmap(ui, repo, **opts):
347 347 timer, fm = gettimer(ui, opts)
348 348 dirstate = repo.dirstate
349 349 'a' in dirstate
350 350 def d():
351 351 dirstate._filefoldmap.get('a')
352 352 del dirstate._filefoldmap
353 353 timer(d)
354 354 fm.end()
355 355
356 356 @command('perfdirfoldmap', formatteropts)
357 357 def perfdirfoldmap(ui, repo, **opts):
358 358 timer, fm = gettimer(ui, opts)
359 359 dirstate = repo.dirstate
360 360 'a' in dirstate
361 361 def d():
362 362 dirstate._dirfoldmap.get('a')
363 363 del dirstate._dirfoldmap
364 364 del dirstate._dirs
365 365 timer(d)
366 366 fm.end()
367 367
368 368 @command('perfdirstatewrite', formatteropts)
369 369 def perfdirstatewrite(ui, repo, **opts):
370 370 timer, fm = gettimer(ui, opts)
371 371 ds = repo.dirstate
372 372 "a" in ds
373 373 def d():
374 374 ds._dirty = True
375 375 ds.write(repo.currenttransaction())
376 376 timer(d)
377 377 fm.end()
378 378
379 379 @command('perfmergecalculate',
380 380 [('r', 'rev', '.', 'rev to merge against')] + formatteropts)
381 381 def perfmergecalculate(ui, repo, rev, **opts):
382 382 timer, fm = gettimer(ui, opts)
383 383 wctx = repo[None]
384 384 rctx = scmutil.revsingle(repo, rev, rev)
385 385 ancestor = wctx.ancestor(rctx)
386 386 # we don't want working dir files to be stat'd in the benchmark, so prime
387 387 # that cache
388 388 wctx.dirty()
389 389 def d():
390 390 # acceptremote is True because we don't want prompts in the middle of
391 391 # our benchmark
392 392 merge.calculateupdates(repo, wctx, rctx, [ancestor], False, False,
393 393 acceptremote=True, followcopies=True)
394 394 timer(d)
395 395 fm.end()
396 396
397 397 @command('perfpathcopies', [], "REV REV")
398 398 def perfpathcopies(ui, repo, rev1, rev2, **opts):
399 399 timer, fm = gettimer(ui, opts)
400 400 ctx1 = scmutil.revsingle(repo, rev1, rev1)
401 401 ctx2 = scmutil.revsingle(repo, rev2, rev2)
402 402 def d():
403 403 copies.pathcopies(ctx1, ctx2)
404 404 timer(d)
405 405 fm.end()
406 406
407 407 @command('perfmanifest', [], 'REV')
408 408 def perfmanifest(ui, repo, rev, **opts):
409 409 timer, fm = gettimer(ui, opts)
410 410 ctx = scmutil.revsingle(repo, rev, rev)
411 411 t = ctx.manifestnode()
412 412 def d():
413 413 repo.manifest.clearcaches()
414 414 repo.manifest.read(t)
415 415 timer(d)
416 416 fm.end()
417 417
418 418 @command('perfchangeset', formatteropts)
419 419 def perfchangeset(ui, repo, rev, **opts):
420 420 timer, fm = gettimer(ui, opts)
421 421 n = repo[rev].node()
422 422 def d():
423 423 repo.changelog.read(n)
424 424 #repo.changelog._cache = None
425 425 timer(d)
426 426 fm.end()
427 427
428 428 @command('perfindex', formatteropts)
429 429 def perfindex(ui, repo, **opts):
430 430 import mercurial.revlog
431 431 timer, fm = gettimer(ui, opts)
432 432 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
433 433 n = repo["tip"].node()
434 434 def d():
435 435 cl = mercurial.revlog.revlog(repo.svfs, "00changelog.i")
436 436 cl.rev(n)
437 437 timer(d)
438 438 fm.end()
439 439
440 440 @command('perfstartup', formatteropts)
441 441 def perfstartup(ui, repo, **opts):
442 442 timer, fm = gettimer(ui, opts)
443 443 cmd = sys.argv[0]
444 444 def d():
445 445 if os.name != 'nt':
446 446 os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
447 447 else:
448 448 os.environ['HGRCPATH'] = ''
449 449 os.system("%s version -q > NUL" % cmd)
450 450 timer(d)
451 451 fm.end()
452 452
453 453 @command('perfparents', formatteropts)
454 454 def perfparents(ui, repo, **opts):
455 455 timer, fm = gettimer(ui, opts)
456 456 # control the number of commits perfparents iterates over
457 457 # experimental config: perf.parentscount
458 458 count = ui.configint("perf", "parentscount", 1000)
459 459 if len(repo.changelog) < count:
460 460 raise error.Abort("repo needs %d commits for this test" % count)
461 461 repo = repo.unfiltered()
462 462 nl = [repo.changelog.node(i) for i in xrange(count)]
463 463 def d():
464 464 for n in nl:
465 465 repo.changelog.parents(n)
466 466 timer(d)
467 467 fm.end()
468 468
469 469 @command('perfctxfiles', formatteropts)
470 470 def perfctxfiles(ui, repo, x, **opts):
471 471 x = int(x)
472 472 timer, fm = gettimer(ui, opts)
473 473 def d():
474 474 len(repo[x].files())
475 475 timer(d)
476 476 fm.end()
477 477
478 478 @command('perfrawfiles', formatteropts)
479 479 def perfrawfiles(ui, repo, x, **opts):
480 480 x = int(x)
481 481 timer, fm = gettimer(ui, opts)
482 482 cl = repo.changelog
483 483 def d():
484 484 len(cl.read(x)[3])
485 485 timer(d)
486 486 fm.end()
487 487
488 488 @command('perflookup', formatteropts)
489 489 def perflookup(ui, repo, rev, **opts):
490 490 timer, fm = gettimer(ui, opts)
491 491 timer(lambda: len(repo.lookup(rev)))
492 492 fm.end()
493 493
494 494 @command('perfrevrange', formatteropts)
495 495 def perfrevrange(ui, repo, *specs, **opts):
496 496 timer, fm = gettimer(ui, opts)
497 497 revrange = scmutil.revrange
498 498 timer(lambda: len(revrange(repo, specs)))
499 499 fm.end()
500 500
501 501 @command('perfnodelookup', formatteropts)
502 502 def perfnodelookup(ui, repo, rev, **opts):
503 503 timer, fm = gettimer(ui, opts)
504 504 import mercurial.revlog
505 505 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
506 506 n = repo[rev].node()
507 507 cl = mercurial.revlog.revlog(repo.svfs, "00changelog.i")
508 508 def d():
509 509 cl.rev(n)
510 510 clearcaches(cl)
511 511 timer(d)
512 512 fm.end()
513 513
514 514 @command('perflog',
515 515 [('', 'rename', False, 'ask log to follow renames')] + formatteropts)
516 516 def perflog(ui, repo, rev=None, **opts):
517 517 if rev is None:
518 518 rev=[]
519 519 timer, fm = gettimer(ui, opts)
520 520 ui.pushbuffer()
521 521 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
522 522 copies=opts.get('rename')))
523 523 ui.popbuffer()
524 524 fm.end()
525 525
526 526 @command('perfmoonwalk', formatteropts)
527 527 def perfmoonwalk(ui, repo, **opts):
528 528 """benchmark walking the changelog backwards
529 529
530 530 This also loads the changelog data for each revision in the changelog.
531 531 """
532 532 timer, fm = gettimer(ui, opts)
533 533 def moonwalk():
534 534 for i in xrange(len(repo), -1, -1):
535 535 ctx = repo[i]
536 536 ctx.branch() # read changelog data (in addition to the index)
537 537 timer(moonwalk)
538 538 fm.end()
539 539
540 540 @command('perftemplating', formatteropts)
541 541 def perftemplating(ui, repo, rev=None, **opts):
542 542 if rev is None:
543 543 rev=[]
544 544 timer, fm = gettimer(ui, opts)
545 545 ui.pushbuffer()
546 546 timer(lambda: commands.log(ui, repo, rev=rev, date='', user='',
547 547 template='{date|shortdate} [{rev}:{node|short}]'
548 548 ' {author|person}: {desc|firstline}\n'))
549 549 ui.popbuffer()
550 550 fm.end()
551 551
552 552 @command('perfcca', formatteropts)
553 553 def perfcca(ui, repo, **opts):
554 554 timer, fm = gettimer(ui, opts)
555 555 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
556 556 fm.end()
557 557
558 558 @command('perffncacheload', formatteropts)
559 559 def perffncacheload(ui, repo, **opts):
560 560 timer, fm = gettimer(ui, opts)
561 561 s = repo.store
562 562 def d():
563 563 s.fncache._load()
564 564 timer(d)
565 565 fm.end()
566 566
567 567 @command('perffncachewrite', formatteropts)
568 568 def perffncachewrite(ui, repo, **opts):
569 569 timer, fm = gettimer(ui, opts)
570 570 s = repo.store
571 571 s.fncache._load()
572 572 lock = repo.lock()
573 573 tr = repo.transaction('perffncachewrite')
574 574 def d():
575 575 s.fncache._dirty = True
576 576 s.fncache.write(tr)
577 577 timer(d)
578 tr.close()
578 579 lock.release()
579 tr.close()
580 580 fm.end()
581 581
582 582 @command('perffncacheencode', formatteropts)
583 583 def perffncacheencode(ui, repo, **opts):
584 584 timer, fm = gettimer(ui, opts)
585 585 s = repo.store
586 586 s.fncache._load()
587 587 def d():
588 588 for p in s.fncache.entries:
589 589 s.encode(p)
590 590 timer(d)
591 591 fm.end()
592 592
593 593 @command('perfdiffwd', formatteropts)
594 594 def perfdiffwd(ui, repo, **opts):
595 595 """Profile diff of working directory changes"""
596 596 timer, fm = gettimer(ui, opts)
597 597 options = {
598 598 'w': 'ignore_all_space',
599 599 'b': 'ignore_space_change',
600 600 'B': 'ignore_blank_lines',
601 601 }
602 602
603 603 for diffopt in ('', 'w', 'b', 'B', 'wB'):
604 604 opts = dict((options[c], '1') for c in diffopt)
605 605 def d():
606 606 ui.pushbuffer()
607 607 commands.diff(ui, repo, **opts)
608 608 ui.popbuffer()
609 609 title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
610 610 timer(d, title)
611 611 fm.end()
612 612
613 613 @command('perfrevlog', revlogopts + formatteropts +
614 614 [('d', 'dist', 100, 'distance between the revisions'),
615 615 ('s', 'startrev', 0, 'revision to start reading at'),
616 616 ('', 'reverse', False, 'read in reverse')],
617 617 '-c|-m|FILE')
618 618 def perfrevlog(ui, repo, file_=None, startrev=0, reverse=False, **opts):
619 619 """Benchmark reading a series of revisions from a revlog.
620 620
621 621 By default, we read every ``-d/--dist`` revision from 0 to tip of
622 622 the specified revlog.
623 623
624 624 The start revision can be defined via ``-s/--startrev``.
625 625 """
626 626 timer, fm = gettimer(ui, opts)
627 627 _len = getlen(ui)
628 628
629 629 def d():
630 630 r = cmdutil.openrevlog(repo, 'perfrevlog', file_, opts)
631 631
632 632 startrev = 0
633 633 endrev = _len(r)
634 634 dist = opts['dist']
635 635
636 636 if reverse:
637 637 startrev, endrev = endrev, startrev
638 638 dist = -1 * dist
639 639
640 640 for x in xrange(startrev, endrev, dist):
641 641 r.revision(r.node(x))
642 642
643 643 timer(d)
644 644 fm.end()
645 645
646 646 @command('perfrevlogrevision', revlogopts + formatteropts +
647 647 [('', 'cache', False, 'use caches instead of clearing')],
648 648 '-c|-m|FILE REV')
649 649 def perfrevlogrevision(ui, repo, file_, rev=None, cache=None, **opts):
650 650 """Benchmark obtaining a revlog revision.
651 651
652 652 Obtaining a revlog revision consists of roughly the following steps:
653 653
654 654 1. Compute the delta chain
655 655 2. Obtain the raw chunks for that delta chain
656 656 3. Decompress each raw chunk
657 657 4. Apply binary patches to obtain fulltext
658 658 5. Verify hash of fulltext
659 659
660 660 This command measures the time spent in each of these phases.
661 661 """
662 662 if opts.get('changelog') or opts.get('manifest'):
663 663 file_, rev = None, file_
664 664 elif rev is None:
665 665 raise error.CommandError('perfrevlogrevision', 'invalid arguments')
666 666
667 667 r = cmdutil.openrevlog(repo, 'perfrevlogrevision', file_, opts)
668 668 node = r.lookup(rev)
669 669 rev = r.rev(node)
670 670
671 671 def dodeltachain(rev):
672 672 if not cache:
673 673 r.clearcaches()
674 674 r._deltachain(rev)
675 675
676 676 def doread(chain):
677 677 if not cache:
678 678 r.clearcaches()
679 679 r._chunkraw(chain[0], chain[-1])
680 680
681 681 def dodecompress(data, chain):
682 682 if not cache:
683 683 r.clearcaches()
684 684
685 685 start = r.start
686 686 length = r.length
687 687 inline = r._inline
688 688 iosize = r._io.size
689 689 buffer = util.buffer
690 690 offset = start(chain[0])
691 691
692 692 for rev in chain:
693 693 chunkstart = start(rev)
694 694 if inline:
695 695 chunkstart += (rev + 1) * iosize
696 696 chunklength = length(rev)
697 697 b = buffer(data, chunkstart - offset, chunklength)
698 698 revlog.decompress(b)
699 699
700 700 def dopatch(text, bins):
701 701 if not cache:
702 702 r.clearcaches()
703 703 mdiff.patches(text, bins)
704 704
705 705 def dohash(text):
706 706 if not cache:
707 707 r.clearcaches()
708 708 r._checkhash(text, node, rev)
709 709
710 710 def dorevision():
711 711 if not cache:
712 712 r.clearcaches()
713 713 r.revision(node)
714 714
715 715 chain = r._deltachain(rev)[0]
716 716 data = r._chunkraw(chain[0], chain[-1])[1]
717 717 bins = r._chunks(chain)
718 718 text = str(bins[0])
719 719 bins = bins[1:]
720 720 text = mdiff.patches(text, bins)
721 721
722 722 benches = [
723 723 (lambda: dorevision(), 'full'),
724 724 (lambda: dodeltachain(rev), 'deltachain'),
725 725 (lambda: doread(chain), 'read'),
726 726 (lambda: dodecompress(data, chain), 'decompress'),
727 727 (lambda: dopatch(text, bins), 'patch'),
728 728 (lambda: dohash(text), 'hash'),
729 729 ]
730 730
731 731 for fn, title in benches:
732 732 timer, fm = gettimer(ui, opts)
733 733 timer(fn, title=title)
734 734 fm.end()
735 735
736 736 @command('perfrevset',
737 737 [('C', 'clear', False, 'clear volatile cache between each call.'),
738 738 ('', 'contexts', False, 'obtain changectx for each revision')]
739 739 + formatteropts, "REVSET")
740 740 def perfrevset(ui, repo, expr, clear=False, contexts=False, **opts):
741 741 """benchmark the execution time of a revset
742 742
743 743 Use the --clean option if need to evaluate the impact of build volatile
744 744 revisions set cache on the revset execution. Volatile cache hold filtered
745 745 and obsolete related cache."""
746 746 timer, fm = gettimer(ui, opts)
747 747 def d():
748 748 if clear:
749 749 repo.invalidatevolatilesets()
750 750 if contexts:
751 751 for ctx in repo.set(expr): pass
752 752 else:
753 753 for r in repo.revs(expr): pass
754 754 timer(d)
755 755 fm.end()
756 756
757 757 @command('perfvolatilesets', formatteropts)
758 758 def perfvolatilesets(ui, repo, *names, **opts):
759 759 """benchmark the computation of various volatile set
760 760
761 761 Volatile set computes element related to filtering and obsolescence."""
762 762 timer, fm = gettimer(ui, opts)
763 763 repo = repo.unfiltered()
764 764
765 765 def getobs(name):
766 766 def d():
767 767 repo.invalidatevolatilesets()
768 768 obsolete.getrevs(repo, name)
769 769 return d
770 770
771 771 allobs = sorted(obsolete.cachefuncs)
772 772 if names:
773 773 allobs = [n for n in allobs if n in names]
774 774
775 775 for name in allobs:
776 776 timer(getobs(name), title=name)
777 777
778 778 def getfiltered(name):
779 779 def d():
780 780 repo.invalidatevolatilesets()
781 781 repoview.filterrevs(repo, name)
782 782 return d
783 783
784 784 allfilter = sorted(repoview.filtertable)
785 785 if names:
786 786 allfilter = [n for n in allfilter if n in names]
787 787
788 788 for name in allfilter:
789 789 timer(getfiltered(name), title=name)
790 790 fm.end()
791 791
792 792 @command('perfbranchmap',
793 793 [('f', 'full', False,
794 794 'Includes build time of subset'),
795 795 ] + formatteropts)
796 796 def perfbranchmap(ui, repo, full=False, **opts):
797 797 """benchmark the update of a branchmap
798 798
799 799 This benchmarks the full repo.branchmap() call with read and write disabled
800 800 """
801 801 timer, fm = gettimer(ui, opts)
802 802 def getbranchmap(filtername):
803 803 """generate a benchmark function for the filtername"""
804 804 if filtername is None:
805 805 view = repo
806 806 else:
807 807 view = repo.filtered(filtername)
808 808 def d():
809 809 if full:
810 810 view._branchcaches.clear()
811 811 else:
812 812 view._branchcaches.pop(filtername, None)
813 813 view.branchmap()
814 814 return d
815 815 # add filter in smaller subset to bigger subset
816 816 possiblefilters = set(repoview.filtertable)
817 817 allfilters = []
818 818 while possiblefilters:
819 819 for name in possiblefilters:
820 820 subset = branchmap.subsettable.get(name)
821 821 if subset not in possiblefilters:
822 822 break
823 823 else:
824 824 assert False, 'subset cycle %s!' % possiblefilters
825 825 allfilters.append(name)
826 826 possiblefilters.remove(name)
827 827
828 828 # warm the cache
829 829 if not full:
830 830 for name in allfilters:
831 831 repo.filtered(name).branchmap()
832 832 # add unfiltered
833 833 allfilters.append(None)
834 834 oldread = branchmap.read
835 835 oldwrite = branchmap.branchcache.write
836 836 try:
837 837 branchmap.read = lambda repo: None
838 838 branchmap.write = lambda repo: None
839 839 for name in allfilters:
840 840 timer(getbranchmap(name), title=str(name))
841 841 finally:
842 842 branchmap.read = oldread
843 843 branchmap.branchcache.write = oldwrite
844 844 fm.end()
845 845
846 846 @command('perfloadmarkers')
847 847 def perfloadmarkers(ui, repo):
848 848 """benchmark the time to parse the on-disk markers for a repo
849 849
850 850 Result is the number of markers in the repo."""
851 851 timer, fm = gettimer(ui)
852 852 timer(lambda: len(obsolete.obsstore(repo.svfs)))
853 853 fm.end()
854 854
855 855 @command('perflrucachedict', formatteropts +
856 856 [('', 'size', 4, 'size of cache'),
857 857 ('', 'gets', 10000, 'number of key lookups'),
858 858 ('', 'sets', 10000, 'number of key sets'),
859 859 ('', 'mixed', 10000, 'number of mixed mode operations'),
860 860 ('', 'mixedgetfreq', 50, 'frequency of get vs set ops in mixed mode')],
861 861 norepo=True)
862 862 def perflrucache(ui, size=4, gets=10000, sets=10000, mixed=10000,
863 863 mixedgetfreq=50, **opts):
864 864 def doinit():
865 865 for i in xrange(10000):
866 866 util.lrucachedict(size)
867 867
868 868 values = []
869 869 for i in xrange(size):
870 870 values.append(random.randint(0, sys.maxint))
871 871
872 872 # Get mode fills the cache and tests raw lookup performance with no
873 873 # eviction.
874 874 getseq = []
875 875 for i in xrange(gets):
876 876 getseq.append(random.choice(values))
877 877
878 878 def dogets():
879 879 d = util.lrucachedict(size)
880 880 for v in values:
881 881 d[v] = v
882 882 for key in getseq:
883 883 value = d[key]
884 884 value # silence pyflakes warning
885 885
886 886 # Set mode tests insertion speed with cache eviction.
887 887 setseq = []
888 888 for i in xrange(sets):
889 889 setseq.append(random.randint(0, sys.maxint))
890 890
891 891 def dosets():
892 892 d = util.lrucachedict(size)
893 893 for v in setseq:
894 894 d[v] = v
895 895
896 896 # Mixed mode randomly performs gets and sets with eviction.
897 897 mixedops = []
898 898 for i in xrange(mixed):
899 899 r = random.randint(0, 100)
900 900 if r < mixedgetfreq:
901 901 op = 0
902 902 else:
903 903 op = 1
904 904
905 905 mixedops.append((op, random.randint(0, size * 2)))
906 906
907 907 def domixed():
908 908 d = util.lrucachedict(size)
909 909
910 910 for op, v in mixedops:
911 911 if op == 0:
912 912 try:
913 913 d[v]
914 914 except KeyError:
915 915 pass
916 916 else:
917 917 d[v] = v
918 918
919 919 benches = [
920 920 (doinit, 'init'),
921 921 (dogets, 'gets'),
922 922 (dosets, 'sets'),
923 923 (domixed, 'mixed')
924 924 ]
925 925
926 926 for fn, title in benches:
927 927 timer, fm = gettimer(ui, opts)
928 928 timer(fn, title=title)
929 929 fm.end()
930 930
931 931 def uisetup(ui):
932 932 if (util.safehasattr(cmdutil, 'openrevlog') and
933 933 not util.safehasattr(commands, 'debugrevlogopts')):
934 934 # for "historical portability":
935 935 # In this case, Mercurial should be 1.9 (or a79fea6b3e77) -
936 936 # 3.7 (or 5606f7d0d063). Therefore, '--dir' option for
937 937 # openrevlog() should cause failure, because it has been
938 938 # available since 3.5 (or 49c583ca48c4).
939 939 def openrevlog(orig, repo, cmd, file_, opts):
940 940 if opts.get('dir') and not util.safehasattr(repo, 'dirlog'):
941 941 raise error.Abort("This version doesn't support --dir option",
942 942 hint="use 3.5 or later")
943 943 return orig(repo, cmd, file_, opts)
944 944 extensions.wrapfunction(cmdutil, 'openrevlog', openrevlog)
General Comments 0
You need to be logged in to leave comments. Login now