Patchwork [4,of,4] contrib: provide a small script that draw performance plot

login
register
mail settings
Submitter Boris Feld
Date Dec. 14, 2018, 9:24 p.m.
Message ID <e7cc20159b972dbaf602.1544822678@localhost.localdomain>
Download mbox | patch
Permalink /patch/37162/
State Accepted
Headers show

Comments

Boris Feld - Dec. 14, 2018, 9:24 p.m.
# HG changeset patch
# User Paul Morelle <paul.morelle@octobus.net>
# Date 1544807719 -3600
#      Fri Dec 14 18:15:19 2018 +0100
# Node ID e7cc20159b972dbaf602737b8bbc900ba60b2438
# Parent  571449ab5d585525640279fef7979f9cc4f31637
# EXP-Topic sparse-revlog-corner-cases
# Available At https://bitbucket.org/octobus/mercurial-devel/
#              hg pull https://bitbucket.org/octobus/mercurial-devel/ -r e7cc20159b97
contrib: provide a small script that draw performance plot

We have been using this script to look into the result of various runs of the
`hg perfrevlogwrite` command. It seems useful enough to be shared more widely.

Patch

diff --git a/contrib/perf-utils/perf-revlog-write-plot.py b/contrib/perf-utils/perf-revlog-write-plot.py
new file mode 100755
--- /dev/null
+++ b/contrib/perf-utils/perf-revlog-write-plot.py
@@ -0,0 +1,125 @@ 
+#!/usr/bin/env python
+#
+#  Copyright 2018 Paul Morelle <Paul.Morelle@octobus.net>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+#
+# This script use the output of `hg perfrevlogwrite -T json --details` to draw
+# various plot related to write performance in a revlog
+#
+# usage: perf-revlog-write-plot.py details.json
+from __future__ import absolute_import, print_function
+import json
+import re
+
+import numpy as np
+import scipy.signal
+
+from matplotlib import (
+    pyplot as plt,
+    ticker as mticker,
+)
+
+
+def plot(data):
+    items = {}
+    re_title = re.compile(r'^revisions #\d+ of \d+, rev (\d+)$')
+    for item in data:
+        m = re_title.match(item['title'])
+        if m is None:
+            continue
+
+        rev = int(m.group(1))
+        items[rev] = item
+
+    min_rev = min(items.keys())
+    max_rev = max(items.keys())
+    ary = np.empty((2, max_rev - min_rev + 1))
+    for rev, item in items.items():
+        ary[0][rev - min_rev] = rev
+        ary[1][rev - min_rev] = item['wall']
+
+    fig = plt.figure()
+    comb_plt = fig.add_subplot(211)
+    other_plt = fig.add_subplot(212)
+
+    comb_plt.plot(ary[0],
+                  np.cumsum(ary[1]),
+                  color='red',
+                  linewidth=1,
+                  label='comb')
+
+    plots = []
+    p = other_plt.plot(ary[0],
+                       ary[1],
+                       color='red',
+                       linewidth=1,
+                       label='wall')
+    plots.append(p)
+
+    colors = {
+        10: ('green', 'xkcd:grass green'),
+        100: ('blue', 'xkcd:bright blue'),
+        1000: ('purple', 'xkcd:dark pink'),
+    }
+    for n, color in colors.items():
+        avg_n = np.convolve(ary[1], np.full(n, 1. / n), 'valid')
+        p = other_plt.plot(ary[0][n - 1:],
+                           avg_n,
+                           color=color[0],
+                           linewidth=1,
+                           label='avg time last %d' % n)
+        plots.append(p)
+
+        med_n = scipy.signal.medfilt(ary[1], n + 1)
+        p = other_plt.plot(ary[0],
+                           med_n,
+                           color=color[1],
+                           linewidth=1,
+                           label='median time last %d' % n)
+        plots.append(p)
+
+    formatter = mticker.ScalarFormatter()
+    formatter.set_scientific(False)
+    formatter.set_useOffset(False)
+
+    comb_plt.grid()
+    comb_plt.xaxis.set_major_formatter(formatter)
+    comb_plt.legend()
+
+    other_plt.grid()
+    other_plt.xaxis.set_major_formatter(formatter)
+    leg = other_plt.legend()
+    leg2plot = {}
+    for legline, plot in zip(leg.get_lines(), plots):
+        legline.set_picker(5)
+        leg2plot[legline] = plot
+
+    def onpick(event):
+        legline = event.artist
+        plot = leg2plot[legline]
+        visible = not plot[0].get_visible()
+        for l in plot:
+            l.set_visible(visible)
+
+        if visible:
+            legline.set_alpha(1.0)
+        else:
+            legline.set_alpha(0.2)
+        fig.canvas.draw()
+    fig.canvas.mpl_connect('pick_event', onpick)
+
+    plt.show()
+
+
+if __name__ == '__main__':
+    import sys
+
+    if len(sys.argv) > 1:
+        print('reading from %r' % sys.argv[1])
+        with open(sys.argv[1], 'r') as fp:
+            plot(json.load(fp))
+    else:
+        print('reading from stdin')
+        plot(json.load(sys.stdin))
diff --git a/tests/test-check-module-imports.t b/tests/test-check-module-imports.t
--- a/tests/test-check-module-imports.t
+++ b/tests/test-check-module-imports.t
@@ -23,6 +23,7 @@  outputs, which should be fixed later.
   > -X contrib/packaging/hg-docker \
   > -X contrib/python-zstandard/ \
   > -X contrib/win32/hgwebdir_wsgi.py \
+  > -X contrib/perf-utils/perf-revlog-write-plot.py \
   > -X doc/gendoc.py \
   > -X doc/hgmanpage.py \
   > -X i18n/posplit \