diff --git a/CHANGELOG.md b/CHANGELOG.md index e3945d1a..3d1795ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Multi-iterator constructors now return a status corresponding to allocation success or failure, and raise `MemoryError` instead of `ValueError` [gh-373](https://github.com/IntelPython/mkl_fft/pull/373) * `_direct_fftnd` now also checks the status returned by the backend instead of discarding it [gh-373](https://github.com/IntelPython/mkl_fft/pull/373) * Pinned Cython in the Coverity Scan workflow so generated code stays stable between scans, and added `coverity/README.md` documenting the known false-positive families and the scan review checklist [gh-374](https://github.com/IntelPython/mkl_fft/pull/374) +* Reduced Python overhead in `norm="forward"`/`"ortho"` scaling by computing the scale factor without `numpy.prod` [gh-384](https://github.com/IntelPython/mkl_fft/pull/384) ### Fixed * Fixed `norm="forward"`/`"ortho"` scaling in `fftn`, `ifftn`, `rfftn`, `irfftn` and the `fft2` family when only a subset of axes is transformed: the scale used the full array shape instead of the transformed axes [gh-336](https://github.com/IntelPython/mkl_fft/issues/336), [gh-370](https://github.com/IntelPython/mkl_fft/pull/370) diff --git a/mkl_fft/_fft_utils.py b/mkl_fft/_fft_utils.py index 9681adcb..8e2fb505 100644 --- a/mkl_fft/_fft_utils.py +++ b/mkl_fft/_fft_utils.py @@ -23,6 +23,8 @@ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import math + import numpy as np # pylint: disable=no-name-in-module @@ -70,7 +72,15 @@ def _compute_fwd_scale(norm, n, shape): return 1.0 ss = n if n is not None else shape - nn = np.prod(ss) + # Avoid np.prod's array-creation overhead on the hot scalar (1-D) and + # sequence (N-D) paths; np.prod stays as the fallback for array-like + # `n`/`s` (e.g. np.array(8)) that math.prod can't handle. + if isinstance(ss, (int, np.integer)): + nn = ss + elif isinstance(ss, (list, tuple)): + nn = math.prod(ss) + else: + nn = np.prod(ss) fsc = 1 / nn if nn != 0 else 1 if norm == "forward": return fsc