[PATCH 3 of 3] branches: improve performance by removing redundant operations

Brodie Rao brodie at sf.io
Sun May 13 07:20:40 CDT 2012


# HG changeset patch
# User Brodie Rao <brodie at sf.io>
# Date 1336910647 -7200
# Node ID 02270aa9656b4365ee315d0117c0f49effffa172
# Parent  43d75fdda09a7edd987cb084e2464eb268a3cc46
branches: improve performance by removing redundant operations

This refactors the branches command so it collects all the information it needs
about a branch in one pass over the branch map.

In particular, it fixes an issue where the command called repo.branchtags() to
get branch tips, and then used repo.branchheads() to get the closed/open
status. Both repo methods read the changelog to determine if the branch is
closed, resulting in extra, redundant I/O.

For the PyPy repo with 744 branches and 843 branch heads, this brings
hg branches over NFS from:

   CallCount    Recursive    Total(ms)   Inline(ms) module:lineno(function)
        2427            0      0.9057      0.9057   <open>
        2424            0      0.4096      0.4096   <method 'close' of 'file' objects>
        2424            0      0.0476      0.0476   <method 'read' of 'file' objects>
           1            0      0.0468      0.0468   mercurial.revlog:637(headrevs)
          +1            0      0.0000      0.0000   +<len>
        2422            0      0.0443      0.0443   <method 'seek' of 'file' objects>
        2962            0      0.0337      0.0337   <zlib.decompress>
        2491            0      1.8008      0.0322   mercurial.changelog:182(read)
       +2491            0      1.6982      0.0315   +mercurial.revlog:881(revision)
       +2488            0      0.0269      0.0134   +mercurial.changelog:28(decodeextra)
       +4982            0      0.0085      0.0085   +<method 'split' of 'str' objects>
       +4982            0      0.0274      0.0049   +mercurial.encoding:61(tolocal)
       +2491            0      0.0039      0.0039   +<method 'index' of 'str' objects>
        2491            0      1.6982      0.0315   mercurial.revlog:881(revision)
       +2413            0      0.0112      0.0112   +mercurial.revlog:305(rev)
       +2491            0      1.5315      0.0068   +mercurial.revlog:847(_chunkraw)
       +2491            0      0.0414      0.0058   +mercurial.revlog:945(_checkhash)
       +2491            0      0.0028      0.0028   +mercurial.revlog:349(flags)
       +2491            0      0.0025      0.0025   +<mercurial.mpatch.patches>
        2422            0      1.5089      0.0286   mercurial.revlog:818(_loadchunk)
       +2422            0      0.4093      0.4093   +<method 'close' of 'file' objects>
       +2422            0      0.0451      0.0451   +<method 'read' of 'file' objects>
       +2422            0      0.0443      0.0443   +<method 'seek' of 'file' objects>
       +2422            0      0.9703      0.0096   +mercurial.store:374(__call__)
       +2422            0      0.0079      0.0069   +mercurial.revlog:810(_addchunk)
        5804            0      0.0204      0.0204   mercurial.revlog:305(rev)
        2426            0      0.9552      0.0177   mercurial.scmutil:218(__call__)
       +2426            0      0.9057      0.9057   +<open>
       +2426            0      0.0120      0.0083   +os.path:80(split)
       +2426            0      0.0061      0.0049   +mercurial.scmutil:92(__call__)
Time: real 1.950 secs (user 0.560+0.000 sys 0.220+0.000)

