##// END OF EJS Templates
perf: add a perfloadmarkers command...
Pierre-Yves David -
r23485:ccb93e9a default
parent child Browse files
Show More
@@ -1,527 +1,536
1 1 # perf.py - performance test routines
2 2 '''helper extension to measure performance'''
3 3
4 4 from mercurial import cmdutil, scmutil, util, commands, obsolete
5 5 from mercurial import repoview, branchmap, merge, copies
6 6 import time, os, sys
7 7 import functools
8 8
9 9 cmdtable = {}
10 10 command = cmdutil.command(cmdtable)
11 11
12 12 def gettimer(ui, opts=None):
13 13 """return a timer function and formatter: (timer, formatter)
14 14
15 15 This functions exist to gather the creation of formatter in a single
16 16 place instead of duplicating it in all performance command."""
17 17 if opts is None:
18 18 opts = {}
19 19 # redirect all to stderr
20 20 ui = ui.copy()
21 21 ui.fout = ui.ferr
22 22 # get a formatter
23 23 fm = ui.formatter('perf', opts)
24 24 return functools.partial(_timer, fm), fm
25 25
26 26 def _timer(fm, func, title=None):
27 27 results = []
28 28 begin = time.time()
29 29 count = 0
30 30 while True:
31 31 ostart = os.times()
32 32 cstart = time.time()
33 33 r = func()
34 34 cstop = time.time()
35 35 ostop = os.times()
36 36 count += 1
37 37 a, b = ostart, ostop
38 38 results.append((cstop - cstart, b[0] - a[0], b[1]-a[1]))
39 39 if cstop - begin > 3 and count >= 100:
40 40 break
41 41 if cstop - begin > 10 and count >= 3:
42 42 break
43 43
44 44 fm.startitem()
45 45
46 46 if title:
47 47 fm.write('title', '! %s\n', title)
48 48 if r:
49 49 fm.write('result', '! result: %s\n', r)
50 50 m = min(results)
51 51 fm.plain('!')
52 52 fm.write('wall', ' wall %f', m[0])
53 53 fm.write('comb', ' comb %f', m[1] + m[2])
54 54 fm.write('user', ' user %f', m[1])
55 55 fm.write('sys', ' sys %f', m[2])
56 56 fm.write('count', ' (best of %d)', count)
57 57 fm.plain('\n')
58 58
59 59 @command('perfwalk')
60 60 def perfwalk(ui, repo, *pats):
61 61 timer, fm = gettimer(ui)
62 62 try:
63 63 m = scmutil.match(repo[None], pats, {})
64 64 timer(lambda: len(list(repo.dirstate.walk(m, [], True, False))))
65 65 except Exception:
66 66 try:
67 67 m = scmutil.match(repo[None], pats, {})
68 68 timer(lambda: len([b for a, b, c in repo.dirstate.statwalk([], m)]))
69 69 except Exception:
70 70 timer(lambda: len(list(cmdutil.walk(repo, pats, {}))))
71 71 fm.end()
72 72
73 73 @command('perfannotate')
74 74 def perfannotate(ui, repo, f):
75 75 timer, fm = gettimer(ui)
76 76 fc = repo['.'][f]
77 77 timer(lambda: len(fc.annotate(True)))
78 78 fm.end()
79 79
80 80 @command('perfstatus',
81 81 [('u', 'unknown', False,
82 82 'ask status to look for unknown files')])
83 83 def perfstatus(ui, repo, **opts):
84 84 #m = match.always(repo.root, repo.getcwd())
85 85 #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False,
86 86 # False))))
87 87 timer, fm = gettimer(ui)
88 88 timer(lambda: sum(map(len, repo.status(**opts))))
89 89 fm.end()
90 90
91 91 @command('perfaddremove')
92 92 def perfaddremove(ui, repo):
93 93 timer, fm = gettimer(ui)
94 94 try:
95 95 oldquiet = repo.ui.quiet
96 96 repo.ui.quiet = True
97 97 timer(lambda: scmutil.addremove(repo, dry_run=True))
98 98 finally:
99 99 repo.ui.quiet = oldquiet
100 100 fm.end()
101 101
102 102 def clearcaches(cl):
103 103 # behave somewhat consistently across internal API changes
104 104 if util.safehasattr(cl, 'clearcaches'):
105 105 cl.clearcaches()
106 106 elif util.safehasattr(cl, '_nodecache'):
107 107 from mercurial.node import nullid, nullrev
108 108 cl._nodecache = {nullid: nullrev}
109 109 cl._nodepos = None
110 110
111 111 @command('perfheads')
112 112 def perfheads(ui, repo):
113 113 timer, fm = gettimer(ui)
114 114 cl = repo.changelog
115 115 def d():
116 116 len(cl.headrevs())
117 117 clearcaches(cl)
118 118 timer(d)
119 119 fm.end()
120 120
121 121 @command('perftags')
122 122 def perftags(ui, repo):
123 123 import mercurial.changelog
124 124 import mercurial.manifest
125 125 timer, fm = gettimer(ui)
126 126 def t():
127 127 repo.changelog = mercurial.changelog.changelog(repo.sopener)
128 128 repo.manifest = mercurial.manifest.manifest(repo.sopener)
129 129 repo._tags = None
130 130 return len(repo.tags())
131 131 timer(t)
132 132 fm.end()
133 133
134 134 @command('perfancestors')
135 135 def perfancestors(ui, repo):
136 136 timer, fm = gettimer(ui)
137 137 heads = repo.changelog.headrevs()
138 138 def d():
139 139 for a in repo.changelog.ancestors(heads):
140 140 pass
141 141 timer(d)
142 142 fm.end()
143 143
144 144 @command('perfancestorset')
145 145 def perfancestorset(ui, repo, revset):
146 146 timer, fm = gettimer(ui)
147 147 revs = repo.revs(revset)
148 148 heads = repo.changelog.headrevs()
149 149 def d():
150 150 s = repo.changelog.ancestors(heads)
151 151 for rev in revs:
152 152 rev in s
153 153 timer(d)
154 154 fm.end()
155 155
156 156 @command('perfdirs')
157 157 def perfdirs(ui, repo):
158 158 timer, fm = gettimer(ui)
159 159 dirstate = repo.dirstate
160 160 'a' in dirstate
161 161 def d():
162 162 dirstate.dirs()
163 163 del dirstate._dirs
164 164 timer(d)
165 165 fm.end()
166 166
167 167 @command('perfdirstate')
168 168 def perfdirstate(ui, repo):
169 169 timer, fm = gettimer(ui)
170 170 "a" in repo.dirstate
171 171 def d():
172 172 repo.dirstate.invalidate()
173 173 "a" in repo.dirstate
174 174 timer(d)
175 175 fm.end()
176 176
177 177 @command('perfdirstatedirs')
178 178 def perfdirstatedirs(ui, repo):
179 179 timer, fm = gettimer(ui)
180 180 "a" in repo.dirstate
181 181 def d():
182 182 "a" in repo.dirstate._dirs
183 183 del repo.dirstate._dirs
184 184 timer(d)
185 185 fm.end()
186 186
187 187 @command('perfdirstatefoldmap')
188 188 def perffoldmap(ui, repo):
189 189 timer, fm = gettimer(ui)
190 190 dirstate = repo.dirstate
191 191 'a' in dirstate
192 192 def d():
193 193 dirstate._foldmap.get('a')
194 194 del dirstate._foldmap
195 195 del dirstate._dirs
196 196 timer(d)
197 197 fm.end()
198 198
199 199 @command('perfdirstatewrite')
200 200 def perfdirstatewrite(ui, repo):
201 201 timer, fm = gettimer(ui)
202 202 ds = repo.dirstate
203 203 "a" in ds
204 204 def d():
205 205 ds._dirty = True
206 206 ds.write()
207 207 timer(d)
208 208 fm.end()
209 209
210 210 @command('perfmergecalculate',
211 211 [('r', 'rev', '.', 'rev to merge against')])
212 212 def perfmergecalculate(ui, repo, rev):
213 213 timer, fm = gettimer(ui)
214 214 wctx = repo[None]
215 215 rctx = scmutil.revsingle(repo, rev, rev)
216 216 ancestor = wctx.ancestor(rctx)
217 217 # we don't want working dir files to be stat'd in the benchmark, so prime
218 218 # that cache
219 219 wctx.dirty()
220 220 def d():
221 221 # acceptremote is True because we don't want prompts in the middle of
222 222 # our benchmark
223 223 merge.calculateupdates(repo, wctx, rctx, ancestor, False, False, False,
224 224 acceptremote=True)
225 225 timer(d)
226 226 fm.end()
227 227
228 228 @command('perfpathcopies', [], "REV REV")
229 229 def perfpathcopies(ui, repo, rev1, rev2):
230 230 timer, fm = gettimer(ui)
231 231 ctx1 = scmutil.revsingle(repo, rev1, rev1)
232 232 ctx2 = scmutil.revsingle(repo, rev2, rev2)
233 233 def d():
234 234 copies.pathcopies(ctx1, ctx2)
235 235 timer(d)
236 236 fm.end()
237 237
238 238 @command('perfmanifest', [], 'REV')
239 239 def perfmanifest(ui, repo, rev):
240 240 timer, fm = gettimer(ui)
241 241 ctx = scmutil.revsingle(repo, rev, rev)
242 242 t = ctx.manifestnode()
243 243 def d():
244 244 repo.manifest._mancache.clear()
245 245 repo.manifest._cache = None
246 246 repo.manifest.read(t)
247 247 timer(d)
248 248 fm.end()
249 249
250 250 @command('perfchangeset')
251 251 def perfchangeset(ui, repo, rev):
252 252 timer, fm = gettimer(ui)
253 253 n = repo[rev].node()
254 254 def d():
255 255 repo.changelog.read(n)
256 256 #repo.changelog._cache = None
257 257 timer(d)
258 258 fm.end()
259 259
260 260 @command('perfindex')
261 261 def perfindex(ui, repo):
262 262 import mercurial.revlog
263 263 timer, fm = gettimer(ui)
264 264 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
265 265 n = repo["tip"].node()
266 266 def d():
267 267 cl = mercurial.revlog.revlog(repo.sopener, "00changelog.i")
268 268 cl.rev(n)
269 269 timer(d)
270 270 fm.end()
271 271
272 272 @command('perfstartup')
273 273 def perfstartup(ui, repo):
274 274 timer, fm = gettimer(ui)
275 275 cmd = sys.argv[0]
276 276 def d():
277 277 os.system("HGRCPATH= %s version -q > /dev/null" % cmd)
278 278 timer(d)
279 279 fm.end()
280 280
281 281 @command('perfparents')
282 282 def perfparents(ui, repo):
283 283 timer, fm = gettimer(ui)
284 284 nl = [repo.changelog.node(i) for i in xrange(1000)]
285 285 def d():
286 286 for n in nl:
287 287 repo.changelog.parents(n)
288 288 timer(d)
289 289 fm.end()
290 290
291 291 @command('perflookup')
292 292 def perflookup(ui, repo, rev):
293 293 timer, fm = gettimer(ui)
294 294 timer(lambda: len(repo.lookup(rev)))
295 295 fm.end()
296 296
297 297 @command('perfrevrange')
298 298 def perfrevrange(ui, repo, *specs):
299 299 timer, fm = gettimer(ui)
300 300 revrange = scmutil.revrange
301 301 timer(lambda: len(revrange(repo, specs)))
302 302 fm.end()
303 303
304 304 @command('perfnodelookup')
305 305 def perfnodelookup(ui, repo, rev):
306 306 timer, fm = gettimer(ui)
307 307 import mercurial.revlog
308 308 mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg
309 309 n = repo[rev].node()
310 310 cl = mercurial.revlog.revlog(repo.sopener, "00changelog.i")
311 311 def d():
312 312 cl.rev(n)
313 313 clearcaches(cl)
314 314 timer(d)
315 315 fm.end()
316 316
317 317 @command('perflog',
318 318 [('', 'rename', False, 'ask log to follow renames')])
319 319 def perflog(ui, repo, **opts):
320 320 timer, fm = gettimer(ui)
321 321 ui.pushbuffer()
322 322 timer(lambda: commands.log(ui, repo, rev=[], date='', user='',
323 323 copies=opts.get('rename')))
324 324 ui.popbuffer()
325 325 fm.end()
326 326
327 327 @command('perfmoonwalk')
328 328 def perfmoonwalk(ui, repo):
329 329 """benchmark walking the changelog backwards
330 330
331 331 This also loads the changelog data for each revision in the changelog.
332 332 """
333 333 timer, fm = gettimer(ui)
334 334 def moonwalk():
335 335 for i in xrange(len(repo), -1, -1):
336 336 ctx = repo[i]
337 337 ctx.branch() # read changelog data (in addition to the index)
338 338 timer(moonwalk)
339 339 fm.end()
340 340
341 341 @command('perftemplating')
342 342 def perftemplating(ui, repo):
343 343 timer, fm = gettimer(ui)
344 344 ui.pushbuffer()
345 345 timer(lambda: commands.log(ui, repo, rev=[], date='', user='',
346 346 template='{date|shortdate} [{rev}:{node|short}]'
347 347 ' {author|person}: {desc|firstline}\n'))
348 348 ui.popbuffer()
349 349 fm.end()
350 350
351 351 @command('perfcca')
352 352 def perfcca(ui, repo):
353 353 timer, fm = gettimer(ui)
354 354 timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate))
355 355 fm.end()
356 356
357 357 @command('perffncacheload')
358 358 def perffncacheload(ui, repo):
359 359 timer, fm = gettimer(ui)
360 360 s = repo.store
361 361 def d():
362 362 s.fncache._load()
363 363 timer(d)
364 364 fm.end()
365 365
366 366 @command('perffncachewrite')
367 367 def perffncachewrite(ui, repo):
368 368 timer, fm = gettimer(ui)
369 369 s = repo.store
370 370 s.fncache._load()
371 371 def d():
372 372 s.fncache._dirty = True
373 373 s.fncache.write()
374 374 timer(d)
375 375 fm.end()
376 376
377 377 @command('perffncacheencode')
378 378 def perffncacheencode(ui, repo):
379 379 timer, fm = gettimer(ui)
380 380 s = repo.store
381 381 s.fncache._load()
382 382 def d():
383 383 for p in s.fncache.entries:
384 384 s.encode(p)
385 385 timer(d)
386 386 fm.end()
387 387
388 388 @command('perfdiffwd')
389 389 def perfdiffwd(ui, repo):
390 390 """Profile diff of working directory changes"""
391 391 timer, fm = gettimer(ui)
392 392 options = {
393 393 'w': 'ignore_all_space',
394 394 'b': 'ignore_space_change',
395 395 'B': 'ignore_blank_lines',
396 396 }
397 397
398 398 for diffopt in ('', 'w', 'b', 'B', 'wB'):
399 399 opts = dict((options[c], '1') for c in diffopt)
400 400 def d():
401 401 ui.pushbuffer()
402 402 commands.diff(ui, repo, **opts)
403 403 ui.popbuffer()
404 404 title = 'diffopts: %s' % (diffopt and ('-' + diffopt) or 'none')
405 405 timer(d, title)
406 406 fm.end()
407 407
408 408 @command('perfrevlog',
409 409 [('d', 'dist', 100, 'distance between the revisions')],
410 410 "[INDEXFILE]")
411 411 def perfrevlog(ui, repo, file_, **opts):
412 412 timer, fm = gettimer(ui)
413 413 from mercurial import revlog
414 414 dist = opts['dist']
415 415 def d():
416 416 r = revlog.revlog(lambda fn: open(fn, 'rb'), file_)
417 417 for x in xrange(0, len(r), dist):
418 418 r.revision(r.node(x))
419 419
420 420 timer(d)
421 421 fm.end()
422 422
423 423 @command('perfrevset',
424 424 [('C', 'clear', False, 'clear volatile cache between each call.')],
425 425 "REVSET")
426 426 def perfrevset(ui, repo, expr, clear=False):
427 427 """benchmark the execution time of a revset
428 428
429 429 Use the --clean option if need to evaluate the impact of build volatile
430 430 revisions set cache on the revset execution. Volatile cache hold filtered
431 431 and obsolete related cache."""
432 432 timer, fm = gettimer(ui)
433 433 def d():
434 434 if clear:
435 435 repo.invalidatevolatilesets()
436 436 for r in repo.revs(expr): pass
437 437 timer(d)
438 438 fm.end()
439 439
440 440 @command('perfvolatilesets')
441 441 def perfvolatilesets(ui, repo, *names):
442 442 """benchmark the computation of various volatile set
443 443
444 444 Volatile set computes element related to filtering and obsolescence."""
445 445 timer, fm = gettimer(ui)
446 446 repo = repo.unfiltered()
447 447
448 448 def getobs(name):
449 449 def d():
450 450 repo.invalidatevolatilesets()
451 451 obsolete.getrevs(repo, name)
452 452 return d
453 453
454 454 allobs = sorted(obsolete.cachefuncs)
455 455 if names:
456 456 allobs = [n for n in allobs if n in names]
457 457
458 458 for name in allobs:
459 459 timer(getobs(name), title=name)
460 460
461 461 def getfiltered(name):
462 462 def d():
463 463 repo.invalidatevolatilesets()
464 464 repoview.filterrevs(repo, name)
465 465 return d
466 466
467 467 allfilter = sorted(repoview.filtertable)
468 468 if names:
469 469 allfilter = [n for n in allfilter if n in names]
470 470
471 471 for name in allfilter:
472 472 timer(getfiltered(name), title=name)
473 473 fm.end()
474 474
475 475 @command('perfbranchmap',
476 476 [('f', 'full', False,
477 477 'Includes build time of subset'),
478 478 ])
479 479 def perfbranchmap(ui, repo, full=False):
480 480 """benchmark the update of a branchmap
481 481
482 482 This benchmarks the full repo.branchmap() call with read and write disabled
483 483 """
484 484 timer, fm = gettimer(ui)
485 485 def getbranchmap(filtername):
486 486 """generate a benchmark function for the filtername"""
487 487 if filtername is None:
488 488 view = repo
489 489 else:
490 490 view = repo.filtered(filtername)
491 491 def d():
492 492 if full:
493 493 view._branchcaches.clear()
494 494 else:
495 495 view._branchcaches.pop(filtername, None)
496 496 view.branchmap()
497 497 return d
498 498 # add filter in smaller subset to bigger subset
499 499 possiblefilters = set(repoview.filtertable)
500 500 allfilters = []
501 501 while possiblefilters:
502 502 for name in possiblefilters:
503 503 subset = branchmap.subsettable.get(name)
504 504 if subset not in possiblefilters:
505 505 break
506 506 else:
507 507 assert False, 'subset cycle %s!' % possiblefilters
508 508 allfilters.append(name)
509 509 possiblefilters.remove(name)
510 510
511 511 # warm the cache
512 512 if not full:
513 513 for name in allfilters:
514 514 repo.filtered(name).branchmap()
515 515 # add unfiltered
516 516 allfilters.append(None)
517 517 oldread = branchmap.read
518 518 oldwrite = branchmap.branchcache.write
519 519 try:
520 520 branchmap.read = lambda repo: None
521 521 branchmap.write = lambda repo: None
522 522 for name in allfilters:
523 523 timer(getbranchmap(name), title=str(name))
524 524 finally:
525 525 branchmap.read = oldread
526 526 branchmap.branchcache.write = oldwrite
527 527 fm.end()
528
529 @command('perfloadmarkers')
530 def perfloadmarkers(ui, repo):
531 """benchmark the time to parse the on-disk markers for a repo
532
533 Result is the number of markers in the repo."""
534 timer, fm = gettimer(ui)
535 timer(lambda: len(obsolete.obsstore(repo.sopener)))
536 fm.end()
General Comments 0
You need to be logged in to leave comments. Login now