changeset 20839:377a111d1cd2

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.
author FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
date Sat, 15 Feb 2014 19:51:20 +0900
parents fe849868fc5a
children 308344d80fe5
files contrib/hgperf
diffstat 1 files changed, 100 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/hgperf	Sat Feb 15 19:51:20 2014 +0900
@@ -0,0 +1,100 @@
+#!/usr/bin/env python
+#
+# hgperf - measure performance of Mercurial commands
+#
+# Copyright 2014 Matt Mackall <mpm@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()