Skip to content

ENH: Increase np.atleast_2d and np.atleast_3d performance - #30880

Open
benediktjohannes wants to merge 6 commits into
numpy:mainfrom
benediktjohannes:patch-1
Open

benediktjohannes wants to merge 6 commits into
numpy:mainfrom
benediktjohannes:patch-1

Conversation

@benediktjohannes

@benediktjohannes benediktjohannes commented Feb 25, 2026 •

Copy link
Copy Markdown

This pull request features #26130 by @eendebakpt (credits to him for the great work 👍 ) and should improve the performance of np.atleast_2d and np.atleast_3d.

I've not tested the changes, but it seems quite obvious to me from looking at the code that these should be (at least in most cases) positive (and in general more positive than negative, but I'm not one hundred percent sure, so please correct me if I'm mistaken, thanks).

Credits to: @eendebakpt
Contributed by Benedikt Johannes

Credits to: @eendebakpt
Contributed by Benedikt Johannes
@ngoldbaum

This comment was marked as outdated.

@benediktjohannes

This comment was marked as outdated.

@benediktjohannes

This comment was marked as outdated.

@ngoldbaum

This comment was marked as outdated.

@benediktjohannes

This comment was marked as outdated.

@benediktjohannes

This comment was marked as outdated.

@ngoldbaum

This comment was marked as outdated.

@benediktjohannes benediktjohannes changed the title ENH: Increase np.atleast_2d and np.atleast_2d performance ENH: Increase np.atleast_2d and np.atleast_3d performance Feb 25, 2026
@benediktjohannes

Copy link
Copy Markdown
Author

I've got the results for the benchmark! 👍

Script: numpytest.py

import pyperf

runner = pyperf.Runner()

setup="""
import numpy as np
from numpy.polynomial import Polynomial

x = np.array([2, 3, 4])
y = np.array([2, 3])

p = Polynomial([1, 2, 3.])
"""   
runner.timeit(name=f"atleast_2d(1)", stmt=f"np.atleast_2d(1)", setup=setup)
runner.timeit(name=f"atleast_2d(x)", stmt=f"np.atleast_2d(x)", setup=setup)
runner.timeit(name=f"atleast_2d(1, x )", stmt=f"np.atleast_2d(1, x )", setup=setup)

runner.timeit(name=f"atleast_3d(1)", stmt=f"np.atleast_3d(1)", setup=setup)
runner.timeit(name=f"atleast_3d(x)", stmt=f"np.atleast_3d(x)", setup=setup)
runner.timeit(name=f"atleast_3d(1, x )", stmt=f"np.atleast_3d(1, x )", setup=setup)

runner.timeit(name=f"polyadd", stmt=f"np.polyadd(x, y)", setup=setup)

(Credits to @eendebakpt for the great script👍)

Results:

Old (numpy original):

atleast_2d(1): Mean +- std dev: 728 ns +- 21 ns
atleast_2d(x): Mean +- std dev: 575 ns +- 25 ns
atleast_2d(1, x ): Mean +- std dev: 1.16 us +- 0.07 us

atleast_3d(1): Mean +- std dev: 761 ns +- 42 ns
atleast_3d(x): Mean +- std dev: 635 ns +- 63 ns
atleast_3d(1, x ): Mean +- std dev: 1.17 us +- 0.04 us

polyadd: Mean +- std dev: 2.88 us +- 0.13 us

New (numpy by Benedikt Johannes):

atleast_2d(1): Mean +- std dev: 662 ns +- 43 ns
atleast_2d(x): Mean +- std dev: 488 ns +- 22 ns
atleast_2d(1, x ): Mean +- std dev: 1.12 us +- 0.05 us

atleast_3d(1): Mean +- std dev: 671 ns +- 22 ns
atleast_3d(x): Mean +- std dev: 512 ns +- 24 ns
atleast_3d(1, x ): Mean +- std dev: 1.15 us +- 0.06 us

polyadd: Mean +- std dev: 2.80 us +- 0.08 us

@ngoldbaum ngoldbaum added this to the 2.5.0 Release milestone Feb 26, 2026
@benediktjohannes

Copy link
Copy Markdown
Author

