【发布时间】:2021-08-05 04:02:40
【问题描述】:
在 Windows 上的 Python 3.7.9 64 位和 Numpy 1.19.5 上测试
这是一个非常简单但令人困惑的问题。
考虑一下我给自己弄了一大堆形状(10000, 16)。
import time
import numpy as np
arr = np.random.random((10000, 16))
现在我想将每一行与其他每一行的点积相乘。为此,我将数组乘以自身转置。结果数组的大小为(10000, 10000)。这是一项非常昂贵的操作,我不希望它很快。让我们计时吧。
def measure(func):
start = time.time()
func()
print(time.time() - start)
>>> measure(lambda: arr * arr.T)
0.875575065612793
这里没有惊喜。与 Numpy 一样高性能,它仍然需要将近一秒钟的时间来计算结果。
但是如果...
>>> measure(lambda: arr * 1 @ arr.T)
0.4331023693084717
在执行矩阵乘法之前将矩阵乘以1 以某种方式加快了计算速度。
根据测试,如果arr 是其他数据类型,这也成立。
>>> arr = arr.astype('float32')
>>> measure(lambda: arr @ arr.T)
0.6592690944671631
>>> measure(lambda: arr * 1 @ arr.T)
0.22941327095031738
我们可以看到他们确实在计算相同的结果。
>>> np.max(np.abs(arr @ arr.T - arr * 1 @ arr.T))
1.9073486e-06
将数组乘以1(或任何其他标量)是否会给它一些超能力?我们可以测试一下。
>>> arr_times_1 = arr * 1
>>> measure(lambda: arr_times_1 @ arr.T)
0.23055601119995117
看起来确实如此。它会以某种方式改变数组吗? (答案是否定的。)
>>> np.max(np.abs(arr - arr_times_1))
0.0
我们能不能“抓住”这个超级大国?
>>> arr_copy_1 = arr_times_1.copy()
>>> arr_copy_2 = np.array(arr_times_1)
>>> measure(lambda: arr_copy_1 @ arr.T)
0.2252507209777832
>>> measure(lambda: arr_copy_2 @ arr.T)
0.22612690925598145
似乎我们可以。那么np.random.random 给我们的数组有问题吗?
>>> arr_copy_3 = np.array(arr)
>>> measure(lambda: arr_copy_2 @ arr.T)
0.2222919464111328
这个结果肯定支持这个理论。
>>> arr_copy_4 = arr.copy()
>>> measure(lambda: arr_copy_4 @ arr.T)
0.23076415061950684
即使只是在原始数组上调用copy() 似乎也能解决问题。那么可能是什么问题呢?
>>> arr.flags
C_CONTIGUOUS : True
F_CONTIGUOUS : False
OWNDATA : True
WRITEABLE : True
ALIGNED : True
WRITEBACKIFCOPY : False
UPDATEIFCOPY : False
>>> arr_times_1.flags
C_CONTIGUOUS : True
F_CONTIGUOUS : False
OWNDATA : True
WRITEABLE : True
ALIGNED : True
WRITEBACKIFCOPY : False
UPDATEIFCOPY : False
二进制数据有问题?
>>> arr_bytes = arr.tobytes()
>>> arr_times_1_bytes = arr_times_1.tobytes()
>>> arr_bytes == arr_times_1_bytes
True
没有区别。
为什么?
【问题讨论】:
标签: python performance numpy