Patchwork [6,of,8] perf: make perftags clear tags cache correctly

login
register
mail settings
Submitter Katsunori FUJIWARA
Date Aug. 8, 2016, 9:59 a.m.
Message ID <06bb7745a1d6e54bfc1e.1470650385@juju>
Download mbox | patch
Permalink /patch/16189/
State Changes Requested
Delegated to: Yuya Nishihara
Headers show

Comments

Katsunori FUJIWARA - Aug. 8, 2016, 9:59 a.m.
# HG changeset patch
# User FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
# Date 1470649698 -32400
#      Mon Aug 08 18:48:18 2016 +0900
# Node ID 06bb7745a1d6e54bfc1e320ced38cd03d40e66ff
# Parent  fa32febd48fe4ca7f9f7e3f5c021aef7c1ad174c
perf: make perftags clear tags cache correctly

Before this patch, "hg perftags" command doesn't measure performance
of "repo.tags()" correctly, because it doesn't clear tags cache
correctly.

9dca7653b525 replaced repo._tags with repo._tagscache, but didn't
change the code path to clear tags cache in perftags() at that time.
BTW, full history of "tags cache" is:

  - d7df759d0e97 (or 0.6) introduced repo.tagscache as the first "tags cache"
  - 5614a628d173 (or 1.4) replaced repo.tagscache with repo._tags
  - 9dca7653b525 (or 2.0) replaced repo._tags with repo._tagscache
  - 98c867ac1330 (or 2.5) made repo._tagscache filteredpropertycache

To make "hg perftags" clear tags cache correctly, and to increase
"historical portability" of "hg perftags", this patch examines
existence of attributes in repo object, and guess appropriate
procedure to clear tags cache.

To avoid examining existence of attributes at each repetition, this
patch makes repocleartagscachefunc() return the function, which
actually clears tags cache.

This patch uses "<target><what-to-do>func()" naming rule for the
function, which returns the function to do <what-to-do> on <target>,
to avoid name collision in the future, and to group functions by
<target> for readability.

FYI, the table below compares wall time of perftags on recent
mozilla-central repo with each Mercurial version between before and
after this patch.

  ==== ========== =========
  ver  before     after
  ==== ========== =========
  1.9  0.471074   0.467813
       -------- *1 --------
  2.0  0.346238   0.452377
  2.1  0.344596   0.476910
       -------- *2 --------
  2.2  0.069810   0.071222
  2.3  0.067856   0.069523
  2.4  0.067941   0.069545
       -------- *3 --------
  2.5  0.021905   0.022396
  2.6  0.021885   0.022392
  2.7  0.021888   0.022401
  2.8  0.021896   0.022320
  2.9  0.021898   0.022311
  3.0  0.021854   0.022339
  3.1  0.021873   0.022704
  3.2  0.021830   0.022680
  3.3  0.021804   0.022659
  3.4  0.021851   0.022804
  3.5  0.019330   0.020820
  3.6  0.019330   0.020811
  3.7  0.018779   0.020764
       -------- *4 --------
  3.8  0.068357   0.072757
  3.9  0.069433   0.074014
  ==== ========== =========

(*1) repo._tags was replaced with repo._tagscache

     "repo._tags = None" of "before" doesn't clear tags cache for
     Mercurial 2.0 or later. IMHO, this causes significant gap between
     1.9 and 2.0 of "before".

(*2) I'm not sure about this significant gap, but release note
     described "a number of significant performance improvements for
     large repositories"

(*3) repo._tagscache was made as filteredpropertycache

(*4) Calculation of repoview.changelog cache was included into wall
     time. See below for detail about this significant gap:

     https://www.mercurial-scm.org/pipermail/mercurial-devel/2016-April/083410.html

Patch

diff --git a/contrib/perf.py b/contrib/perf.py
--- a/contrib/perf.py
+++ b/contrib/perf.py
@@ -288,6 +288,35 @@  def getvfs(repo):
     else:
         return getattr(repo, 'opener')
 
+def repocleartagscachefunc(repo):
+    """Return the function to clear tags cache according to repo internal API
+    """
+    if util.safehasattr(repo, '_tagscache'): # since 2.0 (or 9dca7653b525)
+        # in this case, setattr(repo, '_tagscache', None) or so isn't
+        # correct way to clear tags cache, because _tagscache itself
+        # is defined as a method of localrepository class.
+        def clearcache():
+            # _tagscache has been filteredpropertycache since 2.5 (or
+            # 98c867ac1330), and delattr() can't work in such case
+            if '_tagscache' in vars(repo):
+                del repo.__dict__['_tagscache']
+        return clearcache
+
+    settags = safeattrsetter(repo, '_tags', ignoremissing=True)
+    if settags: # since 1.4 (or 5614a628d173)
+        return lambda : settags(None)
+
+    settagscache = safeattrsetter(repo, 'tagscache', ignoremissing=True)
+    if settagscache: # since 0.6 (or d7df759d0e97)
+        return lambda : settagscache(None)
+
+    # Mercurial earlier than 0.6 (or d7df759d0e97) logically reaches
+    # this point, but it isn't so problematic, because:
+    # - repo.tags of such Mercurial isn't "callable", and repo.tags()
+    #   in perftags() causes failure soon
+    # - perf.py itself has been available since 1.1 (or eb240755386d)
+    raise error.Abort(("tags API of this hg command is unknown"))
+
 # perf commands
 
 @command('perfwalk', formatteropts)
@@ -359,10 +388,11 @@  def perftags(ui, repo, **opts):
     import mercurial.manifest
     timer, fm = gettimer(ui, opts)
     svfs = getsvfs(repo)
+    repocleartagscache = repocleartagscachefunc(repo)
     def t():
         repo.changelog = mercurial.changelog.changelog(svfs)
         repo.manifest = mercurial.manifest.manifest(svfs)
-        repo._tags = None
+        repocleartagscache()
         return len(repo.tags())
     timer(t)
     fm.end()