Skip to content

Check the NumPy version once instead of on every call - #130337

Open
b84330808 wants to merge 1 commit into
tensorflow:masterfrom
b84330808:numpy-compat-cache-version-check
Open

b84330808 wants to merge 1 commit into
tensorflow:masterfrom
b84330808:numpy-compat-cache-version-check

Conversation

@b84330808

Copy link
Copy Markdown
Contributor

numpy_compat.np_asarray, np_where and np_reshape build two np.lib.NumpyVersion objects (a regex parse of np.__version__ and of the bound) on every call. np_asarray runs for every value returned from tf.numpy_function / tf.compat.v1.py_func, and Dataset.from_generator calls it for every component of every element, so this check was the largest single cost in those per-element Python paths. The installed NumPy version can't change while the process runs, so this computes the flags once at import. tf.experimental.numpy.sign and linspace had the same per-call check and get a module-level flag too. Behaviour is unchanged.

Measured on Windows 11, i7-9700, Python 3.11.9, tf-nightly 2.23.0.dev20261112, NumPy 2.4.6, CPU only. Base and patched numpy_compat.py were swapped in alternately, 4 runs each, one process per run; median [min-max]:

workload (20,000 elements, batch(64)) before after
Dataset.from_generator yielding (float32[32], int64) with output_signature 6,623 [6,279-6,760] elem/s 9,149 [9,020-9,272] elem/s
Dataset.from_tensor_slices(...).map(lambda a: tf.numpy_function(f, [a], tf.float32)) 13,810 [13,554-14,081] elem/s 17,700 [16,723-18,095] elem/s

np_asarray(np.float32 array of 32, dtype=np.float32) alone goes from 7.0 to 0.54 us per call.

numpy_compat_test.py, script_ops_test.py, from_generator_test.py and tensor_util_test.py pass with the patched module, and tnp.sign/tnp.linspace give the same results as NumPy 2.4 on tf-nightly. py_func_test.py has the same result before and after: everything passes except testRenamedDeviceInTestClusterCorrectlyIdentifiedAsLocalhost, which fails on this Windows machine either way.

`np_asarray`, `np_where` and `np_reshape` parsed `np.__version__` with
`np.lib.NumpyVersion` on every call. `np_asarray` runs for every value
that `tf.numpy_function`/`tf.py_func` returns and for every component that
`tf.data.Dataset.from_generator` yields, so the version parsing showed up
as the largest cost in those per-element paths. The installed NumPy
version doesn't change at runtime, so compute the flags at import.

`tf.experimental.numpy.sign` and `linspace` did the same per call; they
get a module-level flag too.
@google-ml-butler google-ml-butler Bot added the size:S CL Change Size: Small label Nov 14, 2026
@google-ml-butler
google-ml-butler Bot requested a review from cantonios November 14, 2026 10:24
@google-ml-butler google-ml-butler Bot added the awaiting review Pull request awaiting review label Nov 14, 2026

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request optimizes NumPy version checks by caching the results in module-level constants instead of parsing the version string on every function call. The review feedback suggests avoiding code duplication by importing the centralized constants from numpy_compat into the other modules, and replacing the deprecated np.lib.NumpyVersion with packaging.version.Version to ensure future compatibility.

Comment on lines 18 to +23
import numpy as np

# The installed NumPy version does not change while the process runs, so
# check it once here instead of parsing `np.__version__` on every call.
_NP_2_0_OR_LATER = np.lib.NumpyVersion(np.__version__) >= '2.0.0.dev0'
_NP_2_1_OR_LATER = np.lib.NumpyVersion(np.__version__) >= '2.1.0.rc0'

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.

medium

np.lib.NumpyVersion is deprecated in NumPy 2.0 and is scheduled for removal in a future release. To avoid deprecation warnings and ensure future compatibility, use packaging.version.Version instead.

Suggested change
import numpy as np
# The installed NumPy version does not change while the process runs, so
# check it once here instead of parsing `np.__version__` on every call.
_NP_2_0_OR_LATER = np.lib.NumpyVersion(np.__version__) >= '2.0.0.dev0'
_NP_2_1_OR_LATER = np.lib.NumpyVersion(np.__version__) >= '2.1.0.rc0'
import numpy as np
from packaging.version import Version
# The installed NumPy version does not change while the process runs, so
# check it once here instead of parsing `np.__version__` on every call.
_NP_2_0_OR_LATER = Version(np.__version__) >= Version('2.0.0.dev0')
_NP_2_1_OR_LATER = Version(np.__version__) >= Version('2.1.0.rc0')

Comment on lines 43 to +47
from tensorflow.python.util import nest
from tensorflow.python.util import tf_export

# The installed NumPy version does not change while the process runs.
_NP_2_0_OR_LATER = np.lib.NumpyVersion(np.__version__) >= '2.0.0.dev0'

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.

medium

Instead of duplicating the NumPy version check and parsing logic across multiple files, import the centralized _NP_2_0_OR_LATER constant from tensorflow.python.util.numpy_compat.

Suggested change
from tensorflow.python.util import nest
from tensorflow.python.util import tf_export
# The installed NumPy version does not change while the process runs.
_NP_2_0_OR_LATER = np.lib.NumpyVersion(np.__version__) >= '2.0.0.dev0'
from tensorflow.python.util import nest
from tensorflow.python.util import tf_export
from tensorflow.python.util.numpy_compat import _NP_2_0_OR_LATER

Comment on lines 42 to +46
from tensorflow.python.ops.numpy_ops import np_utils
from tensorflow.python.util import tf_export

# The installed NumPy version does not change while the process runs.
_NP_2_0_OR_LATER = np.lib.NumpyVersion(np.__version__) >= '2.0.0.dev0'

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.

medium

Instead of duplicating the NumPy version check and parsing logic across multiple files, import the centralized _NP_2_0_OR_LATER constant from tensorflow.python.util.numpy_compat.

Suggested change
from tensorflow.python.ops.numpy_ops import np_utils
from tensorflow.python.util import tf_export
# The installed NumPy version does not change while the process runs.
_NP_2_0_OR_LATER = np.lib.NumpyVersion(np.__version__) >= '2.0.0.dev0'
from tensorflow.python.ops.numpy_ops import np_utils
from tensorflow.python.util import tf_export
from tensorflow.python.util.numpy_compat import _NP_2_0_OR_LATER

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

Labels

awaiting review Pull request awaiting review size:S CL Change Size: Small

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant