D3962: worker: ability to disable worker for CPU heavy tasks

indygreg (Gregory Szorc) phabricator at mercurial-scm.org
Wed Jul 18 00:43:41 UTC 2018


indygreg created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  The worker on Windows is implemented using a thread pool. If worker
  tasks are executing CPU bound code and holding onto the GIL, there
  will be non-substantial overhead in Python context switching between
  active threads. This can result in significant slowdowns of tasks.
  
  This commit teaches the code for determining whether to use a worker
  to take CPU heavy tasks into account. If work is CPU heavy, the
  thread based Windows worker implementation rejects it.
  
  By itself, this patch is backwards compatible and shouldn't be overly
  contentious. I concede the "cost" of "CPU heavy" work should probably
  be a number and worked into an algorithm somehow. But I'm not sure
  what the algorithm should be and a bool is the easiest to implement.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D3962

AFFECTED FILES
  mercurial/worker.py
  tests/test-simple-update.t

CHANGE DETAILS

diff --git a/tests/test-simple-update.t b/tests/test-simple-update.t
--- a/tests/test-simple-update.t
+++ b/tests/test-simple-update.t
@@ -65,7 +65,7 @@
 
   $ cat <<EOF > forceworker.py
   > from mercurial import extensions, worker
-  > def nocost(orig, ui, costperop, nops):
+  > def nocost(orig, ui, costperop, nops, cpuheavy=False):
   >     return worker._numworkers(ui) > 1
   > def uisetup(ui):
   >     extensions.wrapfunction(worker, 'worthwhile', nocost)
diff --git a/mercurial/worker.py b/mercurial/worker.py
--- a/mercurial/worker.py
+++ b/mercurial/worker.py
@@ -57,18 +57,27 @@
 
 if pycompat.isposix or pycompat.iswindows:
     _STARTUP_COST = 0.01
+    # The Windows worker is thread based. If tasks are CPU bound, threads
+    # in the presence of the GIL result in excessive context switching and
+    # this overhead can slow down execution.
+    _ALLOW_CPU_HEAVY = False
 else:
     _STARTUP_COST = 1e30
+    _ALLOW_CPU_HEAVY = True
 
-def worthwhile(ui, costperop, nops):
+def worthwhile(ui, costperop, nops, cpuheavy=False):
     '''try to determine whether the benefit of multiple processes can
     outweigh the cost of starting them'''
+
+    if cpuheavy and not _ALLOW_CPU_HEAVY:
+        return False
+
     linear = costperop * nops
     workers = _numworkers(ui)
     benefit = linear - (_STARTUP_COST * workers + linear / workers)
     return benefit >= 0.15
 
-def worker(ui, costperarg, func, staticargs, args):
+def worker(ui, costperarg, func, staticargs, args, cpuheavy=False):
     '''run a function, possibly in parallel in multiple worker
     processes.
 
@@ -82,9 +91,13 @@
 
     args - arguments to split into chunks, to pass to individual
     workers
+
+    cpuheavy - whether each individual item of work requires a lot of CPU.
+    CPU bound tasks may not use workers if e.g. the worker is implemented by
+    thread pools (which are subject to the GIL).
     '''
     enabled = ui.configbool('worker', 'enabled')
-    if enabled and worthwhile(ui, costperarg, len(args)):
+    if enabled and worthwhile(ui, costperarg, len(args), cpuheavy=cpuheavy):
         return _platformworker(ui, func, staticargs, args)
     return func(*staticargs + (args,))
 



To: indygreg, #hg-reviewers
Cc: mercurial-devel


More information about the Mercurial-devel mailing list