Simplify 'sum_by_group' - #796
Open
dad616610 wants to merge 1 commit into
Open
Conversation
dad616610
force-pushed
the
perf_improve_sum_by_group
branch
from
April 9, 2026 15:40
4573b4e to
3a2139d
Compare
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.
This PR replaces the
_sum_by_groupfunction. The main goals are to improve readability and maintainability; performance was improved as wellReadability and maintenance
IMO, it became more clear what the code does (maybe I became more familiar with the code and my judgement is skewed). Also all the work is now being done by one function, not scattered around: this should ease maintenance, since there's less code now
I added tests as well. This better documents the function and makes future changes more confident.
Performance
How I measured
I collected all the
indicesandvaluespassed to_sum_by_group_npand made this my testing data.Testing data collection
Assuming we're on a commit prior current one (a83733b), so we still have the old
_sum_by_group_np.The code below would save
indicesandvaluesunder "data" directory. I'm getting ~28k small files: 14k for indices and 14k for values.Then I created a file under the project root to test different function implementations, let's call it "test_sum_by_group.py". It's content is under the spoiler below. I test the function on all inputs 10 times (this is one round), do 3 such rounds and take a minimum.
Performance test file
The function implementations are shown in the next collapsible sections.
Functions:
old
This is the previous
_sum_by_group_npnew_bincount
new_cumsum
Numba-powered version of
new_bincount(basically just adding a decorator)nmb
There is no support for
np.add.reduceatin numba and the regularnp.cumsumwith subtracting works even worse, so there's no numba-powered version ofnew_cumsum.To run the testing suite I used
uv run test_sum_by_group.pyResults
Here're the results I obtained on my machine (measurement unit is seconds):
new_bincountgives ~5x speedup; in terms of time-per-call (time / 10 (in one round I run a function 10 times) / 10_000 (roughly a number of inputs) we get 21 us vs. 4 us, which is pretty fast, if you ask me.nmbdoes run faster than thenew_bincountby 30%, but the absolute speedup is marginal: 4 us time-per-call vs. 3 us. We should also take the compilation time into account here. It takes ~1.2 sec to compile, meaning numba version wouldoutperform non-numba version only after
1.2 / (0.4 - 0.3) = 12runs of performance test suite!This numba over non-numba gain deemed miniscule to me, so I decided not to include the numba version at all.
new_cumsumis 2x slower thannew_bincountit has a serious advantage:new_bincountneeds to allocate an array ofindices.max() + 1size (128 MiB for size 2**24 of dtype np.int64). With arrays this big we're spending more time allocating, than running an algorithm.new_cumsumdoesn't suffer from this problem, but even with array of 2**24 sizenew_bincountwas still faster. Arrays of this size (more correctly, indices with such numbers) are unlikely to be used and we would lose lots of performance, so I chosenew_bincountovernew_cumsum.Performance of
uv run pytestThe testing suite (
uv run pytest) became a whopping 1s faster!I measured 10 runs with hyperfine. This result is without this PR:
And this one is with this commit:
API change
sum_by_groupa public function (no particular reason)The previous version returned both unique indices and grouped sums as one list:
[indices] + grouped_sums. I made the new function return a tuple of unique indices and grouped sums as separate objects:(indices, grouped_sums). All internal usages were updated.See the change from the caller side in these diffs:
This was done to ease numba-powering the functions, but IMO it also more clearly separates the output: we get
unique_indicesas separate entity andgrouped_sumsas another entity. Though, either returning method is equivalent performance-wiseIf any of the API changes are unnecessary, I'm happy to revert them