Download src/performance_test.py from elegantly/purem_sandbox: direct link, hf CLI and curl.
- Browser
- Download file 9.69 kB
-
https://huggingface.co/elegantly/purem_sandbox/resolve/main/src/performance_test.py
- Command line
-
hf download hf://elegantly/purem_sandbox/src/performance_test.py
-
curl -L -o performance_test.py https://huggingface.co/elegantly/purem_sandbox/resolve/main/src/performance_test.py
9.69 kB
| """ | |
| Business Source License 1.1 | |
| Copyright (C) 2025 Raman Marozau, raman@worktif.com | |
| Use of this software is governed by the Business Source License included in the LICENSE file and at www.mariadb.com/bsl11. | |
| Change Date: Never | |
| On the date above, in accordance with the Business Source License, use of this software will be governed by the open source license specified in the LICENSE file. | |
| Additional Use Grant: Free for personal and non-commercial research use only. | |
| SPDX-License-Identifier: BUSL-1.1 | |
| """ | |
| import gc | |
| import math | |
| import os | |
| import random | |
| from typing import List | |
| import numpy as np | |
| import pytest | |
| import torch | |
| from numba import njit | |
| from purem import purem | |
| purem.configure(license_key="purem-sandbox") | |
| ARRAY_SIZE = int(os.environ.get("ARRAY_SIZE", 200_000)) | |
| def generate_numbers(size, start=0.0, end=1.0) -> List[float]: | |
| """ | |
| Generates a list of random floating-point numbers within a specified range. | |
| This function creates a list of random numbers based on the size specified by | |
| the user. The numbers are uniformly distributed and fall within the range | |
| defined by the `start` and `end` parameters. If `start` and `end` are not provided, | |
| they default to 0.0 and 1.0, respectively. | |
| :param size: The number of random numbers to generate. | |
| :type size: int | |
| :param start: The lower bound of the range for the random numbers. Defaults to 0.0. | |
| :type start: float | |
| :param end: The upper bound of the range for the random numbers. Defaults to 1.0. | |
| :type end: float | |
| :return: A list of random floating-point numbers within the specified range. | |
| :rtype: list[float] | |
| """ | |
| return [random.uniform(start, end) for _ in range(size)] | |
| def clean_memory() -> None: | |
| """ | |
| A pytest fixture to clean up memory before each test. | |
| This fixture is automatically used for every test function due to the | |
| `autouse=True` parameter. It ensures that garbage collection is triggered, | |
| helping maintain a clean state in memory between test executions. | |
| :return: None | |
| """ | |
| gc.collect() | |
| def array_instance() -> np.ndarray: | |
| """ | |
| Creates and returns a numpy array fixture for testing. | |
| This fixture generates a numpy array based on the provided | |
| `generate_numbers` function and the defined `ARRAY_SIZE`. | |
| The array elements are cast to the `float` data type. | |
| :yield: A numpy array of floating-point numbers for testing. | |
| :rtype: np.ndarray | |
| """ | |
| return np.array(generate_numbers(ARRAY_SIZE), dtype=float) | |
| def softmax_purem(arr: np.ndarray) -> np.ndarray: | |
| """ | |
| Apply the softmax function to the input array. | |
| The softmax function computes the probabilities for each element | |
| in the input array by exponentiating them and normalizing the | |
| results so that they sum to 1. It is commonly used in machine | |
| learning classification problems. | |
| :param arr: Input array for which to compute the softmax. | |
| Should be a NumPy ndarray. | |
| :type arr: np.ndarray | |
| :return: The softmax-transformed array, where values represent | |
| probabilities and sum to 1 over the input dimensions. | |
| :rtype: np.ndarray | |
| """ | |
| return purem.softmax(arr) | |
| def softmax_numpy(arr: np.ndarray) -> np.ndarray: | |
| """ | |
| Compute the softmax of a given array using numpy. | |
| This function calculates the softmax of the input array. It stabilizes | |
| the computation by subtracting the maximum value from the array to | |
| prevent numerical overflow in the exponential calculation. | |
| :param arr: Input array for which softmax needs to be calculated. | |
| :type arr: np.ndarray | |
| :return: The computed softmax values as an array. | |
| :rtype: np.ndarray | |
| """ | |
| arr_max = np.max(arr) | |
| shifted = arr - arr_max | |
| arr_exp = np.exp(shifted) | |
| return arr_exp / np.sum(arr_exp) | |
| # device = "mps" if torch.backends.mps.is_available() else "cpu" | |
| device = "cpu" | |
| def softmax_torch(arr: np.ndarray) -> torch.Tensor: | |
| """ | |
| Applies the softmax function along the last dimension of the input array. | |
| This function takes a NumPy array or similar Python data structure as input, | |
| converts it to a PyTorch tensor, and computes the softmax values along the | |
| last dimension. The input is cast to a float32 tensor for compatibility with | |
| PyTorch computations. The result is returned as a PyTorch tensor. | |
| :param arr: Input array to be converted to a PyTorch tensor and processed | |
| using the softmax function. The array can be of any shape, with the | |
| softmax computation applied along its last dimension. | |
| :type arr: array-like | |
| :return: A PyTorch tensor containing the softmax values computed from the | |
| input array along its last dimension. | |
| :rtype: torch.Tensor | |
| """ | |
| array_torch = torch.asarray(arr, dtype=torch.float32, device=device) | |
| arr_torch = array_torch.to(dtype=torch.float32) | |
| return arr_torch.softmax(dim=-1) | |
| def softmax_numba(arr: np.ndarray) -> np.ndarray: | |
| """ | |
| Computes the softmax function for a given array with NumPy and Numba for | |
| parallelized execution. The function calculates the exponential normalization | |
| of a NumPy array to transform input values into probabilities that sum to one. | |
| The function optimizes the computation using the maximum value of the input | |
| array to improve numerical stability, specifically by avoiding overflow | |
| and underflow during exponentiation. | |
| :param arr: Input array of floats | |
| :type arr: numpy.ndarray | |
| :return: Array of softmax probabilities corresponding to the input | |
| :rtype: numpy.ndarray | |
| """ | |
| n = arr.shape[0] | |
| out = np.empty_like(arr) | |
| x_max = -np.inf | |
| for i in range(n): | |
| if arr[i] > x_max: | |
| x_max = arr[i] | |
| total = 0.0 | |
| for i in range(n): | |
| out[i] = math.exp(arr[i] - x_max) | |
| total += out[i] | |
| for i in range(n): | |
| out[i] /= total | |
| return out | |
| functions = { | |
| 'Softmax: Purem': softmax_purem, | |
| 'Softmax: NumPy': softmax_numpy, | |
| 'Softmax: PyTorch': softmax_torch, | |
| 'Softmax: Numba': softmax_numba, | |
| } | |
| def test_purem_softmax(benchmark, func_name, array_instance): | |
| """ | |
| Tests the performance of the `softmax_purem` function using the Benchmark fixture. | |
| :param benchmark: Benchmark fixture provided by pytest-benchmark for measuring | |
| the performance of the `softmax_purem` function. | |
| :param func_name: Name of the function being tested in the benchmarking process. | |
| :param array_instance: Input data instance provided to the `softmax_purem` function | |
| during the benchmarking test. | |
| :return: None | |
| """ | |
| benchmark(softmax_purem, array_instance) | |
| def test_numpy_softmax(benchmark, func_name, array_instance): | |
| """ | |
| Benchmark test for the numpy implementation of the softmax function. This test | |
| uses pytest parametrize to evaluate the performance of the function across | |
| different sizes of input and under a specific function name label. It clears | |
| garbage, generates random floating-point numbers of the specified size, and | |
| then benchmarks the softmax function. | |
| :param benchmark: The pytest-benchmark fixture used for executing the | |
| performance test of the softmax function. | |
| :param func_name: Determines the function name label for benchmarking. | |
| :param size: The size of the input data array to be generated and used in | |
| the softmax function. | |
| :return: None | |
| """ | |
| benchmark(softmax_numpy, array_instance) | |
| def test_torch_softmax(benchmark, func_name, array_instance): | |
| """ | |
| Tests the performance of the PyTorch implementation of the softmax function over | |
| an array of a specified size using the pytest benchmarking framework. | |
| This test generates a random array of numbers of the given size, applies the | |
| softmax operation implemented in PyTorch, and records its performance metrics | |
| using the pytest-benchmark plugin. | |
| :param benchmark: A pytest-benchmark function used to measure the performance | |
| of the specified function under test. | |
| :param func_name: The name of the function being tested. | |
| :param size: The size of the input array for which softmax computation is executed. | |
| :rtype: None | |
| :return: Returns None as this function is a test case invoked by pytest. | |
| """ | |
| benchmark(softmax_torch, array_instance) | |
| def test_numba_softmax(benchmark, func_name, array_instance): | |
| """ | |
| Tests the performance of the Numba-accelerated softmax function using pytest. | |
| This test leverages the pytest benchmarking tool to measure the time taken | |
| to compute the softmax operation on an array of specified size using the | |
| Numba implementation. It uses parameterized inputs for varying | |
| function names and sizes to validate the performance in different configurations. | |
| :param benchmark: The pytest benchmarking fixture used to time the execution | |
| of the softmax function. | |
| :param func_name: The name of the function being tested. This parameter is | |
| parameterized by the pytest decorator. | |
| :param size: The size of the input array for which the softmax function is | |
| benchmarked. This parameter is also parameterized by the | |
| pytest decorator. | |
| :return: None | |
| """ | |
| benchmark(softmax_numba, array_instance) | |