Repository: Qiskit/qiskit

Issue:
Improve default selection for parallel macOS
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues to confirm this idea does not exist. -->

### What is the expected enhancement?

As a follow up to #5324 we need to improve the default selection for parallel_map on OSX. In that PR we changed it to default to not be enabled for all macOS users, however this was probably a bit too conservative. We should improve the logic to find the problematic OS and Python version combinations and disable it by default on those platforms (and likely warn about it). In other cases we can leave the previous default and enable multiple processes by default.


Discussion:

Resolving patch:
diff --git a/qiskit/tools/parallel.py b/qiskit/tools/parallel.py
--- a/qiskit/tools/parallel.py
+++ b/qiskit/tools/parallel.py
@@ -50,6 +50,8 @@
 
 import os
 from concurrent.futures import ProcessPoolExecutor
+import sys
+
 from qiskit.exceptions import QiskitError
 from qiskit.utils.multiprocessing import local_hardware_info
 from qiskit.tools.events.pubsub import Publisher
@@ -57,6 +59,22 @@
 
 CONFIG = user_config.get_config()
 
+if os.getenv('QISKIT_PARALLEL', None) is not None:
+    PARALLEL_DEFAULT = os.getenv('QISKIT_PARALLEL', None).lower() == 'true'
+else:
+    # Default False on Windows
+    if sys.platform == 'win32':
+        PARALLEL_DEFAULT = False
+    # On macOS default false on Python >=3.8
+    elif sys.platform == 'darwin':
+        if sys.version_info[0] == 3 and sys.version_info[1] >= 8:
+            PARALLEL_DEFAULT = False
+        else:
+            PARALLEL_DEFAULT = True
+    # On linux (and other OSes) default to True
+    else:
+        PARALLEL_DEFAULT = True
+
 # Set parallel flag
 if os.getenv('QISKIT_IN_PARALLEL') is None:
     os.environ['QISKIT_IN_PARALLEL'] = 'FALSE'
@@ -64,7 +82,7 @@
 if os.getenv("QISKIT_NUM_PROCS") is not None:
     CPU_COUNT = int(os.getenv('QISKIT_NUM_PROCS'))
 else:
-    CPU_COUNT = CONFIG.get('num_processes') or local_hardware_info()['cpus']
+    CPU_COUNT = CONFIG.get('num_process', local_hardware_info()['cpus'])
 
 
 def _task_wrapper(param):
@@ -118,7 +136,7 @@ def _callback(_):
 
     # Run in parallel if not Win and not in parallel already
     if num_processes > 1 and os.getenv('QISKIT_IN_PARALLEL') == 'FALSE' \
-            and CONFIG.get('parallel_enabled', user_config.PARALLEL_DEFAULT):
+            and CONFIG.get('parallel_enabled', PARALLEL_DEFAULT):
         os.environ['QISKIT_IN_PARALLEL'] = 'TRUE'
         try:
             results = []
diff --git a/qiskit/user_config.py b/qiskit/user_config.py
--- a/qiskit/user_config.py
+++ b/qiskit/user_config.py
@@ -14,7 +14,6 @@
 
 import configparser
 import os
-import sys
 from warnings import warn
 
 from qiskit import exceptions
@@ -22,14 +21,6 @@
 DEFAULT_FILENAME = os.path.join(os.path.expanduser("~"),
                                 '.qiskit', 'settings.conf')
 
-if os.getenv('QISKIT_PARALLEL', None) is not None:
-    PARALLEL_DEFAULT = os.getenv('QISKIT_PARALLEL', None).lower() == 'true'
-else:
-    if sys.platform in {'darwin', 'win32'}:
-        PARALLEL_DEFAULT = False
-    else:
-        PARALLEL_DEFAULT = True
-
 
 class UserConfig:
     """Class representing a user config file
@@ -132,8 +123,9 @@ def read_config_file(self):
 
             # Parse parallel
             parallel_enabled = self.config_parser.getboolean(
-                'default', 'parallel', fallback=PARALLEL_DEFAULT)
-            self.settings['parallel_enabled'] = parallel_enabled
+                'default', 'parallel', fallback=None)
+            if parallel_enabled is not None:
+                self.settings['parallel_enabled'] = parallel_enabled
 
             # Parse num_processes
             num_processes = self.config_parser.getint(
@@ -158,7 +150,7 @@ def get_config():
     """
     filename = os.getenv('QISKIT_SETTINGS', DEFAULT_FILENAME)
     if not os.path.isfile(filename):
-        return {'parallel_enabled': PARALLEL_DEFAULT}
+        return {}
     user_config = UserConfig(filename)
     user_config.read_config_file()
     return user_config.settings


Test patch:
diff --git a/test/python/test_user_config.py b/test/python/test_user_config.py
--- a/test/python/test_user_config.py
+++ b/test/python/test_user_config.py
@@ -68,8 +68,7 @@ def test_circuit_drawer_valid(self):
             file.flush()
             config = user_config.UserConfig(self.file_path)
             config.read_config_file()
-            self.assertEqual({'circuit_drawer': 'latex',
-                              'parallel_enabled': user_config.PARALLEL_DEFAULT},
+            self.assertEqual({'circuit_drawer': 'latex'},
                              config.settings)
 
     def test_optimization_level_valid(self):
@@ -84,8 +83,7 @@ def test_optimization_level_valid(self):
             config = user_config.UserConfig(self.file_path)
             config.read_config_file()
             self.assertEqual(
-                {'transpile_optimization_level': 1,
-                 'parallel_enabled': user_config.PARALLEL_DEFAULT},
+                {'transpile_optimization_level': 1},
                 config.settings)
 
     def test_invalid_num_processes(self):
@@ -113,8 +111,7 @@ def test_valid_num_processes(self):
             config = user_config.UserConfig(self.file_path)
             config.read_config_file()
             self.assertEqual(
-                {'parallel_enabled': user_config.PARALLEL_DEFAULT,
-                 'num_processes': 31},
+                {'num_processes': 31},
                 config.settings)
 
     def test_valid_parallel(self):