如果单个整数具有Python本地int类型,如何更快地将其添加到NumPy数组中

7

我向一个有1000个元素的整数数组中添加一个整数。如果我先将单个整数从numpy.int64转换为本地的Python类型int,这会更快25%。

为什么?作为一个经验法则,我是否应该将单个数字转换为本地 Python 格式,以便在处理大约此大小的数组时进行单个数字到数组操作?

注:可能与我的先前问题“如果数字具有本地 Python 复杂类型,则共轭复数的速度要快得多”相关。

import numpy as np

nnu = 10418
nnu_use = 5210
a = np.random.randint(nnu,size=1000)
b = np.random.randint(nnu_use,size=1)[0]

%timeit a + b                            # --> 3.9 µs ± 19.9 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit a + int(b)                       # --> 2.87 µs ± 8.07 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

请注意,即使是标量到标量的操作,加速比也可能非常巨大(50倍),如下所示:
np.random.seed(100)

a = (np.random.rand(1))[0]
a_native = float(a)
b = complex(np.random.rand(1)+1j*np.random.rand(1))
c = (np.random.rand(1)+1j*np.random.rand(1))[0]
c_native = complex(c)

%timeit a * (b - b.conjugate() * c)                # 6.48 µs ± 49.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit a_native * (b - b.conjugate() * c_native)  # 283 ns ± 7.78 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit a * b                                      # 5.07 µs ± 17.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit a_native * b                               # 94.5 ns ± 0.868 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

更新:最新的numpy版本是否修复了速度差异?numpy 1.23的发布说明中提到标量运算现在更快,详见https://numpy.org/devdocs/release/1.23.0-notes.html#performance-improvements-and-changeshttps://github.com/numpy/numpy/pull/21188。我正在使用python 3.7.6, numpy 1.21.2


2
我不知道答案,但我敢猜测:我认为b是通过引用传递的,而int(b)是按值传递的,并且两者采用不同的代码路径。这可以解释方差差异,因为按值传递的版本通常更易于缓存,也可能解释平均差异。另一个表明这可能是情况的指标是b具有内部缓冲区(memoryview(b)有效),并且您可以像处理任何其他ndarray一样检查b.flags - FirefoxMetzger
1个回答

1
在我的Windows PC上,使用CPython 3.8.1,我得到了以下结果:
[Old] Numpy 1.22.4:
 - First test: 1.65 µs VS 1.43 µs
 - Second:     2.03 µs VS 0.17 µs

[New] Numpy 1.23.1:
 - First test: 1.38 µs VS 1.24 µs    <----  A bit better than Numpy 1.22.4
 - Second:     0.38 µs VS 0.17 µs    <----  Much better than Numpy 1.22.4

虽然新版本的Numpy提供了很好的性能提升,但使用(默认的)CPython解释器时,本机类型始终比Numpy类型更快。事实上,解释器需要调用Numpy的C函数,而原生类型不需要。此外,Numpy的检查和包装不是最优的,但Numpy一开始就没有设计用于快速标量计算(尽管之前的开销是不合理的)。事实上,标量计算非常低效,解释器阻止任何快速执行。
如果您计划执行许多标量操作,则需要使用本地编译代码,可能使用Cython、Numba或甚至原始的C/C++模块。请注意,Cython不会对Numpy调用进行优化/内联,但可以更快地处理本机类型。本地代码可以在一个甚至两个数量级的时间内完成此操作。
请注意,在第一种情况下,Numpy函数中的路径不同,并且当值不是CPython对象时,Numpy进行了额外的检查,这些检查略微昂贵。但仍应该是恒定的开销(现在相对较小)。否则,这将是一个错误(应该报告)。
相关文献:为什么 np.sum(range(N)) 非常慢?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接