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