diff --git a/README.md b/README.md index 3f2b196f..73a6c1f6 100644 --- a/README.md +++ b/README.md @@ -427,10 +427,22 @@ An complete example can be found [here]( ### Sequential BLAS within OpenMP parallel region When one wants to have sequential BLAS calls within an OpenMP parallel region, it's -safer to set `limits="sequential_blas_under_openmp"` since setting `limits=1` and -`user_api="blas"` might not lead to the expected behavior in some configurations -(e.g. OpenBLAS with the OpenMP threading layer -https://github.com/xianyi/OpenBLAS/issues/2985). +necessary to set `limits="sequential_blas_under_openmp"` since setting `limits=1` and +`user_api="blas"` leads to unexpected behavior for OpenBLAS with the OpenMP threading +layer: OpenBLAS and the calling code share the same OpenMP runtime, so it also sets +the number of OpenMP threads of the current thread to 1. Inside the limit, any OpenMP +parallel region started from this thread without an explicit number of threads runs +on a single thread, and `omp_get_max_threads()` returns 1. + +```python +from threadpoolctl import ThreadpoolController + +controller = ThreadpoolController() + +with controller.limit(limits="sequential_blas_under_openmp"): + # Compiled code with an OpenMP parallel loop that calls BLAS. + my_openmp_kernel(X) +``` ### Known Limitations @@ -517,6 +529,10 @@ Current libraries where `threadpoolctl` explicitly makes this choice are: * MKL, for all threading backends. * OpenBLAS (v0.3.34 or later) when using the OpenMP backend, on Linux and macOS. + threadpoolctl then limits OpenBLAS with `omp_set_num_threads`, so the limit + also applies to the OpenMP parallel regions of the calling code. To get + sequential BLAS calls within an OpenMP parallel region, see + [Sequential BLAS within OpenMP parallel region](#sequential-blas-within-openmp-parallel-region). When using OpenMP, this is also the default on Linux and macOS. On Windows OpenMP has a per-thread worker pool but the limiting API affects all threads in diff --git a/threadpoolctl.py b/threadpoolctl.py index 5ff701ef..2eab17c3 100644 --- a/threadpoolctl.py +++ b/threadpoolctl.py @@ -1048,6 +1048,9 @@ def _get_params_for_sequential_blas_under_openmp(self): if self.select( internal_api="openblas", threading_layer="openmp" ).lib_controllers: + # Don't change anything: OpenBLAS is already sequential by default when it + # is called from an OpenMP parallel region, and limiting it would also + # limit the OpenMP parallel regions of the calling thread. return {"limits": None, "user_api": None} return {"limits": 1, "user_api": "blas"}