Skip to content

Use thread-local limiting APIs when possible, and fix deadlock - #228

Merged
ogrisel merged 27 commits into
joblib:masterfrom
itamarst:216-thread-local-setting-api
Sep 3, 2026
Merged

Use thread-local limiting APIs when possible, and fix deadlock#228
ogrisel merged 27 commits into
joblib:masterfrom
itamarst:216-thread-local-setting-api

Conversation

@itamarst

@itamarst itamarst commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Fixes #216
Fixes #229

I figured out a way to measure thread-creation that works on Linux, at least. Or that's the hope, anyway. (ltrace would be much more robust but I can't get it to work on Conda binaries).

@itamarst itamarst changed the title ENH Use thread-local limiting APIs when possible ENH Use thread-local limiting APIs when possible, and fix reentrancy bug Sep 1, 2026
@itamarst itamarst changed the title ENH Use thread-local limiting APIs when possible, and fix reentrancy bug ENH Use thread-local limiting APIs when possible, and fix deadlock Sep 1, 2026
@itamarst itamarst changed the title ENH Use thread-local limiting APIs when possible, and fix deadlock Use thread-local limiting APIs when possible, and fix deadlock Sep 1, 2026
@itamarst

itamarst commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

Not sure what's up with openmp suddenly failing to compile on macOS. Seems unrelated to this PR.

@itamarst
itamarst marked this pull request as ready for review September 1, 2026 15:42

@ogrisel ogrisel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks very much @itamarst for the PR. Overall, this LGTM but there are a few points to address:

(
{"user_api": "openmp"},
"current_thread",
# Windows OpenMP is process-wide:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I asked an LLM to know if this was a bug or not. Apparently, this is not a bug but a consequence of a being an implementation of an older version of the spec:

Suggested change
# Windows OpenMP is process-wide:
# Windows OpenMP is process-wide as it claims to implement
# the OpenMP 2.0 spec. This would be a violation of the
# OpenMP 3.0 spec which mandates per-task
# Internal Control Variables such as `nthreads-var`.
# https://learn.microsoft.com/en-us/cpp/build/reference/openmp-enable-openmp-2-0-support?view=msvc-180

For information, recent MSVC can be configured to build with the -openmp:llvm flag to leverage libomp instead: https://devblogs.microsoft.com/cppblog/improved-openmp-support-for-cpp-in-visual-studio/

I don't know if there is an easy way to snif libomp specific symbols on such MSVC generated binary files.

@itamarst itamarst Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I imagine there's no reason not to use omp_set_num_threads on Windows, it might just not be thread-local (but it won't be worse than openblas_set_num_threads). So as a first pass I will:

  • Change the code to run on Windows too.
  • Still leave the test disabled on Windows.
  • Open an issue to see if detection can be done.
  • Open an issue with OpenBLAS on Conda-Forge to use this flag, with the presumption I will verify that it doesn't already.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sounds like that VS flag is experimental for now, so maybe I won't suggest it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's still worth expanding the inline comment to explain that the current windows OpenMP runtime implement semantics from an older version of the OpenMP spec but that it is hopefully expected to change in the future, at which point we might want to update this test to make sure it passes on all platforms.

Comment thread tests/test_threadpoolctl.py
Comment thread threadpoolctl.py
Comment thread threadpoolctl.py
Comment thread threadpoolctl.py
@itamarst

itamarst commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

Not really sure what's going on with the new compiler failures...

@itamarst

itamarst commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

Dug into compiler failures a bit: conda-forge/python-feedstock#914 (comment)

@itamarst

itamarst commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

There's a new PR in conda-forge/python-feedstock to fix the compilation issue, so hopefully by tomorrow it should be working again, will just need to re-run.

@ogrisel

ogrisel commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

I retriggered a build and the compiler problems went away but pylatest_flexiblas has the test_setting_limit_on_thread_local_blas_api_is_actually_thread_local[thread_local_blas_libs1] that fails with:

        # _limit_blas runs BLAS operations in 2 Python threads, so by changing the
        # BLAS limit from 1 to 4 we expect an extra 2 * (4 - 1) == 6 threads.
        extra_threads = num_threads_created(4) - num_threads_created(1)
>       assert extra_threads == 6
E       assert 0 == 6

on top of the unrelated macOS OpenMP failures.

@ogrisel ogrisel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More feedback on the new iteration of this PR.

Comment thread tests/test_threadpoolctl.py Outdated
Comment thread CHANGES.md
Comment thread tests/test_threadpoolctl.py Outdated
@itamarst

itamarst commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

I retriggered a build and the compiler problems went away but pylatest_flexiblas has the test_setting_limit_on_thread_local_blas_api_is_actually_thread_local[thread_local_blas_libs1] that fails with:

        # _limit_blas runs BLAS operations in 2 Python threads, so by changing the
        # BLAS limit from 1 to 4 we expect an extra 2 * (4 - 1) == 6 threads.
        extra_threads = num_threads_created(4) - num_threads_created(1)
>       assert extra_threads == 6
E       assert 0 == 6

on top of the unrelated macOS OpenMP failures.

I've seen occasional results (in CI only) where the number of threads is unexpectedly when number of threads was increased. My current theory is that maybe OpenMP caps number of threads to number of cores, so I'll see if I can figure out hardware used in that run.

@itamarst

itamarst commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

Oh I wonder if that's just because it's flexiblas. So setting the number of threads is using the flexiblas API which doesn't use the _local() MKL variant. I'll open an issue for flexiblas to look at separately since I don't want that to block this PR.

…re there are multiple BLAS installed on the system.

@ogrisel ogrisel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@ogrisel
ogrisel merged commit cb49e09 into joblib:master Sep 3, 2026
20 of 22 checks passed
@itamarst
itamarst deleted the 216-thread-local-setting-api branch September 3, 2026 16:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Potential deadlock when loading shared libraries, specifically mkl with libiomp Use thread-local APIs when possible

2 participants