Skip to content

take slower when out is provided #28636

Open

Description

@ricardoV94

Describe the issue:

I've noticed this when trying to use the C-API PyArray_TakeFrom but it can be shown using python as well:

import numpy as np
print(np.__version__)
# 2.2.4

x = np.arange(85, dtype=float)
idxs = np.repeat(np.arange(85), 110)
out = np.empty_like(np.take(x, idxs, out=None))
%timeit np.take(x, idxs, out=None)
%timeit np.take(x, idxs, out=out)
# 6.76 μs ± 106 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
# 8.99 μs ± 30.8 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)

With those sizes, I would expect the overhead to be mostly from copying the values, not checking whether the provided output is valid.

Python and NumPy Versions:

numpy 2.2.4, python 3.12.8

Runtime Environment:

No response

Context for the issue:

No response

Activity

  1. changed the title [-]`numpy_take` slower when `out` is provided[/-] [+]`take` slower when `out` is provided[/+] on Apr 3, 2025
  2. ngoldbaum commented on Apr 3, 2025

    @ngoldbaum
    Member

    I see the same thing on my Mac:

    In [1]: import numpy as np
       ...: print(np.__version__)
       ...:
       ...: x = np.arange(85, dtype=float)
       ...: idxs = np.repeat(np.arange(85), 110)
       ...: out = np.empty_like(np.take(x, idxs, out=None))
       ...: %timeit np.take(x, idxs, out=None)
       ...: %timeit np.take(x, idxs, out=out)
    2.3.0.dev0+git20250403.dfbf9ed
    17.3 μs ± 459 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
    21.6 μs ± 39.4 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
    

    Let me see if I can get samply to give me a useful profile so we can understand where the difference is.

  3. ngoldbaum commented on Apr 3, 2025

    @ngoldbaum
    Member

    The difference is that using out leads to a code path that does a copy. I'm not sure offhand why a copy is necessary here.

    with out:

    Image

    without:

    Image

    EDIT: (also pretty neat that you can get Python frames in samply profiles!)

  4. seberg commented on Apr 3, 2025

    @seberg
    Member

    The reason is that take ensures that the output is unchanged when an out-of-bounds access happens.

    That is how it has always been, I don't disagree that this is likely not needed. Not sure about changing it, but I would be OK if a few people are in favor (with a release note).

    EDIT: I.e. mode="wrap" makes this go away.

  5. ricardoV94 commented on Apr 3, 2025

    @ricardoV94
    Author

    I would like to have it raise still. If anybody objects to changing the behavior could we instead have an additional raise+mutate mode instead?

  6. added
    triage reviewIssue/PR to be discussed at the next triage meeting
    on Apr 4, 2025
  7. seberg commented on Apr 4, 2025

    @seberg
    Member

    Let's bring it up at the triage meeting. I am carefully in favor of just changing it with a release note. It just feels like out= is usually for speed, and you can use out[...] = take(arr, idx).
    So, if you are interested in this, I think this is likely enough to happen that opening a PR for it is useful.

    (Adding a mode seems a lot of addition for nothing to me.)

  8. ricardoV94 commented on Apr 4, 2025

    @ricardoV94
    Author

    Totally agree

  9. removed
    triage reviewIssue/PR to be discussed at the next triage meeting
    on Aug 20, 2025
  10. mattip commented on Aug 20, 2025

    @mattip
    Member

    We discussed this at a triage meeting and would like to see a PR to implement the idea of avoiding a copy.

  11. Ijtihed commented on Jul 8, 2026

    @Ijtihed
    Contributor

    I can take a look at and implement this. gut feeling says it'll be easy and quite a performance boost. let's see!

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions