solprop.backends.numba_cpu.openmp#
The pyFFTW import, made so that its OpenMP threads sleep when they are idle.
pyFFTW’s Linux wheels run FFTW’s threads through their own copy of GNU OpenMP
(libgomp). That runtime reads OMP_WAIT_POLICY once, when it is loaded. With the
variable unset, a thread that finishes a parallel region spins for GNU’s default count
before it sleeps. The kernels’ Numba pool runs next on the same cores, so the spinning
FFTW threads hold cores that the kernels’ threads want. The kernels’ threads then hold
the cores that FFTW’s team needs for its next transform, and each transform waits for
the scheduler.
Measured on the four-mode RDW capillary (the suite’s rdw), on 8 cores of a
two-socket x86-64 server with the Numba pool at 8 on its TBB layer: with FFTW’s pool at
8 threads a planned transform took 3.5-5.9 ms in place of the 0.1 ms it takes at one
thread, and a step 129 ms in place of 25 ms. At 2 and 4 FFTW threads the step took
128 ms. With OMP_WAIT_POLICY=passive it took 25.4 ms at 8 FFTW threads. On the full
CtC benchmark window, whose transforms get four FFTW threads by the backend’s own size
rule, the passive policy took the window from 6.92 to 4.85 s. An Apple-silicon laptop,
whose pyFFTW wheel uses LLVM’s OpenMP, showed no such slowdown, and this module leaves
other platforms alone.
So on Linux the import sets OMP_WAIT_POLICY=passive for pyFFTW’s runtime and
restores the environment straight away. An OpenMP runtime that loads later reads the
user’s environment, not this setting: the native backend’s runtime, or Numba’s OpenMP
layer. It is not done when the user has set OMP_WAIT_POLICY or GOMP_SPINCOUNT,
since then that choice is theirs. It is not done either when pyfftw was imported
before this module, because its runtime has then already read the environment.
WAIT_POLICY says which of these happened, and the backend’s metadata records
it as fftw_wait_policy.
Module Attributes
The variables by which a user chooses how GNU OpenMP's idle threads wait. |
|
The variables that make GNU OpenMP bind its threads ( |
|
How pyFFTW's OpenMP threads wait in this process ( |
|
Whether pyFFTW's OpenMP threads are bound here ( |
Functions
|
Return whether a runtime about to load would bind its threads. |
|
Start Numba's threads inside this block so that they wait passively and unbound. |
|
Return how pyFFTW's OpenMP threads will wait, and so whether to set it. |
- solprop.backends.numba_cpu.openmp.BINDING = 'none'#
Whether pyFFTW’s OpenMP threads are bound here (
binding_policy()).
- solprop.backends.numba_cpu.openmp.BINDING_VARIABLES = ('OMP_PROC_BIND', 'OMP_PLACES')#
The variables that make GNU OpenMP bind its threads (
OMP_PLACESalone does).
- solprop.backends.numba_cpu.openmp.VARIABLES = ('OMP_WAIT_POLICY', 'GOMP_SPINCOUNT')#
The variables by which a user chooses how GNU OpenMP’s idle threads wait.
- solprop.backends.numba_cpu.openmp.WAIT_POLICY = 'passive'#
How pyFFTW’s OpenMP threads wait in this process (
wait_policy()).
- solprop.backends.numba_cpu.openmp.binding_policy(platform, environ, *, loaded)[source]#
Return whether a runtime about to load would bind its threads.
On Linux, GNU OpenMP binds its threads, the main thread among them, when
OMP_PROC_BINDis set to anything butfalseor whenOMP_PLACESis set. Fornumba_cpu’s runtimes that is ruinous: the comb-to-continuum window took 45-47 s with binding against 17 s without, on 64 Zen 5 cores, whatever Numba’s threading layer (SEP 28), because the bound runtimes stack their threads on the same few cores. Binding remains the user’s choice for the native backend’s runtime.- Parameters:
- platformstr
sys.platform.- environMapping
The process environment.
- loadedbool
Whether the runtime has already read the environment.
- Returns:
- str
"unbound"when the load should setOMP_PROC_BIND=false, which is on Linux only and only when binding is asked for;"inherited: ..."when the runtime is loaded already under binding;"none"when nothing asks for it.
Examples
>>> from solprop.backends.numba_cpu.openmp import binding_policy >>> binding_policy("linux", {"OMP_PROC_BIND": "close"}, loaded=False) 'unbound' >>> binding_policy("linux", {"OMP_PROC_BIND": "false"}, loaded=False) 'none'
- solprop.backends.numba_cpu.openmp.numba_launch(platform='linux')[source]#
Start Numba’s threads inside this block so that they wait passively and unbound.
Numba’s OpenMP threading layer loads the shared GNU OpenMP runtime when its threads first start. With every core in the pool, its workers spin between parallel loops on the cores FFTW’s threads need: the comb-to-continuum window took 17 s on 64 Zen 5 cores against 5.4 s with
OMP_WAIT_POLICY=passive(SEP 28). On Linux, when nothing has decided otherwise, the runtime is loaded passive and unbound; when it is already loaded (by the native backend’s build, say) under settings this cannot change, Numba’s ownworkqueuelayer, which uses no OpenMP, is chosen instead (5.5 s on the same window).- Parameters:
- platformstr, optional
sys.platform.
- Yields:
- str
What applies, for the run’s metadata:
"passive","workqueue","started"(Numba’s threads were running already),"environment"(the user chose a layer or a wait policy) or"runtime default".
- solprop.backends.numba_cpu.openmp.wait_policy(platform, environ, *, loaded)[source]#
Return how pyFFTW’s OpenMP threads will wait, and so whether to set it.
- Parameters:
- platformstr
sys.platform.- environMapping
The process environment.
- loadedbool
Whether
pyfftwis already imported, in which case its runtime has read the environment already.
- Returns:
- str
"passive"when this import should set it, which is on Linux only and only when nothing else decides it. Otherwise it describes what decides it:"inherited"when pyFFTW was imported first,"environment: NAME=value"when the user set one ofVARIABLES, or"runtime default"on other platforms.
Examples
>>> from solprop.backends.numba_cpu.openmp import wait_policy >>> wait_policy("linux", {}, loaded=False) 'passive' >>> wait_policy("linux", {"OMP_WAIT_POLICY": "active"}, loaded=False) 'environment: OMP_WAIT_POLICY=active'