ezmsg.sigproc.util.threaded_filt#
Split scipy IIR filtering across threads along a non-sample axis.
Each channel of an IIR filter is an independent recurrence, so splitting the non-sample axes across threads changes nothing about the arithmetic any one channel sees – the result is bit-identical to the single-threaded call, not merely close. That is what makes this usable in a pipeline whose whole point is that offline output matches online output exactly.
The win is real but strictly large-chunk. scipy’s sosfilt/lfilter are
single-threaded C loops, so the speedup tracks chunk bytes – measured
end-to-end through FilterTransformer, order-4 SOS, three independent reps:
0.25 MiB 0.50x 0.82x 0.79x loss 0.50 MiB 0.99x 1.07x 1.18x break-even 1.00 MiB 1.65x 1.74x 2.25x win 2.00 MiB 2.28x 2.36x 2.43x win 8.00 MiB ~3.3x win
32.00 MiB ~3.4x win
The ratio depends on total bytes rather than on the channel/sample split: at a
fixed 1 MiB, 256x512, 512x256 and 1024x128 all land within 1.53-1.63x. Below
~0.5 MiB the dispatch cost dominates and threading is a loss – hence
DEFAULT_MIN_BYTES and the hard gate in should_thread(). Online
chunk sizes stay single-threaded.
The pool is module-level and created once, so a graph with many filter units
shares one bounded set of workers rather than spawning a pool per unit, and no
per-chunk thread creation ever happens. Note that this is explicit threading
we control; it is unrelated to the implicit threading inside BLAS or
scipy.fft, which is governed by environment variables and can nest badly
underneath a pool like this one. scipy’s IIR filters touch neither.
Module Attributes
Chunks smaller than this (in bytes) are filtered single-threaded. |
|
Upper bound on pool size. |
Functions
- filt_threaded(filt_func, coefs, data, axis_idx, zi, zi_axis_offset, min_bytes=1048576)[source]#
Apply
filt_funcin parallel over blocks of a non-sample axis.- Parameters:
filt_func (Callable) –
scipy.signal.sosfiltorscipy.signal.lfilter.coefs (tuple) – Positional coefficient args for
filt_func.data (NDArray) – Input array; the sample axis is
axis_idx.zi (NDArray) – Filter state.
zi_axis_offset (int) – How far
zi’s axes are shifted relative todata’s. 1 for SOS (leadingn_sectionsaxis), 0 for BA.min_bytes (int) – Threshold below which this falls back to one call.
axis_idx (int)
- Returns:
(y, zf), bit-identical to the equivalent single-threaded call.- Return type:
tuple[NDArray, NDArray]
- get_pool()[source]#
The shared worker pool, created on first use and closed at interpreter exit.
- Return type:
ThreadPoolExecutor