prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Concurrent execution results in uneven work per thread
  I'm running `pre-commit` from current `master` to test the concurrency feature introduced with #851. While it in general seems to work, work is distributed pretty uneven. One hook we run is [`prospector`](https://github.com/guykisel/prospector-mirror) which is nice for testing, because it takes a relatively long time and it prints the time taken in its output.
  
  Running `pre-commit run -a --verbose prospector | grep "Time Taken"` on a medium sized project (~100 Python files) results in the following distribution of work to the available 4 logical CPU cores:
  ```
  Time Taken: 17.10 seconds
  Time Taken: 8.70 seconds
  Time Taken: 18.68 seconds
  Time Taken: 108.02 seconds
  ```
  
  Especially compared to running it with concurrency disabled (using `PRE_COMMIT_NO_CONCURRENCY`), it's pretty obvious that concurrency doesn't provide any real benefit here:
  ```
  Time Taken: 116.95 seconds
  ```
  
  I'd be happy to help debugging this further. Just tell me what other information you need. :slightly_smiling_face: 
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `pre_commit/languages/helpers.py`
  Content:
  ```
  1 from __future__ import unicode_literals
  2 
  3 import multiprocessing
  4 import os
  5 import shlex
  6 
  7 from pre_commit.util import cmd_output
  8 from pre_commit.xargs import xargs
  9 
  10 
  11 def run_setup_cmd(prefix, cmd):
  12     cmd_output(*cmd, cwd=prefix.prefix_dir, encoding=None)
  13 
  14 
  15 def environment_dir(ENVIRONMENT_DIR, language_version):
  16     if ENVIRONMENT_DIR is None:
  17         return None
  18     else:
  19         return '{}-{}'.format(ENVIRONMENT_DIR, language_version)
  20 
  21 
  22 def to_cmd(hook):
  23     return tuple(shlex.split(hook['entry'])) + tuple(hook['args'])
  24 
  25 
  26 def assert_version_default(binary, version):
  27     if version != 'default':
  28         raise AssertionError(
  29             'For now, pre-commit requires system-installed {}'.format(binary),
  30         )
  31 
  32 
  33 def assert_no_additional_deps(lang, additional_deps):
  34     if additional_deps:
  35         raise AssertionError(
  36             'For now, pre-commit does not support '
  37             'additional_dependencies for {}'.format(lang),
  38         )
  39 
  40 
  41 def basic_get_default_version():
  42     return 'default'
  43 
  44 
  45 def basic_healthy(prefix, language_version):
  46     return True
  47 
  48 
  49 def no_install(prefix, version, additional_dependencies):
  50     raise AssertionError('This type is not installable')
  51 
  52 
  53 def target_concurrency(hook):
  54     if hook['require_serial'] or 'PRE_COMMIT_NO_CONCURRENCY' in os.environ:
  55         return 1
  56     else:
  57         # Travis appears to have a bunch of CPUs, but we can't use them all.
  58         if 'TRAVIS' in os.environ:
  59             return 2
  60         else:
  61             try:
  62                 return multiprocessing.cpu_count()
  63             except NotImplementedError:
  64                 return 1
  65 
  66 
  67 def run_xargs(hook, cmd, file_args):
  68     return xargs(cmd, file_args, target_concurrency=target_concurrency(hook))
  69
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/pre_commit/languages/helpers.py b/pre_commit/languages/helpers.py
  --- a/pre_commit/languages/helpers.py
  +++ b/pre_commit/languages/helpers.py
  @@ -2,12 +2,18 @@
   
   import multiprocessing
   import os
  +import random
   import shlex
   
  +import six
  +
   from pre_commit.util import cmd_output
   from pre_commit.xargs import xargs
   
   
  +FIXED_RANDOM_SEED = 1542676186
  +
  +
   def run_setup_cmd(prefix, cmd):
       cmd_output(*cmd, cwd=prefix.prefix_dir, encoding=None)
   
  @@ -64,5 +70,21 @@
                   return 1
   
   
  +def _shuffled(seq):
  +    """Deterministically shuffle identically under both py2 + py3."""
  +    fixed_random = random.Random()
  +    if six.PY2:  # pragma: no cover (py2)
  +        fixed_random.seed(FIXED_RANDOM_SEED)
  +    else:
  +        fixed_random.seed(FIXED_RANDOM_SEED, version=1)
  +
  +    seq = list(seq)
  +    random.shuffle(seq, random=fixed_random.random)
  +    return seq
  +
  +
   def run_xargs(hook, cmd, file_args):
  +    # Shuffle the files so that they more evenly fill out the xargs partitions,
  +    # but do it deterministically in case a hook cares about ordering.
  +    file_args = _shuffled(file_args)
       return xargs(cmd, file_args, target_concurrency=target_concurrency(hook))
  
verification_info: {"golden_diff": "diff --git a/pre_commit/languages/helpers.py b/pre_commit/languages/helpers.py\n--- a/pre_commit/languages/helpers.py\n+++ b/pre_commit/languages/helpers.py\n@@ -2,12 +2,18 @@\n \n import multiprocessing\n import os\n+import random\n import shlex\n \n+import six\n+\n from pre_commit.util import cmd_output\n from pre_commit.xargs import xargs\n \n \n+FIXED_RANDOM_SEED = 1542676186\n+\n+\n def run_setup_cmd(prefix, cmd):\n     cmd_output(*cmd, cwd=prefix.prefix_dir, encoding=None)\n \n@@ -64,5 +70,21 @@\n                 return 1\n \n \n+def _shuffled(seq):\n+    \"\"\"Deterministically shuffle identically under both py2 + py3.\"\"\"\n+    fixed_random = random.Random()\n+    if six.PY2:  # pragma: no cover (py2)\n+        fixed_random.seed(FIXED_RANDOM_SEED)\n+    else:\n+        fixed_random.seed(FIXED_RANDOM_SEED, version=1)\n+\n+    seq = list(seq)\n+    random.shuffle(seq, random=fixed_random.random)\n+    return seq\n+\n+\n def run_xargs(hook, cmd, file_args):\n+    # Shuffle the files so that they more evenly fill out the xargs partitions,\n+    # but do it deterministically in case a hook cares about ordering.\n+    file_args = _shuffled(file_args)\n     return xargs(cmd, file_args, target_concurrency=target_concurrency(hook))\n", "issue": "Concurrent execution results in uneven work per thread\nI'm running `pre-commit` from current `master` to test the concurrency feature introduced with #851. While it in general seems to work, work is distributed pretty uneven. One hook we run is [`prospector`](https://github.com/guykisel/prospector-mirror) which is nice for testing, because it takes a relatively long time and it prints the time taken in its output.\r\n\r\nRunning `pre-commit run -a --verbose prospector | grep \"Time Taken\"` on a medium sized project (~100 Python files) results in the following distribution of work to the available 4 logical CPU cores:\r\n```\r\nTime Taken: 17.10 seconds\r\nTime Taken: 8.70 seconds\r\nTime Taken: 18.68 seconds\r\nTime Taken: 108.02 seconds\r\n```\r\n\r\nEspecially compared to running it with concurrency disabled (using `PRE_COMMIT_NO_CONCURRENCY`), it's pretty obvious that concurrency doesn't provide any real benefit here:\r\n```\r\nTime Taken: 116.95 seconds\r\n```\r\n\r\nI'd be happy to help debugging this further. Just tell me what other information you need. :slightly_smiling_face: \n", "before_files": [{"content": "from __future__ import unicode_literals\n\nimport multiprocessing\nimport os\nimport shlex\n\nfrom pre_commit.util import cmd_output\nfrom pre_commit.xargs import xargs\n\n\ndef run_setup_cmd(prefix, cmd):\n    cmd_output(*cmd, cwd=prefix.prefix_dir, encoding=None)\n\n\ndef environment_dir(ENVIRONMENT_DIR, language_version):\n    if ENVIRONMENT_DIR is None:\n        return None\n    else:\n        return '{}-{}'.format(ENVIRONMENT_DIR, language_version)\n\n\ndef to_cmd(hook):\n    return tuple(shlex.split(hook['entry'])) + tuple(hook['args'])\n\n\ndef assert_version_default(binary, version):\n    if version != 'default':\n        raise AssertionError(\n            'For now, pre-commit requires system-installed {}'.format(binary),\n        )\n\n\ndef assert_no_additional_deps(lang, additional_deps):\n    if additional_deps:\n        raise AssertionError(\n            'For now, pre-commit does not support '\n            'additional_dependencies for {}'.format(lang),\n        )\n\n\ndef basic_get_default_version():\n    return 'default'\n\n\ndef basic_healthy(prefix, language_version):\n    return True\n\n\ndef no_install(prefix, version, additional_dependencies):\n    raise AssertionError('This type is not installable')\n\n\ndef target_concurrency(hook):\n    if hook['require_serial'] or 'PRE_COMMIT_NO_CONCURRENCY' in os.environ:\n        return 1\n    else:\n        # Travis appears to have a bunch of CPUs, but we can't use them all.\n        if 'TRAVIS' in os.environ:\n            return 2\n        else:\n            try:\n                return multiprocessing.cpu_count()\n            except NotImplementedError:\n                return 1\n\n\ndef run_xargs(hook, cmd, file_args):\n    return xargs(cmd, file_args, target_concurrency=target_concurrency(hook))\n", "path": "pre_commit/languages/helpers.py"}], "after_files": [{"content": "from __future__ import unicode_literals\n\nimport multiprocessing\nimport os\nimport random\nimport shlex\n\nimport six\n\nfrom pre_commit.util import cmd_output\nfrom pre_commit.xargs import xargs\n\n\nFIXED_RANDOM_SEED = 1542676186\n\n\ndef run_setup_cmd(prefix, cmd):\n    cmd_output(*cmd, cwd=prefix.prefix_dir, encoding=None)\n\n\ndef environment_dir(ENVIRONMENT_DIR, language_version):\n    if ENVIRONMENT_DIR is None:\n        return None\n    else:\n        return '{}-{}'.format(ENVIRONMENT_DIR, language_version)\n\n\ndef to_cmd(hook):\n    return tuple(shlex.split(hook['entry'])) + tuple(hook['args'])\n\n\ndef assert_version_default(binary, version):\n    if version != 'default':\n        raise AssertionError(\n            'For now, pre-commit requires system-installed {}'.format(binary),\n        )\n\n\ndef assert_no_additional_deps(lang, additional_deps):\n    if additional_deps:\n        raise AssertionError(\n            'For now, pre-commit does not support '\n            'additional_dependencies for {}'.format(lang),\n        )\n\n\ndef basic_get_default_version():\n    return 'default'\n\n\ndef basic_healthy(prefix, language_version):\n    return True\n\n\ndef no_install(prefix, version, additional_dependencies):\n    raise AssertionError('This type is not installable')\n\n\ndef target_concurrency(hook):\n    if hook['require_serial'] or 'PRE_COMMIT_NO_CONCURRENCY' in os.environ:\n        return 1\n    else:\n        # Travis appears to have a bunch of CPUs, but we can't use them all.\n        if 'TRAVIS' in os.environ:\n            return 2\n        else:\n            try:\n                return multiprocessing.cpu_count()\n            except NotImplementedError:\n                return 1\n\n\ndef _shuffled(seq):\n    \"\"\"Deterministically shuffle identically under both py2 + py3.\"\"\"\n    fixed_random = random.Random()\n    if six.PY2:  # pragma: no cover (py2)\n        fixed_random.seed(FIXED_RANDOM_SEED)\n    else:\n        fixed_random.seed(FIXED_RANDOM_SEED, version=1)\n\n    seq = list(seq)\n    random.shuffle(seq, random=fixed_random.random)\n    return seq\n\n\ndef run_xargs(hook, cmd, file_args):\n    # Shuffle the files so that they more evenly fill out the xargs partitions,\n    # but do it deterministically in case a hook cares about ordering.\n    file_args = _shuffled(file_args)\n    return xargs(cmd, file_args, target_concurrency=target_concurrency(hook))\n", "path": "pre_commit/languages/helpers.py"}]}