Improve computation time and memory usage across all statistics#17
Merged
Conversation
- BatchCorr (single-matrix mode): derive variances from diag(cov) instead of maintaining two redundant BatchVar streams (2.2x faster) - BatchCov: single NaN scan and single centering when batch2 is batch1, divide the small (d, d) result instead of a batch-sized temporary (1.3x faster, -33% peak memory) - BatchVar: reuse the batch sum computed for the mean update, removing one O(n*d) einsum pass per update (1.25x faster) - BatchNanMax/Min: np.fmax/np.fmin reductions replace np.nanmax/np.nanmin (no internal batch copy, no warning suppression); this also fixes all-NaN slices in later batches poisoning the running extremum - BatchNanPeakToPeak: share one isfinite pass between the max and min trackers instead of four (2.7x faster) - BatchWeightedMean: sum the zero-stride broadcast weights view instead of materializing batch * 1 (1.8x faster) - BatchStat: skip the NaN scan entirely for non-float dtypes (1.9x faster on integer data) - Fix NaN filtering for axis != 0 (was IndexError or silently wrong); multi-axis reductions with NaNs now raise a clear NotImplementedError - Fix BatchNanMax/Min __call__ crash on integer inputs - Document the linear memory growth of BatchWeightedSum list mode
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Performance review of all batch statistics, targeting computation time and memory usage. Every change is validated by the existing test suite (59 passed) plus end-to-end correctness checks against the NumPy reference functions (
np.corrcoef,np.average,np.varwith NaNs, etc.). Along the way the review surfaced and fixed three correctness bugs.Benchmarks
Streaming 200,000 × 100 float64 in 20,000-row chunks (best of 5, NumPy 2.5.1):
BatchCorr(single matrix)BatchNanPeakToPeakBatchWeightedMeanBatchSum(integer data)BatchNanMaxBatchCov(single matrix)BatchVarBatchCovpeak memoryOptimizations
BatchCorr(single-matrix mode): the variances are mathematically the diagonal of the covariance, so the two internalBatchVarstreams were redundant work on every update. They are now skipped entirely and__call__usessqrt(diag(cov)). Also stops fancy-index-copying the batch when it contains no NaNs.BatchCov: whenbatch2 is batch1(the common single-argument path), the NaN scan and the mean-centering each ran twice on the same array — now once. The/ nnormalization moved off a batch-sized temporary onto the small(d1, d2)result.BatchVar:einsum("j,ij->j", p+u, v)factors into(p+u) * v.sum(0), and that column sum is already computed for the mean update in the same call — one full O(n·d) pass removed per update.BatchNanMax/BatchNanMin:np.fmax.reduce/np.fmin.reducereplacenp.nanmax/np.nanmin, which internally copy the whole batch and allocate a NaN mask. Also removes the all-NaN warning-suppression block, sincefmax/fminreturn NaN silently.BatchNanPeakToPeak: the max and min trackers each ran twoisfinitepasses per update (4 total); they now share a single pass.BatchWeightedMean: the sum of weights was computed by materializingbroadcast_to(weights, batch.shape) * 1— a full batch-sized allocation. It now sums the zero-stride broadcast view directly.BatchStat: the NaN scan is skipped for non-inexact dtypes (integers/booleans can't hold NaN), which also avoids silent integer overflow innp.add.reduce.Bug fixes found during the review
axis != 0was broken: the mask is computed along the reduction axis but was applied to axis 0 —IndexErroron non-square batches, silently wrong results (NaN output, inflatedn_samples) on square ones. Fixed withnp.compressalong the actual reduction axis; negative axes are normalized. Multi-axis reductions over data containing NaNs now raise a clearNotImplementedErrorpointing to theBatchNan*classes instead of crashing or returning garbage.BatchNanMax/BatchNanMin: an all-NaN column in a later batch propagated NaN throughnp.maximum/np.minimumand permanently corrupted the running extremum, e.g.[[1, 2]]then[[nan, 3]]returned[nan, 3]instead of[1, 3].np.fmax/np.fminignore NaN, fixing this by construction.BatchNanMax/BatchNanMincrashed on integer inputs at call time (cannot convert float NaN to integer): the empty-slice masking now only runs when empty slices exist, casting to float only when NaN placeholders are actually needed.Also documents the linear memory growth of
BatchWeightedSum's list mode (when axis 0 is not reduced) in its docstring.Not changed
The centered covariance update was deliberately kept over the allocation-free raw-Gram formulation (
X₁ᵀX₂ − n·m₁m₂), which would reintroduce catastrophic cancellation — numerical stability wins over the last temporary.🤖 Generated with Claude Code