Repository navigation
perf(processors): allocation-free early-exit contains_nan guard in all nopython kernels - #220
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
contains_nan hard-codes Numba caching (cache=True) and therefore bypasses the existing DSPEED_CACHE/numba_defaults.cache control mechanism used elsewhere.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR introduces a Numba-jitted utils.contains_nan() helper to provide an allocation-free, early-exit NaN scan, and uses it to replace np.isnan(...).any() guards inside many nopython processor kernels to reduce per-call overhead.
Changes:
- Add
contains_nan()insrc/dspeed/utils.pyas an optimized NaN guard for nopython kernels. - Replace
np.isnan(w).any()waveform/array guards across many processors withcontains_nan(...). - Update processor imports to use the new helper.
File summaries
| File | Description |
|---|---|
| src/dspeed/utils.py | Adds contains_nan() Numba helper for allocation-free NaN checks. |
| src/dspeed/processors/windower.py | Use contains_nan for waveform NaN guard. |
| src/dspeed/processors/wf_correction.py | Use contains_nan for waveform/correction NaN guards. |
| src/dspeed/processors/wf_alignment.py | Use contains_nan for waveform NaN guard. |
| src/dspeed/processors/upsampler.py | Use contains_nan for waveform NaN guards. |
| src/dspeed/processors/trap_filters.py | Use contains_nan for waveform NaN guards in trap-family processors. |
| src/dspeed/processors/time_point_thresh.py | Use contains_nan for waveform/threshold NaN guards. |
| src/dspeed/processors/time_over_threshold.py | Use contains_nan for waveform NaN guard. |
| src/dspeed/processors/sort.py | Use contains_nan for waveform NaN guard. |
| src/dspeed/processors/soft_pileup_corr.py | Use contains_nan for waveform NaN guard. |
| src/dspeed/processors/saturation.py | Use contains_nan for waveform NaN guard. |
| src/dspeed/processors/recursive_filter.py | Use contains_nan for input coefficient/waveform NaN guards. |
| src/dspeed/processors/rc_cr2.py | Use contains_nan for waveform NaN guards and output validation. |
| src/dspeed/processors/pulse_injector.py | Use contains_nan for waveform NaN guards. |
| src/dspeed/processors/presum.py | Use contains_nan for waveform NaN guard. |
| src/dspeed/processors/poly_fit.py | Use contains_nan for waveform/parameter NaN guards. |
| src/dspeed/processors/pole_zero.py | Use contains_nan for waveform NaN guards and output validation. |
| src/dspeed/processors/pmt_pulse_injector.py | Use contains_nan for waveform NaN guards. |
| src/dspeed/processors/pad.py | Use contains_nan for waveform-slice NaN guard. |
| src/dspeed/processors/moving_windows.py | Use contains_nan for waveform NaN guards. |
| src/dspeed/processors/min_max.py | Use contains_nan for waveform NaN guards. |
| src/dspeed/processors/log_check.py | Use contains_nan for waveform NaN guard. |
| src/dspeed/processors/linear_slope_fit.py | Use contains_nan for waveform NaN guards. |
| src/dspeed/processors/inl_correction.py | Use contains_nan for INL array NaN guard. |
| src/dspeed/processors/histogram.py | Use contains_nan for waveform NaN guards. |
| src/dspeed/processors/histogram_stats.py | Use contains_nan for weights NaN guards. |
| src/dspeed/processors/get_wf_centroid.py | Use contains_nan for waveform NaN guard. |
| src/dspeed/processors/get_multi_local_extrema.py | Use contains_nan for waveform NaN guard. |
| src/dspeed/processors/fixed_time_pickoff.py | Use contains_nan for waveform NaN guard. |
| src/dspeed/processors/bl_subtract.py | Use contains_nan for waveform NaN guard. |
| src/dspeed/processors/arithmetic.py | Use contains_nan for waveform NaN guard. |
Review details
- Files reviewed: 31/31 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #220 +/- ##
==========================================
+ Coverage 66.74% 66.79% +0.04%
==========================================
Files 61 61
Lines 4066 4111 +45
==========================================
+ Hits 2714 2746 +32
- Misses 1352 1365 +13 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…ee contains_nan In nopython numba kernels np.isnan(w).any() heap-allocates a boolean array and always scans the full input. contains_nan() (new in utils) allocates nothing, vectorizes via a fixed-width inner loop, and exits at the first NaN. Applied to all nopython kernels (object-mode kernels keep numpy's C implementation). Outputs are bit-identical; ~2x on O(1)-work processors like fixed_time_pickoff, ~10% on scan-heavy ones. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…dd unit tests Addresses PR review: cache=True was hard-coded, bypassing the DSPEED_CACHE/DSPEED_BOUNDSCHECK mechanism used by all processors. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
e59946a to
9bf5ae1
Compare
Split out of #217 (per review request there) — middle of the stack. #219 has merged, so this is now a single commit on main: just the NaN-guard change. #217 is stacked on top of this.
Adds
utils.contains_nan()— an allocation-free, early-exit,@guvectorize-compatible NaN scan — and replaces thenp.isnan(w).any()guards in all nopython processor kernels with it.np.isnan(w).any()allocates a boolean array per call and always scans the full waveform;contains_nanallocates nothing, vectorizes, and exits at the first NaN. Measured on real L200 p18 waveform blocks: ~2× on processors whose real work is O(1) (pickoffs, min/max, etc.), ~1.1× on scan-heavy ones.Outputs are bit-identical — verified end-to-end by rebuilding a full 199-channel L200 raw file and comparing column-by-column against a stock-dspeed control run (report: https://claude.ai/code/artifact/6d1c437c-c519-4797-a510-993d6aa80e98).
🤖 Generated with Claude Code