Mercurial > hg
view mercurial/lsprofcalltree.py @ 26909:e36118815a39
phase: improve retractboundary perf
The existing retractboundary implementation computed the new boundary by walking
all descendants of all existing roots and computing the new roots. This is
O(commits since first root), which on long repos can be hundreds of thousands of
commits.
The new algorithm only updates roots that are greater than the new root
locations. For common operations like commit on a repo with the earliest root
several hundred thousand commits ago, this makes retractboundary go from
1 second to 0.008 seconds.
I tested it by running the test suite with both implementations and checking
that the root results were always the identical.
There was some discussion on IRC about the safety of this (i.e. what if the new
nodes are already part of the phase, etc). I've looked into it and believe this
patch is safe:
1) The old existing code already filters the input nodes to only contain nodes
that require retracting (i.e. we only make node X a new root if the old phase
is less than the target phase), so there's no chance of us adding a
unnecessary root to the phase (unless the input root is made unnecessary by
another root in the same input, but see point #3).
2) Another way of thinking about this is: the only way the new algorithm would
be different from the old algorithm is if it added a root that is a
descendant of an old root (since the old algorithm would've caught this in
the big "roots(%ln::)". At the beginning of the function, when we filter out
roots that already meet the phase criteria, the *definition* of meeting the
phase criteria is "not being a descendant of an existing root". Therefore,
by definition none of the new roots we are processing are descendants of an
existing root.
3) If two nodes are passed in as input, and one node is an ancestor of the other
(and therefore the later node should not be a root), this is still caught by
the 'roots(%ln::)' revset. So there's no chance of an extra root being
introduced that way either.
author | Durham Goode <durham@fb.com> |
---|---|
date | Sat, 07 Nov 2015 16:11:49 -0800 |
parents | beae42f3d93b |
children | 071af8d385a9 |
line wrap: on
line source
""" lsprofcalltree.py - lsprof output which is readable by kcachegrind Authors: * David Allouche <david <at> allouche.net> * Jp Calderone & Itamar Shtull-Trauring * Johan Dahlin This software may be used and distributed according to the terms of the GNU General Public License, incorporated herein by reference. """ def label(code): if isinstance(code, str): return '~' + code # built-in functions ('~' sorts at the end) else: return '%s %s:%d' % (code.co_name, code.co_filename, code.co_firstlineno) class KCacheGrind(object): def __init__(self, profiler): self.data = profiler.getstats() self.out_file = None def output(self, out_file): self.out_file = out_file print >> out_file, 'events: Ticks' self._print_summary() for entry in self.data: self._entry(entry) def _print_summary(self): max_cost = 0 for entry in self.data: totaltime = int(entry.totaltime * 1000) max_cost = max(max_cost, totaltime) print >> self.out_file, 'summary: %d' % (max_cost,) def _entry(self, entry): out_file = self.out_file code = entry.code #print >> out_file, 'ob=%s' % (code.co_filename,) if isinstance(code, str): print >> out_file, 'fi=~' else: print >> out_file, 'fi=%s' % (code.co_filename,) print >> out_file, 'fn=%s' % (label(code),) inlinetime = int(entry.inlinetime * 1000) if isinstance(code, str): print >> out_file, '0 ', inlinetime else: print >> out_file, '%d %d' % (code.co_firstlineno, inlinetime) # recursive calls are counted in entry.calls if entry.calls: calls = entry.calls else: calls = [] if isinstance(code, str): lineno = 0 else: lineno = code.co_firstlineno for subentry in calls: self._subentry(lineno, subentry) print >> out_file def _subentry(self, lineno, subentry): out_file = self.out_file code = subentry.code #print >> out_file, 'cob=%s' % (code.co_filename,) print >> out_file, 'cfn=%s' % (label(code),) if isinstance(code, str): print >> out_file, 'cfi=~' print >> out_file, 'calls=%d 0' % (subentry.callcount,) else: print >> out_file, 'cfi=%s' % (code.co_filename,) print >> out_file, 'calls=%d %d' % ( subentry.callcount, code.co_firstlineno) totaltime = int(subentry.totaltime * 1000) print >> out_file, '%d %d' % (lineno, totaltime)