down to:

   CallCount    Recursive    Total(ms)   Inline(ms) module:lineno(function)
        1545            0      0.6035      0.6035   <open>
        1542            0      0.2697      0.2697   <method 'close' of 'file' objects>
           1            0      0.0547      0.0547   mercurial.revlog:637(headrevs)
          +1            0      0.0000      0.0000   +<len>
        1542            0      0.0389      0.0389   <method 'read' of 'file' objects>
        1540            0      0.0316      0.0316   <method 'seek' of 'file' objects>
        1853            0      0.0227      0.0227   <zlib.decompress>
        1557            0      1.2131      0.0226   mercurial.changelog:182(read)
       +1557            0      1.1398      0.0221   +mercurial.revlog:881(revision)
       +1555            0      0.0199      0.0094   +mercurial.changelog:28(decodeextra)
       +3114            0      0.0058      0.0058   +<method 'split' of 'str' objects>
       +3114            0      0.0196      0.0035   +mercurial.encoding:61(tolocal)
       +1557            0      0.0026      0.0026   +<method 'index' of 'str' objects>
        1557            0      1.1398      0.0221   mercurial.revlog:881(revision)
       +1557            0      1.0307      0.0047   +mercurial.revlog:847(_chunkraw)
       +1557            0      0.0287      0.0040   +mercurial.revlog:945(_checkhash)
       +1557            0      0.0018      0.0018   +<mercurial.mpatch.patches>
       +1557            0      0.0018      0.0018   +mercurial.revlog:326(node)
       +1557            0      0.0417      0.0013   +mercurial.revlog:857(_chunkbase)
        1540            0      1.0147      0.0210   mercurial.revlog:818(_loadchunk)
       +1540            0      0.2693      0.2693   +<method 'close' of 'file' objects>
       +1540            0      0.0360      0.0360   +<method 'read' of 'file' objects>
       +1540            0      0.0316      0.0316   +<method 'seek' of 'file' objects>
       +1540            0      0.6487      0.0070   +mercurial.store:374(__call__)
       +1540            0      0.0059      0.0052   +mercurial.revlog:810(_addchunk)
        3192            0      0.0173      0.0173   mercurial.revlog:305(rev)
        8184            0      0.0300      0.0147   <method 'decode' of 'str' objects>
       +5204            0      0.0149      0.0048   +encodings.utf_8:15(decode)
          +1            0      0.0004      0.0000   +encodings:71(search_function)
          43           26      0.0147      0.0129   <__import__>
Time: real 1.390 secs (user 0.450+0.000 sys 0.170+0.000)

diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -936,22 +936,29 @@ def branches(ui, repo, active=False, clo
     """
 
     hexfunc = ui.debugflag and hex or short
-    activebranches = [repo[n].branch() for n in repo.heads()]
-    def testactive(tag, node):
-        realhead = tag in activebranches
-        open = node in repo.branchheads(tag, closed=False)
-        return realhead and open
-    branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
-                          for tag, node in repo.branchtags().items()],
-                      reverse=True)
-
-    for isactive, node, tag in branches:
+
+    activebranches = set([repo[n].branch() for n in repo.heads()])
+    branches = []
+    for tag, heads in repo.branchmap().iteritems():
+        for h in reversed(heads):
+            ctx = repo[h]
+            isopen = not ctx.closesbranch()
+            if isopen:
+                tip = ctx
+                break
+        else:
+            tip = repo[heads[-1]]
+        isactive = tag in activebranches and isopen
+        branches.append((tip, isactive, isopen))
+    branches.sort(key=lambda i: (i[1], i[0].rev(), i[0].branch(), i[2]),
+                  reverse=True)
+
+    for ctx, isactive, isopen in branches:
         if (not active) or isactive:
-            hn = repo.lookup(node)
             if isactive:
                 label = 'branches.active'
                 notice = ''
-            elif hn not in repo.branchheads(tag, closed=False):
+            elif not isopen:
                 if not closed:
                     continue
                 label = 'branches.closed'
@@ -959,11 +966,12 @@ def branches(ui, repo, active=False, clo
             else:
                 label = 'branches.inactive'
                 notice = _(' (inactive)')
-            if tag == repo.dirstate.branch():
+            if ctx.branch() == repo.dirstate.branch():
                 label = 'branches.current'
-            rev = str(node).rjust(31 - encoding.colwidth(tag))
-            rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
-            tag = ui.label(tag, label)
+            rev = str(ctx.rev()).rjust(31 - encoding.colwidth(ctx.branch()))
+            rev = ui.label('%s:%s' % (rev, hexfunc(ctx.node())),
+                           'log.changeset')
+            tag = ui.label(ctx.branch(), label)
             if ui.quiet:
                 ui.write("%s\n" % tag)
             else:


More information about the Mercurial-devel mailing list