Numpy提供具有类似功能的vectorize
和frompyfunc
。
正如SO-post中所指出的,vectorize
Package frompyfunc
并正确处理返回数组的类型,而frompyfunc
返回np.object
的数组。
然而,对于所有大小,frompyfunc
的性能始终比vectorize
高10 - 20%,这也不能用不同的返回类型来解释。
考虑以下变体:
import numpy as np
def do_double(x):
return 2.0*x
vectorize = np.vectorize(do_double)
frompyfunc = np.frompyfunc(do_double, 1, 1)
def wrapped_frompyfunc(arr):
return frompyfunc(arr).astype(np.float64)
wrapped_frompyfunc
只是将frompyfunc
的结果转换为正确的类型--正如我们所看到的,这个操作的开销几乎可以忽略不计。
它产生以下时序(蓝线为frompyfunc
):
我预计vectorize
会有更多的开销--但这应该只适用于小尺寸的情况。另一方面,将np.object
转换为np.float64
也是在wrapped_frompyfunc
中完成的--这仍然要快得多。
如何解释这种性能差异?
使用perfplot-package生成时序比较的代码(给定上述函数):
import numpy as np
import perfplot
perfplot.show(
setup=lambda n: np.linspace(0, 1, n),
n_range=[2**k for k in range(20,27)],
kernels=[
frompyfunc,
vectorize,
wrapped_frompyfunc,
],
labels=["frompyfunc", "vectorize", "wrapped_frompyfunc"],
logx=True,
logy=False,
xlabel='len(x)',
equality_check = None,
)
NB:对于较小的大小,vectorize
的开销要高得多,但这是意料之中的(毕竟它 Package 了frompyfunc
):
2条答案
按热度按时间ajsxfq5m1#
按照@hpaulj的提示,我们可以分析
vectorize
-函数:其显示100%的时间花费在
_vectorize_call
:它显示了我在假设中遗漏的部分:双数组在预处理步骤中完全转换为对象数组(这在内存方面不是一件非常明智的事情)。
wrapped_frompyfunc
的其他部分类似:当我们看一下峰值内存消耗(例如通过
/usr/bin/time python script.py
)时,我们会看到,vectorized
版本的内存消耗是frompyfunc
的两倍,后者使用了更复杂的策略:双数组是以NPY_BUFSIZE
大小的块(即8192)处理的,因此内存中同时只有8192个python-float(24字节+8字节指针)(而不是数组中的元素数,后者可能更高)。从操作系统保留内存的成本+更多的缓存未命中可能导致更长的运行时间。我的收获:
vectorize
和frompyfunc
都不应该被使用,当得到的ufunc
应该被用在“真实的代码”中时。相反,应该用C或者使用numba/similar来写它。在object-array上调用
frompyfunc
所需的时间比在double数组上少:但是,上面的行分析器计时并没有显示出对对象使用
ufunc
而不是对双精度数使用ufunc
的任何优势:3.089595s与3014961.0s。我的怀疑是,这是由于在创建所有对象的情况下缓存未命中更多,而在二级缓存中只有8192个创建的对象(256 Kb)是热的。kyks70gy2#
这个问题完全没有意义。如果速度是问题的关键,那么无论是矢量化还是frompyfunction都不是答案。与更快的方法相比,它们之间的任何速度差异都显得微不足道。
我发现这个问题想知道为什么frompyfunc破坏了我的代码(它返回对象),而vectorize工作(它返回我告诉它做什么),并发现人们谈论速度。
现在,在2020年代,numba/jit是可用的,它吹任何速度优势的frompyfunc清洁出水。
我编写了一个toy应用程序,从另一个应用程序返回一个大型的np.uint8数组,并得到了以下结果。
因此,速度比numpy快1000倍,比python快4000倍
如果有人觉得麻烦的话,我可以把代码贴出来。编写njit版本的代码只不过是在python函数前面添加一行@njit,所以你不需要太过努力。
这比用vectorize封装函数要不方便,因为你必须手动编写numpy数组的循环,但它避免了编写外部C函数。你需要用python的numpy/C类子集编写,避免使用python对象。
也许我在这里对numpy太苛刻了,要求它对一个纯python函数进行矢量化。那么,如果我对numpy的原生数组函数(如min)进行基准测试,会怎样呢?
令人惊讶的是,在np.uint8的385 x360数组上,使用numba/jit比使用np.min获得了10倍的速度提升。np.min(array)为230 us是基准。使用单核时,Numba达到了60 us,使用所有四核时,达到了22 us。