Mercurial > hg-stable
changeset 35599:af25237be091
perf: add threading capability to perfbdiff
Since we are releasing the GIL during diffing, it is interesting to see how a
thread pool would perform on diffing. We add a new `--threads` argument to
commands. Synchronizing the thread pool is a bit complex because we want to be
able to reuse it from one run to another.
On my computer (i7 with 4 cores + hyperthreading), I get the following data for
about 12000 revisions:
threads wall comb wall gain comb overhead
none 31.596715 31.59 0.00% 0.00%
1 31.621228 31.62 -0.08% 0.09%
2 16.406202 32.8 48.08% 3.83%
3 11.598334 34.76 63.29% 10.03%
4 9.205421 36.77 70.87% 16.40%
5 8.517604 42.51 73.04% 34.57%
6 7.94645 47.58 74.85% 50.62%
7 7.434972 51.92 76.47% 64.36%
8 7.070638 55.34 77.62% 75.18%
Compared to the feature disabled (threads=0), the overhead is negligible with
the threading code (threads=1), and the gain is already 48% with two threads.
author | Boris Feld <boris.feld@octobus.net> |
---|---|
date | Sun, 17 Dec 2017 04:31:27 +0100 |
parents | 29248ea26b29 |
children | 31acf6619f08 |
files | contrib/perf.py tests/test-contrib-perf.t |
diffstat | 2 files changed, 48 insertions(+), 7 deletions(-) [+] |
line wrap: on
line diff
--- a/contrib/perf.py Wed Jan 10 17:24:25 2018 +0100 +++ b/contrib/perf.py Sun Dec 17 04:31:27 2017 +0100 @@ -25,7 +25,9 @@ import random import struct import sys +import threading import time +import util.queue from mercurial import ( changegroup, cmdutil, @@ -933,11 +935,25 @@ timer(d) fm.end() +def _bdiffworker(q, ready, done): + while not done.is_set(): + pair = q.get() + while pair is not None: + mdiff.textdiff(*pair) + q.task_done() + pair = q.get() + q.task_done() # for the None one + with ready: + ready.wait() + @command('perfbdiff', revlogopts + formatteropts + [ ('', 'count', 1, 'number of revisions to test (when using --startrev)'), - ('', 'alldata', False, 'test bdiffs for all associated revisions')], + ('', 'alldata', False, 'test bdiffs for all associated revisions'), + ('', 'threads', 0, 'number of thread to use (disable with 0)'), + ], + '-c|-m|FILE REV') -def perfbdiff(ui, repo, file_, rev=None, count=None, **opts): +def perfbdiff(ui, repo, file_, rev=None, count=None, threads=0, **opts): """benchmark a bdiff between revisions By default, benchmark a bdiff between its delta parent and itself. @@ -983,14 +999,39 @@ dp = r.deltaparent(rev) textpairs.append((r.revision(dp), r.revision(rev))) - def d(): - for pair in textpairs: - mdiff.textdiff(*pair) - + withthreads = threads > 0 + if not withthreads: + def d(): + for pair in textpairs: + mdiff.textdiff(*pair) + else: + q = util.queue() + for i in xrange(threads): + q.put(None) + ready = threading.Condition() + done = threading.Event() + for i in xrange(threads): + threading.Thread(target=_bdiffworker, args=(q, ready, done)).start() + q.join() + def d(): + for pair in textpairs: + q.put(pair) + for i in xrange(threads): + q.put(None) + with ready: + ready.notify_all() + q.join() timer, fm = gettimer(ui, opts) timer(d) fm.end() + if withthreads: + done.set() + for i in xrange(threads): + q.put(None) + with ready: + ready.notify_all() + @command('perfdiffwd', formatteropts) def perfdiffwd(ui, repo, **opts): """Profile diff of working directory changes"""
--- a/tests/test-contrib-perf.t Wed Jan 10 17:24:25 2018 +0100 +++ b/tests/test-contrib-perf.t Sun Dec 17 04:31:27 2017 +0100 @@ -175,7 +175,7 @@ $ (testrepohg files -r 1.2 glob:mercurial/*.c glob:mercurial/*.py; > testrepohg files -r tip glob:mercurial/*.c glob:mercurial/*.py) | > "$TESTDIR"/check-perf-code.py contrib/perf.py - contrib/perf.py:498: + contrib/perf.py:\d+: (re) > from mercurial import ( import newer module separately in try clause for early Mercurial [1]