Skip to content

MAINT: Const qualify UFunc inner loops #15355

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 5 commits into from
Jan 22, 2020
Merged

MAINT: Const qualify UFunc inner loops #15355

merged 5 commits into from
Jan 22, 2020

Conversation

Kai-Striega
Copy link
Member

This PR const qualifies the dimension and strides arguments of PyUFuncGenericFunction. Const qualified arguments make it simpler to reason about the behaviour of these ufuncs and prevents accidental mutation. As the const is now required this PR also const qualifies calls to PyUFuncGenericFunction inside the NumPy source code.

This closes #15252

This PR const qualifies the ``dimension`` and ``strides`` arguments of
``PyUFuncGenericFunction``. Const qualified arguments make it simpler
to reason about the behaviour of these ufuncs and prevents accidental
mutatation.

As the const is now required this PR also const qualifies calls to
``PyUFuncGenericFunction`` inside the NumPy source code.
@@ -841,7 +841,7 @@ cdef inline char* _util_dtypestring(dtype descr, char* f, char* end, int* offset

cdef extern from "numpy/ufuncobject.h":

ctypedef void (*PyUFuncGenericFunction) (char **, npy_intp *, npy_intp *, void *)
ctypedef void (*PyUFuncGenericFunction) (char **, const npy_intp *, const npy_intp *, void *)
Copy link
Member Author

Choose a reason for hiding this comment

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

Here and below I'm using west const ((char **, const npy_intp *, const npy_intp *, void *)). This is different to much of the existing east const code as cython doesn't seem to compile east const expressions. Should this be added as a note/fixme? And should this be added as a Cython issue?

Copy link
Member

@eric-wieser eric-wieser Jan 20, 2020

Choose a reason for hiding this comment

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

As far as I can tell, we do not include any const in our cython code, so ok to omit this for now.

I would definitely be in favor of filing a feature request against cython to support east const :) (xref https://stackoverflow.com/questions/56947927/102441)

Copy link
Member Author

Choose a reason for hiding this comment

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

I would definitely be in favor of filing a feature request against cython to support east const :)

It looks like there is an open issue already see cython-1475.

As far as I can tell, we do not include any const in our cython code, so ok to omit this for now.

Ok. I've removed the const qualifiers for now. It looks like Cython added const support in v0.18 with the latest release being 0.28 - I don't know the minimum NumPy requirement.

Copy link
Member

@eric-wieser eric-wieser left a comment

Choose a reason for hiding this comment

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

Generally looks good. Please push any fixups as separate commits, to make subsequent review easier - we can squash at merge time.

@eric-wieser
Copy link
Member

eric-wieser commented Jan 20, 2020

Can you remove this now-incorrect comment and unnecessary cast?

/* gh-15252: The signature of the inner loop considers `countptr`
* mutable. Inner loops aren't actually allowed to modify this
* though, so it's fine to cast it.
*/
innerloop(dataptrs_copy, (npy_intp *)countptr,
strides_copy, innerloopdata);

@eric-wieser eric-wieser added 03 - Maintenance 56 - Needs Release Note. Needs an entry in doc/release/upcoming_changes labels Jan 20, 2020
Previously the `innerlooop` (a  PyUFuncGenericFunction) allowed
pointers to non-const qualified dimensions. PyUFuncGenericFunction now
only accepts const qualified dimensions making the cast, and explaining
comment, obselete.
@charris
Copy link
Member

charris commented Jan 21, 2020

I don't know the minimum NumPy requirement.

Cython 0.28.14 for Python 3.8 support.

@eric-wieser
Copy link
Member

Let's leave cython const correctness to a follow-up

@seberg seberg removed the 56 - Needs Release Note. Needs an entry in doc/release/upcoming_changes label Jan 21, 2020
@seberg
Copy link
Member

seberg commented Jan 22, 2020

Thanks @Kai-Striega and Eric. The cython issue is addressed, the release notes seem fine to me and the changes also look good, so putting this in.

