Thanks to visit codestin.com
Credit goes to github.com

Skip to content

BUG: fix choose refcount leak #24188

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 31, 2023
Merged

Conversation

tylerjereddy
Copy link
Contributor

@tylerjereddy tylerjereddy added 00 - Bug Priority: low Valid, but not for immediate attention labels Jul 15, 2023
np.choose(np.zeros(10000, dtype=int), [a], out=a)
np.choose(np.zeros(10000, dtype=int), [a], out=a)
refc_end = sys.getrefcount(1)
assert refc_end - refc_start < 10
Copy link
Contributor Author

Choose a reason for hiding this comment

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

could probably go less than 10 here, though I think each incantation can at least do +1

Copy link
Member

Choose a reason for hiding this comment

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

In case you iterate: Randomly realized that this actually stopped doing anything useful on Python 3.12, because IIRC the literal 1 will be an immortal object (and thus it's reference count doesn't change reliably).

@seberg
Copy link
Member

seberg commented Jul 17, 2023

Thanks, we are trying a bit to avoid copyswap; unfortunately, I admit, it would be nice if the replacement was slightly shorter, e.g. see: gh-24176

Happy to do this first, though! Is this much slower for e.g. float64? (My guess is no, because memmov is also just a function call, this loop should be specialized for the typical small itemsizes without references (i.e. basic numeric types).

@@ -1074,12 +1077,11 @@ PyArray_Choose(PyArrayObject *ip, PyObject *op, PyArrayObject *out,
break;
}
}
memmove(ret_data, PyArray_MultiIter_DATA(multi, mi), elsize);
copyswap(ret_data, PyArray_MultiIter_DATA(multi, mi), swap, NULL);
Copy link
Member

Choose a reason for hiding this comment

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

I'd prefer not adding new copyswap usages if we can avoid it since it will cause seg faults with new dtypes. Happy to push a fixup here if you don't have time to look at the other PRs replacing copyswap with a single-element cast.

@tylerjereddy
Copy link
Contributor Author

This PR has been revised based on reviewer feedback to replace copyswap with the more verbose approach that tolerates new dtype stuff. It is substantially harder to read, but the CI, including the new regression test, seem to be passing.

Copy link
Member

@ngoldbaum ngoldbaum left a comment

Choose a reason for hiding this comment

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

Agreed this is a lot of boilerplate. It probably makes sense to have a dtype API slot that implements a single-element copy, then you wouldn’t need to deal with the flags or the strides (i think?). You’d still need two stages to get the copy function from the dtype and possibly set up the auxdata, but we could certainly simplify this sort of thing a bunch.

Anyway, thanks for rewriting this in a more forward-compatible way while we wait on polishing out the rough edges in the new dtype API.

@charris
Copy link
Member

charris commented Jul 24, 2023

@seberg Sounds like this can be improved, but is it good enough for now?

@seberg
Copy link
Member

seberg commented Jul 25, 2023

Just noticed it's also missing the xfree. I am happy with just moving it out of the inner-loop, but I do think that needs to be done. I can do that though if @tylerjereddy prefers.

@tylerjereddy
Copy link
Contributor Author

I did try hoisting it up last week but got segfaults, will check again today.

@tylerjereddy
Copy link
Contributor Author

Latest set of changes:

* hoist the special `out` handling code out of the inner
loop (to the degree the testsuite allowed me to)

* add a missing `NPY_cast_info_xfree`

* adjust the regression test such that it fails before/passes
after on both Python 3.11 and 3.12 beta 4, to deal with
PEP 683

@seberg seberg requested a review from ngoldbaum July 31, 2023 10:22
@seberg
Copy link
Member

seberg commented Jul 31, 2023

Thanks I added three commits to:

  1. Deal with errors during the copy (which are not possible for NumPy dtypes copies, but in principle are possible, e.g. for Nathan's strings).
  2. Use this always if there are references:
    • Avoids making out= special, which IMO shouldn't be different really
    • New dtypes don't support PyArray_INCREF() maybe they never will since the API really isn't particularly clean (and may be outright impossible for some dtypes or object arrays in alternative Python implementations).
  3. Remove stray iterator creation (and fix to use obj rather than out for alignment, which went from irrelevant bug to a crash due to my changes).
  4. Use memcpy rather than memmov. We check for no memory overlap, there really isn't a reason to not use it, so I think it is clearer for future iterations also (and probably faster).

Also thought it would be clearer to use dtype since it is defined anyway.

EDIT: Also rebased, due to the noblas enforcement changes hitting CI

tylerjereddy and others added 6 commits July 31, 2023 17:59
* Fixes numpy#22683

* use `copyswap` to avoid the reference count leaking
reported above when `np.choose` is used with `out`

* my impression from the ticket is that Sebastian doesn't
think `copyswap` is a perfect solution, but may suffice
short-term?
* remove copyswap, carefully controlling
the reference counting to pass the testsuite
* hoist the special `out` handling code out of the inner
loop (to the degree the testsuite allowed me to)

* add a missing `NPY_cast_info_xfree`

* adjust the regression test such that it fails before/passes
after on both Python 3.11 and 3.12 beta 4, to deal with
PEP 683
Also hoist the `dtype` definition up and use it.
Not sure this makes a difference, but we check for memory overlap
so `memmov` isn't necessary and if the compiler keeps the order
intact, we want the `memcpy` path to be the hot one.
@ngoldbaum
Copy link
Member

Looks good to me, going to merge. Thanks for pushing this through @tylerjereddy and getting it across the line @seberg!

@ngoldbaum ngoldbaum merged commit e1ad0c1 into numpy:main Jul 31, 2023
@tylerjereddy tylerjereddy deleted the treddy_issue_22683 branch July 31, 2023 19:18
@ngoldbaum ngoldbaum added the 09 - Backport-Candidate PRs tagged should be backported label Jul 31, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

BUG: Choose (and probably more item selection) leaks references with out=
4 participants