Test failure is unrealted (can't install build environment)

@benediktjohannes

Copy link
Copy Markdown
Author

Should I write release notes 2.5 for this change, @ngoldbaum? Or is this done by the team? I only know this from CPython.

@benediktjohannes

Copy link
Copy Markdown
Author

Because it says:

Beyond changes to a functions docstring and possible description in the general documentation, if your change introduces any user-facing modifications they may need to be mentioned in the release notes. To add your change to the release notes, you need to create a short file with a summary and place it in doc/release/upcoming_changes. The file doc/release/upcoming_changes/README.rst details the format and filename conventions.

And the change is kind of user facing in my opinion because the performance is changed and users might notice this (and the latest news document also includes performance improvements).

@ngoldbaum

Copy link
Copy Markdown
Member

minor performance improvements like this don't need a release note

@benediktjohannes

Copy link
Copy Markdown
Author

All checks pass, do I need to use any command like /merge?

@benediktjohannes

Copy link
Copy Markdown
Author

So without the unrelated one

@ngoldbaum

Copy link
Copy Markdown
Member

Please be patient, I added the milestone so this won't get lost before the next major release this summer.

@benediktjohannes

benediktjohannes commented Feb 26, 2026 •

Copy link
Copy Markdown
Author

Thank you!

@benediktjohannes

Copy link
Copy Markdown
Author

But one question: Is everything finished that I got to do? So can I just wait for the summer without doing anything for this?

@ngoldbaum

ngoldbaum commented Feb 26, 2026 •

Copy link
Copy Markdown
Member

Maybe sooner, this PR has only been open for a day or so. The project doesn't move terribly quickly and people do code review as their time and interest permits.

I was also planning to look closer at this at some point. Usually we like to leave PRs that make performance improvements and add new features open for at least a few days.

@ngoldbaum

Copy link
Copy Markdown
Member

Also, IMO this PR shouldn't be backported, so in practice very few people will be able to use this until NumPy 2.5 comes out this summer.

@benediktjohannes

Copy link
Copy Markdown
Author

Ah, okay, thank you very much for the Information! Don’t feel stressed about it!

@charris charris removed this from the 2.5.0 Release milestone May 24, 2026
@charris charris added this to the 2.6.0 Release milestone May 24, 2026

@eendebakpt eendebakpt left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me.

Comment thread numpy/_core/shape_base.py
if result.ndim == 0:
result = result.reshape(1, 1)
elif result.ndim == 1:
result = result[_nx.newaxis, :]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be np.reshape(-1, 1) i think. It is quite a bit faster.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the idea! Does this apply to 2d and 3d as well?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure, but you could try

@ngoldbaum

Copy link
Copy Markdown
Member

It's been seven months so it might make sense to make a new PR if the OP has gone on to other projects.

@benediktjohannes

Copy link
Copy Markdown
Author

@ngoldbaum @eendebakpt I‘m still here 🙂👍 I always wanted to get this done and merged, it’s just that I didn’t Post anything here anymore because You Said I should not be so pushy, so no Problem, we can add this Suggestion by @eendebakpt, thank you by the way for the good idea

@benediktjohannes

Copy link
Copy Markdown
Author

@eendebakpt thanks for the Like, but I think you didn’t See the comment above concerning my question to your Suggestion

@benediktjohannes

Copy link
Copy Markdown
Author

@eendebakpt which axis should be -1 in 3d then? I‘m sorry, Im Not quite sure about your Suggestion, so Could you maybe do This Addition in a seperate PR then and get this merged at First because its a Bit offtopic or could you maybe explain a Bit why this change is correct in 2d and in what Form it could be correct for 3d? Thanks!

@eendebakpt

Copy link
Copy Markdown
Contributor

@eendebakpt which axis should be -1 in 3d then? I‘m sorry, Im Not quite sure about your Suggestion, so Could you maybe do This Addition in a seperate PR then and get this merged at First because its a Bit offtopic or could you maybe explain a Bit why this change is correct in 2d and in what Form it could be correct for 3d? Thanks!

Lets keep the changes out for this PR (I did not check the 3d case, maybe that is not so easy as the 2d case)

@benediktjohannes

Copy link
Copy Markdown
Author

Perfect, thanks, then ready for merge

@benediktjohannes

Copy link
Copy Markdown
Author

@ngoldbaum this PR is Open since 7 months, do you think that it’s maybe possible to get this merged soon, please?

@benediktjohannes

Copy link
Copy Markdown
Author

And ready for Review

@ngoldbaum

Copy link
Copy Markdown
Member

I'm on vacation this week.

If you'd like to get maintainer attention, I invite you to come to one of our weekly video calls
or to send a message to the mailing list.

@benediktjohannes

Copy link
Copy Markdown
Author

Oh, sorry @ngoldbaum, didnt want to interrupt you! Thanks for the Information and have a Nice vacation!

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants