Repository: numpy/numpy
File: benchmarks/benchmarks/bench_lib.py (Python)

Code before:
    def time_nancumprod(self, array_size, percent_nans):
        np.nancumprod(self.arr)

    def time_nanmean(self, array_size, percent_nans):
        np.nanmean(self.arr)

    def time_nanvar(self, array_size, percent_nans):
        np.nanvar(self.arr)

    def time_nanstd(self, array_size, percent_nans):
        np.nanstd(self.arr)

    def time_nanmedian(self, array_size, percent_nans):
        np.nanmedian(self.arr)

    def time_nanquantile(self, array_size, percent_nans):
        np.nanquantile(self.arr, q=0.2)

    def time_nanpercentile(self, array_size, percent_nans):
        np.nanpercentile(self.arr, q=50)


class Unique(Benchmark):
    """Benchmark for np.unique with np.nan values."""

    param_names = ["array_size", "percent_nans", "percent_unique_values", "dtype"]
    params = [
        # sizes of the 1D arrays
        [int(2e3), int(2e6)],
        # percent of np.nan in arrays
        [10., 90.],
        # percent of unique values in arrays
        [0.2, 20.],
        # dtypes of the arrays
        [np.float64, np.complex128, np.dtypes.StringDType(na_object=np.nan)],
    ]

    def setup(self, array_size, percent_nans, percent_unique_values, dtype):
        rng = np.random.default_rng(123)
        # produce a randomly shuffled array with the
        # approximate desired percentage np.nan content
        unique_values_size = max(int(percent_unique_values / 100. * array_size), 2)
        match dtype:
            case np.float64:
                unique_array = rng.uniform(size=unique_values_size).astype(dtype)
            case np.complex128:
                unique_array = np.array(
                    [
                        complex(*rng.uniform(size=2))
                        for _ in range(unique_values_size)
                    ],

Review comment (performance):
This PR was discussed in the community call today because of the slowdown in the `asv` benchmarks, especially because of the impact it has on the CI run time/resource usage.

If we want the ability to run more thorough benchmarks on some occasions and not others, one possible solution is to follow the model used by `scikit-learn` , or something similar to it. For example, they have an `SKLBENCH_PROFILE` environment variable that can be swapped between `regular`, `fast`, and `large_scale`. 

https://github.com/scikit-learn/scikit-learn/blob/726ed184ed80b0191732baaaf5825b86b41db4d2/asv_benchmarks/benchmarks/config.json#L2-L10

It looks like the different `asv` profile settings then propagate through an abstract benchmark base class at: https://github.com/scikit-learn/scikit-learn/blob/main/asv_benchmarks/benchmarks/common.py#L98

Anyway, something like that may be an option, and I told Matti I'd share this earlier today.


Code after:
    def time_nancumprod(self, array_size, percent_nans):
        np.nancumprod(self.arr)

    def time_nanmean(self, array_size, percent_nans):
        np.nanmean(self.arr)

    def time_nanvar(self, array_size, percent_nans):
        np.nanvar(self.arr)

    def time_nanstd(self, array_size, percent_nans):
        np.nanstd(self.arr)

    def time_nanmedian(self, array_size, percent_nans):
        np.nanmedian(self.arr)

    def time_nanquantile(self, array_size, percent_nans):
        np.nanquantile(self.arr, q=0.2)

    def time_nanpercentile(self, array_size, percent_nans):
        np.nanpercentile(self.arr, q=50)


class Unique(Benchmark):
    """Benchmark for np.unique with np.nan values."""

    param_names = ["array_size", "percent_nans", "percent_unique_values", "dtype"]
    params = [
        # sizes of the 1D arrays
        [200, int(2e5)],
        # percent of np.nan in arrays
        [10., 90.],
        # percent of unique values in arrays
        [0.2, 20.],
        # dtypes of the arrays
        [np.float64, np.complex128, np.dtypes.StringDType(na_object=np.nan)],
    ]

    def setup(self, array_size, percent_nans, percent_unique_values, dtype):
        rng = np.random.default_rng(123)
        # produce a randomly shuffled array with the
        # approximate desired percentage np.nan content
        unique_values_size = max(int(percent_unique_values / 100. * array_size), 2)
        match dtype:
            case np.float64:
                unique_array = rng.uniform(size=unique_values_size).astype(dtype)
            case np.complex128:
                unique_array = np.array(
                    [
                        complex(*rng.uniform(size=2))
                        for _ in range(unique_values_size)
                    ],