问题不在于X 和X.T 是同一内存空间的视图本身,
而是 X.T 是 F-contiguous 而不是 C-contiguous。当然,这必须
在这种情况下,至少有一个输入数组必须为真
您将数组与其转置视图相乘。
在 numpy np.dot 将
创建 any F-ordered 输入数组的 C-ordered 副本,而不仅仅是恰好是同一块的视图的那些
记忆。
例如:
X = np.random.randn(1000,50000)
Y = np.random.randn(50000, 100)
# X and Y are both C-order, no copy
%memit np.dot(X, Y)
# maximum of 1: 485.554688 MB per loop
# make X Fortran order and Y C-order, now the larger array (X) gets
# copied
X = np.asfortranarray(X)
%memit np.dot(X, Y)
# maximum of 1: 867.070312 MB per loop
# make X C-order and Y Fortran order, now the smaller array (Y) gets
# copied
X = np.ascontiguousarray(X)
Y = np.asfortranarray(Y)
%memit np.dot(X, Y)
# maximum of 1: 523.792969 MB per loop
# make both of them F-ordered, both get copied!
X = np.asfortranarray(X)
%memit np.dot(X, Y)
# maximum of 1: 905.093750 MB per loop
如果复制存在问题(例如,当X 非常大时),您能做些什么?
最好的选择可能是升级到更新版本的 numpy - 正如@perimosocordiae 指出的那样,这个性能问题已在this pull request 中得到解决。
如果由于某种原因您无法升级 numpy,还有一个技巧可以让您执行快速的、基于 BLAS 的点积,而无需通过直接通过 scipy.linalg.blas 调用相关的 BLAS 函数来强制复制(无耻地从this answer):
from scipy.linalg import blas
X = np.random.randn(1000,50000)
%memit res1 = np.dot(X, X.T)
# maximum of 1: 845.367188 MB per loop
%memit res2 = blas.dgemm(alpha=1., a=X.T, b=X.T, trans_a=True)
# maximum of 1: 471.656250 MB per loop
print np.all(res1 == res2)
# True