annotate CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/site-packages/threadpoolctl-3.5.0.dist-info/METADATA @ 69:33d812a61356

planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author jpayne
date Tue, 18 Mar 2025 17:55:14 -0400
parents
children
rev   line source
jpayne@69 1 Metadata-Version: 2.1
jpayne@69 2 Name: threadpoolctl
jpayne@69 3 Version: 3.5.0
jpayne@69 4 Summary: threadpoolctl
jpayne@69 5 Home-page: https://github.com/joblib/threadpoolctl
jpayne@69 6 License: BSD-3-Clause
jpayne@69 7 Author: Thomas Moreau
jpayne@69 8 Author-email: thomas.moreau.2010@gmail.com
jpayne@69 9 Requires-Python: >=3.8
jpayne@69 10 Description-Content-Type: text/markdown
jpayne@69 11 Classifier: Intended Audience :: Developers
jpayne@69 12 Classifier: License :: OSI Approved :: BSD License
jpayne@69 13 Classifier: Programming Language :: Python :: 3
jpayne@69 14 Classifier: Programming Language :: Python :: 3.8
jpayne@69 15 Classifier: Programming Language :: Python :: 3.9
jpayne@69 16 Classifier: Programming Language :: Python :: 3.10
jpayne@69 17 Classifier: Programming Language :: Python :: 3.11
jpayne@69 18 Classifier: Programming Language :: Python :: 3.12
jpayne@69 19 Classifier: Topic :: Software Development :: Libraries :: Python Modules
jpayne@69 20
jpayne@69 21 # Thread-pool Controls [![Build Status](https://dev.azure.com/joblib/threadpoolctl/_apis/build/status/joblib.threadpoolctl?branchName=master)](https://dev.azure.com/joblib/threadpoolctl/_build/latest?definitionId=1&branchName=master) [![codecov](https://codecov.io/gh/joblib/threadpoolctl/branch/master/graph/badge.svg)](https://codecov.io/gh/joblib/threadpoolctl)
jpayne@69 22
jpayne@69 23 Python helpers to limit the number of threads used in the
jpayne@69 24 threadpool-backed of common native libraries used for scientific
jpayne@69 25 computing and data science (e.g. BLAS and OpenMP).
jpayne@69 26
jpayne@69 27 Fine control of the underlying thread-pool size can be useful in
jpayne@69 28 workloads that involve nested parallelism so as to mitigate
jpayne@69 29 oversubscription issues.
jpayne@69 30
jpayne@69 31 ## Installation
jpayne@69 32
jpayne@69 33 - For users, install the last published version from PyPI:
jpayne@69 34
jpayne@69 35 ```bash
jpayne@69 36 pip install threadpoolctl
jpayne@69 37 ```
jpayne@69 38
jpayne@69 39 - For contributors, install from the source repository in developer
jpayne@69 40 mode:
jpayne@69 41
jpayne@69 42 ```bash
jpayne@69 43 pip install -r dev-requirements.txt
jpayne@69 44 flit install --symlink
jpayne@69 45 ```
jpayne@69 46
jpayne@69 47 then you run the tests with pytest:
jpayne@69 48
jpayne@69 49 ```bash
jpayne@69 50 pytest
jpayne@69 51 ```
jpayne@69 52
jpayne@69 53 ## Usage
jpayne@69 54
jpayne@69 55 ### Command Line Interface
jpayne@69 56
jpayne@69 57 Get a JSON description of thread-pools initialized when importing python
jpayne@69 58 packages such as numpy or scipy for instance:
jpayne@69 59
jpayne@69 60 ```
jpayne@69 61 python -m threadpoolctl -i numpy scipy.linalg
jpayne@69 62 [
jpayne@69 63 {
jpayne@69 64 "filepath": "/home/ogrisel/miniconda3/envs/tmp/lib/libmkl_rt.so",
jpayne@69 65 "prefix": "libmkl_rt",
jpayne@69 66 "user_api": "blas",
jpayne@69 67 "internal_api": "mkl",
jpayne@69 68 "version": "2019.0.4",
jpayne@69 69 "num_threads": 2,
jpayne@69 70 "threading_layer": "intel"
jpayne@69 71 },
jpayne@69 72 {
jpayne@69 73 "filepath": "/home/ogrisel/miniconda3/envs/tmp/lib/libiomp5.so",
jpayne@69 74 "prefix": "libiomp",
jpayne@69 75 "user_api": "openmp",
jpayne@69 76 "internal_api": "openmp",
jpayne@69 77 "version": null,
jpayne@69 78 "num_threads": 4
jpayne@69 79 }
jpayne@69 80 ]
jpayne@69 81 ```
jpayne@69 82
jpayne@69 83 The JSON information is written on STDOUT. If some of the packages are missing,
jpayne@69 84 a warning message is displayed on STDERR.
jpayne@69 85
jpayne@69 86 ### Python Runtime Programmatic Introspection
jpayne@69 87
jpayne@69 88 Introspect the current state of the threadpool-enabled runtime libraries
jpayne@69 89 that are loaded when importing Python packages:
jpayne@69 90
jpayne@69 91 ```python
jpayne@69 92 >>> from threadpoolctl import threadpool_info
jpayne@69 93 >>> from pprint import pprint
jpayne@69 94 >>> pprint(threadpool_info())
jpayne@69 95 []
jpayne@69 96
jpayne@69 97 >>> import numpy
jpayne@69 98 >>> pprint(threadpool_info())
jpayne@69 99 [{'filepath': '/home/ogrisel/miniconda3/envs/tmp/lib/libmkl_rt.so',
jpayne@69 100 'internal_api': 'mkl',
jpayne@69 101 'num_threads': 2,
jpayne@69 102 'prefix': 'libmkl_rt',
jpayne@69 103 'threading_layer': 'intel',
jpayne@69 104 'user_api': 'blas',
jpayne@69 105 'version': '2019.0.4'},
jpayne@69 106 {'filepath': '/home/ogrisel/miniconda3/envs/tmp/lib/libiomp5.so',
jpayne@69 107 'internal_api': 'openmp',
jpayne@69 108 'num_threads': 4,
jpayne@69 109 'prefix': 'libiomp',
jpayne@69 110 'user_api': 'openmp',
jpayne@69 111 'version': None}]
jpayne@69 112
jpayne@69 113 >>> import xgboost
jpayne@69 114 >>> pprint(threadpool_info())
jpayne@69 115 [{'filepath': '/home/ogrisel/miniconda3/envs/tmp/lib/libmkl_rt.so',
jpayne@69 116 'internal_api': 'mkl',
jpayne@69 117 'num_threads': 2,
jpayne@69 118 'prefix': 'libmkl_rt',
jpayne@69 119 'threading_layer': 'intel',
jpayne@69 120 'user_api': 'blas',
jpayne@69 121 'version': '2019.0.4'},
jpayne@69 122 {'filepath': '/home/ogrisel/miniconda3/envs/tmp/lib/libiomp5.so',
jpayne@69 123 'internal_api': 'openmp',
jpayne@69 124 'num_threads': 4,
jpayne@69 125 'prefix': 'libiomp',
jpayne@69 126 'user_api': 'openmp',
jpayne@69 127 'version': None},
jpayne@69 128 {'filepath': '/home/ogrisel/miniconda3/envs/tmp/lib/libgomp.so.1.0.0',
jpayne@69 129 'internal_api': 'openmp',
jpayne@69 130 'num_threads': 4,
jpayne@69 131 'prefix': 'libgomp',
jpayne@69 132 'user_api': 'openmp',
jpayne@69 133 'version': None}]
jpayne@69 134 ```
jpayne@69 135
jpayne@69 136 In the above example, `numpy` was installed from the default anaconda channel and comes
jpayne@69 137 with MKL and its Intel OpenMP (`libiomp5`) implementation while `xgboost` was installed
jpayne@69 138 from pypi.org and links against GNU OpenMP (`libgomp`) so both OpenMP runtimes are
jpayne@69 139 loaded in the same Python program.
jpayne@69 140
jpayne@69 141 The state of these libraries is also accessible through the object oriented API:
jpayne@69 142
jpayne@69 143 ```python
jpayne@69 144 >>> from threadpoolctl import ThreadpoolController, threadpool_info
jpayne@69 145 >>> from pprint import pprint
jpayne@69 146 >>> import numpy
jpayne@69 147 >>> controller = ThreadpoolController()
jpayne@69 148 >>> pprint(controller.info())
jpayne@69 149 [{'architecture': 'Haswell',
jpayne@69 150 'filepath': '/home/jeremie/miniconda/envs/dev/lib/libopenblasp-r0.3.17.so',
jpayne@69 151 'internal_api': 'openblas',
jpayne@69 152 'num_threads': 4,
jpayne@69 153 'prefix': 'libopenblas',
jpayne@69 154 'threading_layer': 'pthreads',
jpayne@69 155 'user_api': 'blas',
jpayne@69 156 'version': '0.3.17'}]
jpayne@69 157
jpayne@69 158 >>> controller.info() == threadpool_info()
jpayne@69 159 True
jpayne@69 160 ```
jpayne@69 161
jpayne@69 162 ### Setting the Maximum Size of Thread-Pools
jpayne@69 163
jpayne@69 164 Control the number of threads used by the underlying runtime libraries
jpayne@69 165 in specific sections of your Python program:
jpayne@69 166
jpayne@69 167 ```python
jpayne@69 168 >>> from threadpoolctl import threadpool_limits
jpayne@69 169 >>> import numpy as np
jpayne@69 170
jpayne@69 171 >>> with threadpool_limits(limits=1, user_api='blas'):
jpayne@69 172 ... # In this block, calls to blas implementation (like openblas or MKL)
jpayne@69 173 ... # will be limited to use only one thread. They can thus be used jointly
jpayne@69 174 ... # with thread-parallelism.
jpayne@69 175 ... a = np.random.randn(1000, 1000)
jpayne@69 176 ... a_squared = a @ a
jpayne@69 177 ```
jpayne@69 178
jpayne@69 179 The threadpools can also be controlled via the object oriented API, which is especially
jpayne@69 180 useful to avoid searching through all the loaded shared libraries each time. It will
jpayne@69 181 however not act on libraries loaded after the instantiation of the
jpayne@69 182 `ThreadpoolController`:
jpayne@69 183
jpayne@69 184 ```python
jpayne@69 185 >>> from threadpoolctl import ThreadpoolController
jpayne@69 186 >>> import numpy as np
jpayne@69 187 >>> controller = ThreadpoolController()
jpayne@69 188
jpayne@69 189 >>> with controller.limit(limits=1, user_api='blas'):
jpayne@69 190 ... a = np.random.randn(1000, 1000)
jpayne@69 191 ... a_squared = a @ a
jpayne@69 192 ```
jpayne@69 193
jpayne@69 194 ### Restricting the limits to the scope of a function
jpayne@69 195
jpayne@69 196 `threadpool_limits` and `ThreadpoolController` can also be used as decorators to set
jpayne@69 197 the maximum number of threads used by the supported libraries at a function level. The
jpayne@69 198 decorators are accessible through their `wrap` method:
jpayne@69 199
jpayne@69 200 ```python
jpayne@69 201 >>> from threadpoolctl import ThreadpoolController, threadpool_limits
jpayne@69 202 >>> import numpy as np
jpayne@69 203 >>> controller = ThreadpoolController()
jpayne@69 204
jpayne@69 205 >>> @controller.wrap(limits=1, user_api='blas')
jpayne@69 206 ... # or @threadpool_limits.wrap(limits=1, user_api='blas')
jpayne@69 207 ... def my_func():
jpayne@69 208 ... # Inside this function, calls to blas implementation (like openblas or MKL)
jpayne@69 209 ... # will be limited to use only one thread.
jpayne@69 210 ... a = np.random.randn(1000, 1000)
jpayne@69 211 ... a_squared = a @ a
jpayne@69 212 ...
jpayne@69 213 ```
jpayne@69 214
jpayne@69 215 ### Switching the FlexiBLAS backend
jpayne@69 216
jpayne@69 217 `FlexiBLAS` is a BLAS wrapper for which the BLAS backend can be switched at runtime.
jpayne@69 218 `threadpoolctl` exposes python bindings for this feature. Here's an example but note
jpayne@69 219 that this part of the API is experimental and subject to change without deprecation:
jpayne@69 220
jpayne@69 221 ```python
jpayne@69 222 >>> from threadpoolctl import ThreadpoolController
jpayne@69 223 >>> import numpy as np
jpayne@69 224 >>> controller = ThreadpoolController()
jpayne@69 225
jpayne@69 226 >>> controller.info()
jpayne@69 227 [{'user_api': 'blas',
jpayne@69 228 'internal_api': 'flexiblas',
jpayne@69 229 'num_threads': 1,
jpayne@69 230 'prefix': 'libflexiblas',
jpayne@69 231 'filepath': '/usr/local/lib/libflexiblas.so.3.3',
jpayne@69 232 'version': '3.3.1',
jpayne@69 233 'available_backends': ['NETLIB', 'OPENBLASPTHREAD', 'ATLAS'],
jpayne@69 234 'loaded_backends': ['NETLIB'],
jpayne@69 235 'current_backend': 'NETLIB'}]
jpayne@69 236
jpayne@69 237 # Retrieve the flexiblas controller
jpayne@69 238 >>> flexiblas_ct = controller.select(internal_api="flexiblas").lib_controllers[0]
jpayne@69 239
jpayne@69 240 # Switch the backend with one predefined at build time (listed in "available_backends")
jpayne@69 241 >>> flexiblas_ct.switch_backend("OPENBLASPTHREAD")
jpayne@69 242 >>> controller.info()
jpayne@69 243 [{'user_api': 'blas',
jpayne@69 244 'internal_api': 'flexiblas',
jpayne@69 245 'num_threads': 4,
jpayne@69 246 'prefix': 'libflexiblas',
jpayne@69 247 'filepath': '/usr/local/lib/libflexiblas.so.3.3',
jpayne@69 248 'version': '3.3.1',
jpayne@69 249 'available_backends': ['NETLIB', 'OPENBLASPTHREAD', 'ATLAS'],
jpayne@69 250 'loaded_backends': ['NETLIB', 'OPENBLASPTHREAD'],
jpayne@69 251 'current_backend': 'OPENBLASPTHREAD'},
jpayne@69 252 {'user_api': 'blas',
jpayne@69 253 'internal_api': 'openblas',
jpayne@69 254 'num_threads': 4,
jpayne@69 255 'prefix': 'libopenblas',
jpayne@69 256 'filepath': '/usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.8.so',
jpayne@69 257 'version': '0.3.8',
jpayne@69 258 'threading_layer': 'pthreads',
jpayne@69 259 'architecture': 'Haswell'}]
jpayne@69 260
jpayne@69 261 # It's also possible to directly give the path to a shared library
jpayne@69 262 >>> flexiblas_controller.switch_backend("/home/jeremie/miniforge/envs/flexiblas_threadpoolctl/lib/libmkl_rt.so")
jpayne@69 263 >>> controller.info()
jpayne@69 264 [{'user_api': 'blas',
jpayne@69 265 'internal_api': 'flexiblas',
jpayne@69 266 'num_threads': 2,
jpayne@69 267 'prefix': 'libflexiblas',
jpayne@69 268 'filepath': '/usr/local/lib/libflexiblas.so.3.3',
jpayne@69 269 'version': '3.3.1',
jpayne@69 270 'available_backends': ['NETLIB', 'OPENBLASPTHREAD', 'ATLAS'],
jpayne@69 271 'loaded_backends': ['NETLIB',
jpayne@69 272 'OPENBLASPTHREAD',
jpayne@69 273 '/home/jeremie/miniforge/envs/flexiblas_threadpoolctl/lib/libmkl_rt.so'],
jpayne@69 274 'current_backend': '/home/jeremie/miniforge/envs/flexiblas_threadpoolctl/lib/libmkl_rt.so'},
jpayne@69 275 {'user_api': 'openmp',
jpayne@69 276 'internal_api': 'openmp',
jpayne@69 277 'num_threads': 4,
jpayne@69 278 'prefix': 'libomp',
jpayne@69 279 'filepath': '/home/jeremie/miniforge/envs/flexiblas_threadpoolctl/lib/libomp.so',
jpayne@69 280 'version': None},
jpayne@69 281 {'user_api': 'blas',
jpayne@69 282 'internal_api': 'openblas',
jpayne@69 283 'num_threads': 4,
jpayne@69 284 'prefix': 'libopenblas',
jpayne@69 285 'filepath': '/usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.8.so',
jpayne@69 286 'version': '0.3.8',
jpayne@69 287 'threading_layer': 'pthreads',
jpayne@69 288 'architecture': 'Haswell'},
jpayne@69 289 {'user_api': 'blas',
jpayne@69 290 'internal_api': 'mkl',
jpayne@69 291 'num_threads': 2,
jpayne@69 292 'prefix': 'libmkl_rt',
jpayne@69 293 'filepath': '/home/jeremie/miniforge/envs/flexiblas_threadpoolctl/lib/libmkl_rt.so.2',
jpayne@69 294 'version': '2024.0-Product',
jpayne@69 295 'threading_layer': 'gnu'}]
jpayne@69 296 ```
jpayne@69 297
jpayne@69 298 You can observe that the previously linked OpenBLAS shared object stays loaded by
jpayne@69 299 the Python program indefinitely, but FlexiBLAS itself no longer delegates BLAS calls
jpayne@69 300 to OpenBLAS as indicated by the `current_backend` attribute.
jpayne@69 301 ### Writing a custom library controller
jpayne@69 302
jpayne@69 303 Currently, `threadpoolctl` has support for `OpenMP` and the main `BLAS` libraries.
jpayne@69 304 However it can also be used to control the threadpool of other native libraries,
jpayne@69 305 provided that they expose an API to get and set the limit on the number of threads.
jpayne@69 306 For that, one must implement a controller for this library and register it to
jpayne@69 307 `threadpoolctl`.
jpayne@69 308
jpayne@69 309 A custom controller must be a subclass of the `LibController` class and implement
jpayne@69 310 the attributes and methods described in the docstring of `LibController`. Then this
jpayne@69 311 new controller class must be registered using the `threadpoolctl.register` function.
jpayne@69 312 An complete example can be found [here](
jpayne@69 313 https://github.com/joblib/threadpoolctl/blob/master/tests/_pyMylib/__init__.py).
jpayne@69 314
jpayne@69 315 ### Sequential BLAS within OpenMP parallel region
jpayne@69 316
jpayne@69 317 When one wants to have sequential BLAS calls within an OpenMP parallel region, it's
jpayne@69 318 safer to set `limits="sequential_blas_under_openmp"` since setting `limits=1` and
jpayne@69 319 `user_api="blas"` might not lead to the expected behavior in some configurations
jpayne@69 320 (e.g. OpenBLAS with the OpenMP threading layer
jpayne@69 321 https://github.com/xianyi/OpenBLAS/issues/2985).
jpayne@69 322
jpayne@69 323 ### Known Limitations
jpayne@69 324
jpayne@69 325 - `threadpool_limits` can fail to limit the number of inner threads when nesting
jpayne@69 326 parallel loops managed by distinct OpenMP runtime implementations (for instance
jpayne@69 327 libgomp from GCC and libomp from clang/llvm or libiomp from ICC).
jpayne@69 328
jpayne@69 329 See the `test_openmp_nesting` function in [tests/test_threadpoolctl.py](
jpayne@69 330 https://github.com/joblib/threadpoolctl/blob/master/tests/test_threadpoolctl.py)
jpayne@69 331 for an example. More information can be found at:
jpayne@69 332 https://github.com/jeremiedbb/Nested_OpenMP
jpayne@69 333
jpayne@69 334 Note however that this problem does not happen when `threadpool_limits` is
jpayne@69 335 used to limit the number of threads used internally by BLAS calls that are
jpayne@69 336 themselves nested under OpenMP parallel loops. `threadpool_limits` works as
jpayne@69 337 expected, even if the inner BLAS implementation relies on a distinct OpenMP
jpayne@69 338 implementation.
jpayne@69 339
jpayne@69 340 - Using Intel OpenMP (ICC) and LLVM OpenMP (clang) in the same Python program
jpayne@69 341 under Linux is known to cause problems. See the following guide for more details
jpayne@69 342 and workarounds:
jpayne@69 343 https://github.com/joblib/threadpoolctl/blob/master/multiple_openmp.md
jpayne@69 344
jpayne@69 345 - Setting the maximum number of threads of the OpenMP and BLAS libraries has a global
jpayne@69 346 effect and impacts the whole Python process. There is no thread level isolation as
jpayne@69 347 these libraries do not offer thread-local APIs to configure the number of threads to
jpayne@69 348 use in nested parallel calls.
jpayne@69 349
jpayne@69 350
jpayne@69 351 ## Maintainers
jpayne@69 352
jpayne@69 353 To make a release:
jpayne@69 354
jpayne@69 355 - Bump the version number (`__version__`) in `threadpoolctl.py` and update the
jpayne@69 356 release date in `CHANGES.md`.
jpayne@69 357
jpayne@69 358 - Build the distribution archives:
jpayne@69 359
jpayne@69 360 ```bash
jpayne@69 361 pip install flit
jpayne@69 362 flit build
jpayne@69 363 ```
jpayne@69 364
jpayne@69 365 and check the contents of `dist/`.
jpayne@69 366
jpayne@69 367 - If everything is fine, make a commit for the release, tag it and push the
jpayne@69 368 tag to github:
jpayne@69 369
jpayne@69 370 ```bash
jpayne@69 371 git tag -a X.Y.Z
jpayne@69 372 git push git@github.com:joblib/threadpoolctl.git X.Y.Z
jpayne@69 373 ```
jpayne@69 374
jpayne@69 375 - Upload the wheels and source distribution to PyPI using flit. Since PyPI doesn't
jpayne@69 376 allow password authentication anymore, the username needs to be changed to the
jpayne@69 377 generic name `__token__`:
jpayne@69 378
jpayne@69 379 ```bash
jpayne@69 380 FLIT_USERNAME=__token__ flit publish
jpayne@69 381 ```
jpayne@69 382
jpayne@69 383 and a PyPI token has to be passed in place of the password.
jpayne@69 384
jpayne@69 385 - Create a PR for the release on the [conda-forge feedstock](https://github.com/conda-forge/threadpoolctl-feedstock) (or wait for the bot to make it).
jpayne@69 386
jpayne@69 387 - Publish the release on github.
jpayne@69 388
jpayne@69 389 ### Credits
jpayne@69 390
jpayne@69 391 The initial dynamic library introspection code was written by @anton-malakhov
jpayne@69 392 for the smp package available at https://github.com/IntelPython/smp .
jpayne@69 393
jpayne@69 394 threadpoolctl extends this for other operating systems. Contrary to smp,
jpayne@69 395 threadpoolctl does not attempt to limit the size of Python multiprocessing
jpayne@69 396 pools (threads or processes) or set operating system-level CPU affinity
jpayne@69 397 constraints: threadpoolctl only interacts with native libraries via their
jpayne@69 398 public runtime APIs.
jpayne@69 399