diff --git a/benchmarks/benchmarks/__init__.py b/benchmarks/benchmarks/__init__.py index e4193cf0582f..7b9f1d3e688d 100644 --- a/benchmarks/benchmarks/__init__.py +++ b/benchmarks/benchmarks/__init__.py @@ -1 +1,53 @@ from . import common +import sys +import os + +def show_cpu_features(): + from numpy.lib.utils import _opt_info + info = _opt_info() + info = "NumPy CPU features: " + (info if info else 'nothing enabled') + # ASV wrapping stdout & stderr, so we assume having a tty here + if 'SHELL' in os.environ and sys.platform != 'win32': + # to avoid the red color that imposed by ASV + print(f"\033[33m{info}\033[0m") + else: + print(info) + +def dirty_lock(lock_name, lock_on_count=1): + # this lock occurred before each round to avoid duplicate printing + if not hasattr(os, "getppid"): + return False + ppid = os.getppid() + if not ppid or ppid == os.getpid(): + # not sure if this gonna happen, but ASV run each round in + # a separate process so the lock should be based on the parent + # process id only + return False + lock_path = os.path.abspath(os.path.join( + os.path.dirname(__file__), "..", "env", lock_name) + ) + # ASV load the 'benchmark_dir' to discovering the available benchmarks + # the issue here is ASV doesn't capture any strings from stdout or stderr + # during this stage so we escape it and lock on the second increment + try: + with open(lock_path, 'a+') as f: + f.seek(0) + count, _ppid = (f.read().split() + [0, 0])[:2] + count, _ppid = int(count), int(_ppid) + if _ppid == ppid: + if count >= lock_on_count: + return True + count += 1 + else: + count = 0 + f.seek(0) + f.truncate() + f.write(f"{str(count)} {str(ppid)}") + except IOError: + pass + return False + + +# FIXME: there's no official way to provide extra information to the test log +if not dirty_lock("print_cpu_features.lock"): + show_cpu_features() diff --git a/numpy/_pytesttester.py b/numpy/_pytesttester.py index 813e069a4702..acfaa1ca54a1 100644 --- a/numpy/_pytesttester.py +++ b/numpy/_pytesttester.py @@ -35,25 +35,13 @@ def _show_numpy_info(): - from numpy.core._multiarray_umath import ( - __cpu_features__, __cpu_baseline__, __cpu_dispatch__ - ) import numpy as np print("NumPy version %s" % np.__version__) relaxed_strides = np.ones((10, 1), order="C").flags.f_contiguous print("NumPy relaxed strides checking option:", relaxed_strides) - - if len(__cpu_baseline__) == 0 and len(__cpu_dispatch__) == 0: - enabled_features = "nothing enabled" - else: - enabled_features = ' '.join(__cpu_baseline__) - for feature in __cpu_dispatch__: - if __cpu_features__[feature]: - enabled_features += " %s*" % feature - else: - enabled_features += " %s?" % feature - print("NumPy CPU features:", enabled_features) + info = np.lib.utils._opt_info() + print("NumPy CPU features: ", (info if info else 'nothing enabled')) diff --git a/numpy/lib/utils.py b/numpy/lib/utils.py index 91e7cb8a3531..12a7cacdce87 100644 --- a/numpy/lib/utils.py +++ b/numpy/lib/utils.py @@ -197,20 +197,20 @@ def deprecate(*args, **kwargs): def deprecate_with_doc(msg): """ Deprecates a function and includes the deprecation in its docstring. - - This function is used as a decorator. It returns an object that can be - used to issue a DeprecationWarning, by passing the to-be decorated - function as argument, this adds warning to the to-be decorated function's + + This function is used as a decorator. It returns an object that can be + used to issue a DeprecationWarning, by passing the to-be decorated + function as argument, this adds warning to the to-be decorated function's docstring and returns the new function object. - + See Also -------- - deprecate : Decorate a function such that it issues a `DeprecationWarning` - + deprecate : Decorate a function such that it issues a `DeprecationWarning` + Parameters ---------- msg : str - Additional explanation of the deprecation. Displayed in the + Additional explanation of the deprecation. Displayed in the docstring after the warning. Returns @@ -218,7 +218,7 @@ def deprecate_with_doc(msg): obj : object """ - return _Deprecate(message=msg) + return _Deprecate(message=msg) #-------------------------------------------- @@ -1042,4 +1042,30 @@ def _median_nancheck(data, result, axis, out): result[n] = np.nan return result +def _opt_info(): + """ + Returns a string contains the supported CPU features by the current build. + + The string format can be explained as follows: + - dispatched features that are supported by the running machine + end with `*`. + - dispatched features that are "not" supported by the running machine + end with `?`. + - remained features are representing the baseline. + """ + from numpy.core._multiarray_umath import ( + __cpu_features__, __cpu_baseline__, __cpu_dispatch__ + ) + + if len(__cpu_baseline__) == 0 and len(__cpu_dispatch__) == 0: + return '' + + enabled_features = ' '.join(__cpu_baseline__) + for feature in __cpu_dispatch__: + if __cpu_features__[feature]: + enabled_features += f" {feature}*" + else: + enabled_features += f" {feature}?" + + return enabled_features #-----------------------------------------------------------------------------