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

login
register
mail settings
Submitter Katsunori FUJIWARA
Date Oct. 8, 2016, 4:18 p.m.
Message ID <c0a43c0925f918e00943.1475943508@feefifofum>
Download mbox | patch
Permalink /patch/16949/
State Accepted
Headers show

Comments

Katsunori FUJIWARA - Oct. 8, 2016, 4:18 p.m.
# HG changeset patch
# User FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
# Date 1475942600 -32400
#      Sun Oct 09 01:03:20 2016 +0900
# Node ID c0a43c0925f918e009436b875dd32d5ef1471702
# Parent  27633d2fb3282c3ba3e7eb9e2232d1c55cab4406
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 perftags clear tags cache correctly, and to increase
"historical portability" of 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.

mozilla-central repo (85 tags on 308365 revs) with each Mercurial
version between before and after this patch.

  ==== ========= =========
  ver  before    after
  ==== ========= =========
  1.9  0.476062  0.466464
       ------- *1 -------
  2.0  0.346309  0.458327
  2.1  0.343106  0.454489
       ------- *2 -------
  2.2  0.069790  0.071263
  2.3  0.067829  0.069340
  2.4  0.068075  0.069573
       ------- *3 -------
  2.5  0.021896  0.022406
  2.6  0.021900  0.022374
  2.7  0.021883  0.022379
  2.8  0.021949  0.022327
  2.9  0.021877  0.022330
  3.0  0.021860  0.022314
  3.1  0.021869  0.022669
  3.2  0.021831  0.022668
  3.3  0.021809  0.022691
  3.4  0.021861  0.022916
  3.5  0.019335  0.020749
  3.6  0.019319  0.020866
  3.7  0.018781  0.020251
       ------- *4 -------
  3.8  0.068262  0.072558
  3.9  0.069682  0.073773
  ==== ========= =========

(*1) repo._tags was replaced with repo._tagscache at this point

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

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

(*3) filtered changelog was cached in repoview as repoview.changelog
     at this point (by 4d92e2d75cff)

     This avoids calculation of filtered changelog at each repetition
     of t().

(*4) calculation of filtered changelog was included into wall time at
     this point (by 332926212ef8), again

     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
@@ -304,6 +304,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 existing code paths
+        # expect _tagscache to be a structured object.
+        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
+
+    repotags = safeattrsetter(repo, '_tags', ignoremissing=True)
+    if repotags: # since 1.4 (or 5614a628d173)
+        return lambda : repotags.set(None)
+
+    repotagscache = safeattrsetter(repo, 'tagscache', ignoremissing=True)
+    if repotagscache: # since 0.6 (or d7df759d0e97)
+        return lambda : repotagscache.set(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)
@@ -375,10 +404,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()