Sim*_*ksy 5 python performance numpy
我注意到matmulnumpy 中的函数的性能比乘以数组视图时的函数要差得多dot。在这种情况下,我的数组视图是复杂数组的真实部分。这是重现该问题的一些代码:
import numpy as np
from timeit import timeit
N = 1300
xx = np.random.randn(N, N) + 1j
yy = np.random.randn(N, N) + 1J
x = np.real(xx)
y = np.real(yy)
assert np.shares_memory(x, xx)
assert np.shares_memory(y, yy)
dot = timeit('np.dot(x,y)', number = 10, globals = globals())
matmul = timeit('np.matmul(x,y)', number = 10, globals = globals())
print('time for np.matmul: ', matmul)
print('time for np.dot: ', dot)
Run Code Online (Sandbox Code Playgroud)
在我的机器上输出如下:
time for np.matmul: 23.023062199994456
time for np.dot: 0.2706864000065252
Run Code Online (Sandbox Code Playgroud)
这显然与共享内存有关,因为替换np.real(xx)为np.real(xx).copy()可以消除性能差异。
浏览 numpy 文档并不是特别有帮助,因为列出的差异没有讨论处理内存视图时的实现细节。
这些时间表明dot正在执行copy以下操作real:
In [22]: timeit np.dot(xx.real,xx.real)\n232 ms \xc2\xb1 3.34 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 1 loop each)\n\nIn [23]: timeit np.dot(xx.real.copy(),xx.real.copy())\n232 ms \xc2\xb1 4.18 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 1 loop each)\nRun Code Online (Sandbox Code Playgroud)\n将其应用于matmul几乎相同的时间:
In [24]: timeit np.matmul(xx.real.copy(),xx.real.copy())\n231 ms \xc2\xb1 3.54 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 1 loop each)\nRun Code Online (Sandbox Code Playgroud)\n再次,matmul我们real正在走一些缓慢的路线。 matmul/dot当给定数组时,两者都表现较差int,尽管没有实际情况那么慢matmul real。 matmul/dot也可以处理object数据类型,但速度更慢。
因此,作为 python 级别的用户,有很多幕后发生的事情是我们看不到的(也没有记录在案)。
\n我很想改变标题以关注复实数,但决定检查另一个view- 浮点数组的切片
In [42]: y=xx.real.copy()[::2,::2];y.shape,y.dtype\nOut[42]: ((650, 650), dtype('float64'))\n\nIn [43]: timeit np.dot(y,y)\n36.4 ms \xc2\xb1 63.4 \xc2\xb5s per loop (mean \xc2\xb1 std. dev. of 7 runs, 10 loops each)\nIn [44]: timeit np.dot(y.copy(),y.copy())\n35.6 ms \xc2\xb1 191 \xc2\xb5s per loop (mean \xc2\xb1 std. dev. of 7 runs, 10 loops each)\nRun Code Online (Sandbox Code Playgroud)\n再次明显的是,dot正在使用copies视图。 matmul才不是:
In [45]: timeit np.matmul(y,y)\n1.89 s \xc2\xb1 3.01 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 1 loop each)\nRun Code Online (Sandbox Code Playgroud)\n但对于副本,时间与点相同:
\nIn [46]: timeit np.matmul(y.copy(),y.copy())\n35.3 ms \xc2\xb1 102 \xc2\xb5s per loop (mean \xc2\xb1 std. dev. of 7 runs, 10 loops each)\nRun Code Online (Sandbox Code Playgroud)\n所以我的猜测是,如果不能将数组直接发送到 BLAS 例程,则 dot通常会生成 a 。显然采取了一条较慢的路线。copymatmul
虽然它们对 2d 数组的处理类似,dot但matmul在处理 3+d 数组的方式上却有很大不同。事实上,添加的主要原因@是为矩阵乘法提供方便的“批量”概念。
坚持使用大型复杂数组,让一个数组变大 3 倍:
\nIn [49]: yy=np.array([xx,xx,xx]);yy.shape\nOut[49]: (3, 1300, 1300)\n\nIn [50]: timeit np.dot(xx,xx)\n794 ms \xc2\xb1 12.2 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 1 loop each) \nIn [51]: timeit np.dot(xx,yy) # (yy,xx) same timings\n55.5 s \xc2\xb1 151 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 1 loop each)\nIn [52]: timeit np.matmul(xx,yy) # (yy,yy) same\n2.58 s \xc2\xb1 362 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 1 loop each)\nRun Code Online (Sandbox Code Playgroud)\nmatmul刚刚把时间增加了3;dot到了 70 岁。我可以探索更多事物,但不能在分钟范围内进行计时。