@seberg seberg merged commit e94cec8 into numpy:master Jan 22, 2020
@Kai-Striega Kai-Striega deleted the make-ufunc-loop-signature-const-correct branch January 22, 2020 04:55
ArmageddonKnight pushed a commit to UofT-EcoSystem/tensorflow that referenced this pull request Jun 22, 2020
Since `numpy==1.19.0` contains at least one breaking ABI change (numpy/numpy#15355), we need to either upper bound the dependency's range or fix our code to support both ABIs. Since the second requires more changes, we prefer the first one for now.

PiperOrigin-RevId: 317699730
Change-Id: Ia62a779f9ec42d63d3fac1b69cd75e6084358d2f
adk9 added a commit to adk9/docs that referenced this pull request Jun 23, 2020
Fix numpy to pre-1.19.0 because of breaking ABI change in numpy 1.19.0 (numpy/numpy#15355)

See tensorflow/tensorflow#40688.
copybara-service bot pushed a commit to tensorflow/tensorflow that referenced this pull request Oct 16, 2020
Also cleans up `setup.py`, upgrading dependencies, adding comments.

Fixes #41902 by changing constraint on `numpy` dependency to require `numpy` above the C++ API breaking change (numpy/numpy#15355, 79518fa)

Fixes duplicate #43679 (duplicate of #41902)

Tested:
```console
(venv) mihaimaruseac@ankh:/tmp/tf/cp/tf$ bazel build //tensorflow/tools/pip_package:build_pip_package
...
(venv) mihaimaruseac@ankh:/tmp/tf/cp/tf$ ./bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg
...
(venv) mihaimaruseac@ankh:/tmp/tf/cp/tf$ pip install --upgrade pip
...
(venv) mihaimaruseac@ankh:/tmp/tf/cp/tf$ pip install --upgrade pip
Collecting pip
  Using cached pip-20.2.3-py2.py3-none-any.whl (1.5 MB)
Installing collected packages: pip
  Attempting uninstall: pip
    Found existing installation: pip 20.1.1
    Uninstalling pip-20.1.1:
      Successfully uninstalled pip-20.1.1
Successfully installed pip-20.2.3
(venv) mihaimaruseac@ankh:/tmp/tf/cp/tf$ pip --version
pip 20.2.3 from /tmp/tf/venv/lib/python3.8/site-packages/pip (python 3.8)
(venv) mihaimaruseac@ankh:/tmp/tf/cp/tf$ pip list
Package             Version
------------------- -------
Keras-Preprocessing 1.1.2
numpy               1.19.2
pip                 20.2.3
pkg-resources       0.0.0
setuptools          44.0.0
six                 1.15.0
wheel               0.35.1
(venv) mihaimaruseac@ankh:/tmp/tf/cp/tf$ python -m pip install --upgrade --use-feature=2020-resolver /tmp/tensorflow_pkg/tensorflow-2.4.0-cp38-cp38-linux_x86_64.whl
...
Successfully installed absl-py-0.10.0 astunparse-1.6.3 cachetools-4.1.1 certifi-2020.6.20 chardet-3.0.4 flatbuffers-1.12 gast-0.4.0 google-auth-1.22.1 google-auth-oauthlib-0.4.1 google-pasta-0.2.0 grpcio-1.32.0 h5py-2.10.0 idna-2.10 markdown-3.3.1 oauthlib-3.1.0 opt-einsum-3.3.0 protobuf-3.13.0 pyasn1-0.4.8 pyasn1-modules-0.2.8 requests-2.24.0 requests-oauthlib-1.3.0 rsa-4.6 tensorboard-2.3.0 tensorboard-plugin-wit-1.7.0 tensorflow-2.4.0 tensorflow-estimator-2.3.0 termcolor-1.1.0 typing-extensions-3.7.4.3 urllib3-1.25.10 werkzeug-1.0.1 wrapt-1.12.1
(venv) mihaimaruseac@ankh:/tmp/tf/cp/tf$ pip list
Package                Version
---------------------- ---------
absl-py                0.10.0
astunparse             1.6.3
cachetools             4.1.1
certifi                2020.6.20
chardet                3.0.4
flatbuffers            1.12
gast                   0.4.0
google-auth            1.22.1
google-auth-oauthlib   0.4.1
google-pasta           0.2.0
grpcio                 1.32.0
h5py                   2.10.0
idna                   2.10
Keras-Preprocessing    1.1.2
Markdown               3.3.1
numpy                  1.19.2
oauthlib               3.1.0
opt-einsum             3.3.0
pip                    20.2.3
pkg-resources          0.0.0
protobuf               3.13.0
pyasn1                 0.4.8
pyasn1-modules         0.2.8
requests               2.24.0
requests-oauthlib      1.3.0
rsa                    4.6
setuptools             44.0.0
six                    1.15.0
tensorboard            2.3.0
tensorboard-plugin-wit 1.7.0
tensorflow             2.4.0
tensorflow-estimator   2.3.0
termcolor              1.1.0
typing-extensions      3.7.4.3
urllib3                1.25.10
Werkzeug               1.0.1
wheel                  0.35.1
wrapt                  1.12.1
```
PiperOrigin-RevId: 337563781
Change-Id: I2aa111b21a43e333d6891c0392051d765aade964
@rgommers
Copy link
Member

rgommers commented Feb 12, 2021

I landed on this issue in a roundabout way. It looks like this inadvertently broke the ABI, which affected TensorFlow: tensorflow/tensorflow#41902 (among others).

Comment in current TensorFlow master branch:

    # NOTE: As numpy has releases that break semver guarantees and several other
    # deps depend on numpy without an upper bound, we must install numpy before
    # everything else.
    'numpy ~= 1.19.2',

The ~= means >= 1.19.2; < 1.20.

The ABI break is a done deal now, I'm just commenting on it because it looks like we weren't even aware of it - is that right @seberg, @eric-wieser?

The version pinning thing is something we really should address with some guidance. What TensorFlow does is too restrictive and is going to cause problems if more packages start doing that. But the habit of not specifying any upper bound is indeed a real problem, we should get packages to change that. I'll open a new issue for this.

@eric-wieser
Copy link
Member

I was completely unaware this was an ABI change. Does the tensorflow issue explain what the ABI issue is?

@rgommers
Copy link
Member

The one I linked to doesn't, but this may: tensorflow/tensorflow#40728 (comment)

@eric-wieser
Copy link
Member

My reading of this is that the reviewers were confused, and concluded ultimately that this was an API breakage (which we did know), not an ABI breakage.

@seberg
Copy link
Member

seberg commented Feb 12, 2021

I don't know, but it sounds like a lot of confusion about "link time", but there is no link time with regard to the NumPy API (since we use an API function table instead). If there was an actual ABI break, SciPy and a whole lot of things would have gone down in flames. (How did SciPy fix this? Ignore the compile warnings?)

We were aware that this might force users to ignore warning or const qualify there loops. I did not realize that you might have to do both to get a warning free compile on either version of NumPy. Or am I misunderstanding some C++ foo?

@rgommers
Copy link
Member

That discussion is indeed confusing. They seemed to agree on API break in the end. The trouble seems to be more that the impact was underestimated in gh-15252. You briefly said "force users to add const to their loop signatures", but the rest of the discussion was only about compiler warnings, not that this would break already released packages. The issue doesn't even say "this will be an API break".

@rgommers
Copy link
Member

Maybe it's only a warning in C, but a hard error in C++?

@seberg
Copy link
Member

seberg commented Feb 12, 2021

@rgommers do you have an example of an already released package breaking? I don't understand how symbol names can matter considering the way our API is exported!

@rgommers
Copy link
Member

Never mind, I think that was more incorrect info. tensorflow/tensorflow#41902 says that opting in to the new pip resolver, which got people a different numpy version, broke tensorflow. But reading the long linked Pip issue I don't think that's correct - it involved a source build breaking.

@seberg
Copy link
Member

seberg commented Feb 13, 2021

OK, so the problem is that we were only aware that this will cause compiler warnings in C (which seems acceptable and even good). But, it actually causes C++ to barf, because C++ is much stricter about const (making this a real API break).

As is, I guess a code fix in tensorflow should not have been awfully hard. In some comments it sounds like the reasoning for not doing this (as in a forced cast) was fearing an actual ABI/API break 😞; which simply won't happen.
(What will probably happen at some point is that we deprecate the PyUFunc_RegisterLoopForDescr function through Python warnings.)

In any case, even forcing tensorflow to modify code to compile successfully for both versions was obviously never intended here. It would have been nice to know about the problem at a time when reverting might still have been helpful, we could even have had a 1.19.1 within days of the problem being noticed (and since it was not an ABI break, probably very few would have been hit).

(I don't know C++ well enough to know if there might be some other actual "warning only" solution for C++.)

@eric-wieser
Copy link
Member

You briefly said "force users to add const to their loop signatures", but the rest of the discussion was only about compiler warnings, not that this would break already released packages.

Yes, I guess I forgot that already-released source (but not binary) packages are affected by API changes when users build them from source against the latest numpy.

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.

Make the ufunc loop signature const correct
5 participants