[PATCH 1 of 5 RESEND] contrib: add "hgperf" command to measure performance of commands easily

FUJIWARA Katsunori foozy at lares.dti.ne.jp
Sat Feb 15 04:59:33 CST 2014


# HG changeset patch
# User FUJIWARA Katsunori <foozy at lares.dti.ne.jp>
# Date 1392461480 -32400
#      Sat Feb 15 19:51:20 2014 +0900
# Node ID 89fbd429ad6b0b182e593dc39885e405d25762b5
# Parent  7648e9aef6eeab00a0946e877690e94fb12d389b
contrib: add "hgperf" command to measure performance of commands easily

Newly added "hgperf" command repeats "dispatch.runcommand()" for
specified Mercurial command and measure performance of it in the same
manner of "contrib/perf.py" extension.

Users (mainly developers) can examine performance of the target
command easily, without adding new entry for it to "contrib/perf.py"
extension.

diff --git a/contrib/hgperf b/contrib/hgperf
new file mode 100644
--- /dev/null
+++ b/contrib/hgperf
@@ -0,0 +1,100 @@
+#!/usr/bin/env python
+#
+# hgperf - measure performance of Mercurial commands
+#
+# Copyright 2014 Matt Mackall <mpm at selenic.com>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+'''measure performance of Mercurial commands
+
+Using ``hgperf`` instead of ``hg`` measures performance of the target
+Mercurial command. For example, the execution below measures
+performance of :hg:`heads --topo`::
+
+    $ hgperf heads --topo
+
+All command output via ``ui`` is suppressed, and just measurement
+result is displayed: see also "perf" extension in "contrib".
+
+Costs of processing before dispatching to the command function like
+below are not measured::
+
+    - parsing command line (e.g. option validity check)
+    - reading configuration files in
+
+But ``pre-`` and ``post-`` hook invocation for the target command is
+measured, even though these are invoked before or after dispatching to
+the command function, because these may be required to repeat
+execution of the target command correctly.
+'''
+
+import os
+import sys
+
+libdir = '@LIBDIR@'
+
+if libdir != '@' 'LIBDIR' '@':
+    if not os.path.isabs(libdir):
+        libdir = os.path.join(os.path.dirname(os.path.realpath(__file__)),
+                              libdir)
+        libdir = os.path.abspath(libdir)
+    sys.path.insert(0, libdir)
+
+# enable importing on demand to reduce startup time
+try:
+    from mercurial import demandimport; demandimport.enable()
+except ImportError:
+    import sys
+    sys.stderr.write("abort: couldn't find mercurial libraries in [%s]\n" %
+                     ' '.join(sys.path))
+    sys.stderr.write("(check your install and PYTHONPATH)\n")
+    sys.exit(-1)
+
+import mercurial.util
+import mercurial.dispatch
+
+import time
+
+def timer(func, title=None):
+    results = []
+    begin = time.time()
+    count = 0
+    while True:
+        ostart = os.times()
+        cstart = time.time()
+        r = func()
+        cstop = time.time()
+        ostop = os.times()
+        count += 1
+        a, b = ostart, ostop
+        results.append((cstop - cstart, b[0] - a[0], b[1]-a[1]))
+        if cstop - begin > 3 and count >= 100:
+            break
+        if cstop - begin > 10 and count >= 3:
+            break
+    if title:
+        sys.stderr.write("! %s\n" % title)
+    if r:
+        sys.stderr.write("! result: %s\n" % r)
+    m = min(results)
+    sys.stderr.write("! wall %f comb %f user %f sys %f (best of %d)\n"
+                     % (m[0], m[1] + m[2], m[1], m[2], count))
+
+orgruncommand = mercurial.dispatch.runcommand
+
+def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
+    ui.pushbuffer()
+    lui.pushbuffer()
+    timer(lambda : orgruncommand(lui, repo, cmd, fullargs, ui,
+                                 options, d, cmdpats, cmdoptions))
+    ui.popbuffer()
+    lui.popbuffer()
+
+mercurial.dispatch.runcommand = runcommand
+
+for fp in (sys.stdin, sys.stdout, sys.stderr):
+    mercurial.util.setbinary(fp)
+
+mercurial.dispatch.run()


More information about the Mercurial-devel mailing list