Python 当其中一个是np.array(dtype=np.complex128.real)时,numpy matmul非常慢
我发现,当Python 当其中一个是np.array(dtype=np.complex128.real)时,numpy matmul非常慢,python,numpy,Python,Numpy,我发现,当matmuling两个numpy数组时,如果其中一个是更大的复数数组的实部或虚部,则操作可能比使用原始复数数组慢数十甚至数百倍 考虑以下示例: import numpy as np from time import time class timeit(): def __init__(self, string): self.string = string def __enter__(self): self.t0 = time()
matmul
ing两个numpy
数组时,如果其中一个是更大的复数数组的实部或虚部,则操作可能比使用原始复数数组慢数十甚至数百倍
考虑以下示例:
import numpy as np
from time import time
class timeit():
def __init__(self, string):
self.string = string
def __enter__(self):
self.t0 = time()
def __exit__(self, *args):
print(f'{self.string} : {time() - self.t0}')
A = np.random.rand(200, 1000) + 0j
B = np.random.rand(1000, 5000)
with timeit('with complex'):
out = A @ B
Ar = A.real
with timeit('after .real'):
out = Ar @ B
Ai = (A * 1j).imag
with timeit('after .imag'):
out = Ai @ B
with timeit('after .astype(float)'):
out = A.astype(np.float64()) @ B
with timeit('after .real.astype(float)'):
out = A.real.astype(np.float64()) @ B
输出是
with complex : 0.09374785423278809
after .real : 1.9792003631591797
after .imag : 1.717487096786499
after .astype(float) : 0.016920804977416992
after .real.astype(float) : 0.017952442169189453
请注意,当两个数组中的一个为A.real
或A.imag
时,操作速度会慢20倍(如果数组较大,则速度会慢数百倍)
使用A.astype(np.float64)
速度非常快,但每次都会抛出警告,即使虚部为空
唯一快速而安静的解决方案似乎是A.real.astype(float)
,但是,老实说,它在我看来相当难看
通过检查这些数组的内存地址,我得到以下结果
def aid(x):
# This function returns the memory
# block address of an array.
return x.__array_interface__['data'][0]
print(f'ID(A.real) == ID(A): {aid(A.real) == aid(A)}')
print(f'ID(A.imag) == ID(A): {aid(A.imag) == aid(A)}')
print(f'ID(A.astype) == ID(A): {aid(A.astype(np.float64())) == aid(A)}')
print(f'ID(A.real.astype) == ID(A): {aid(A.real.astype(np.float64())) == aid(A)}')
这是回报
ID(A.real) == ID(A): True
ID(A.imag) == ID(A): False
ID(A.astype) == ID(A): False
ID(A.real.astype) == ID(A): False
这似乎表明A.real
与A
具有相同的内存地址,而A.astype(np.float64)
没有。这会导致这种行为吗?但是A
和A.imag
有不同的内存地址,但是matmul
仍然非常慢
这是虫子吗?
我应该使用
A.real.astype(np.float64)
的解决方案吗?重要的不是内存位置或布局。这是@
根据输入类型选择的路径A.real
不是一个“新”数组;它是一种访问复杂数据类型实际值的方法。我没有时间展示我的全部计时结果,但这里有几个快速的结果
In [2]: timeit A@B
436 ms ± 32.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
添加real
会以这种方式降低速度:
In [3]: timeit A.real@B
3.93 s ± 4.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [4]: %%timeit a = A.real
...: a@B
3.92 s ± 3.29 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
但制作一个新的浮点数组会加快速度:
In [5]: %%timeit a = A.real.copy()
...: a@B
101 ms ± 496 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
A.real
不进行任何实际计算:
In [6]: timeit A.real
203 ns ± 9.42 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
从实际值生成新数组并不是那么慢:
In [7]: timeit A.real.copy()
239 µs ± 3.54 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
包含内联复制不会影响时间:
In [8]: timeit A.real.copy()@B
102 ms ± 1.08 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
点
不受实数的困扰
:
In [9]: timeit A.real.dot(B)
106 ms ± 3.49 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
A.dot(B)
与A@B
因此,复杂的计算大约比浮动慢4倍。考虑到A
的平均值是平均值的两倍,这听起来很合理
dot
正确处理实数
,无需太多麻烦即可提取实数
@
有某种缺陷,使其运行缓慢。重要的不是内存位置或布局。这是@
根据输入类型选择的路径A.real
不是一个“新”数组;它是一种访问复杂数据类型实际值的方法。我没有时间展示我的全部计时结果,但这里有几个快速的结果
In [2]: timeit A@B
436 ms ± 32.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
添加real
会以这种方式降低速度:
In [3]: timeit A.real@B
3.93 s ± 4.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [4]: %%timeit a = A.real
...: a@B
3.92 s ± 3.29 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
但制作一个新的浮点数组会加快速度:
In [5]: %%timeit a = A.real.copy()
...: a@B
101 ms ± 496 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
A.real
不进行任何实际计算:
In [6]: timeit A.real
203 ns ± 9.42 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
从实际值生成新数组并不是那么慢:
In [7]: timeit A.real.copy()
239 µs ± 3.54 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
包含内联复制不会影响时间:
In [8]: timeit A.real.copy()@B
102 ms ± 1.08 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
点
不受实数的困扰
:
In [9]: timeit A.real.dot(B)
106 ms ± 3.49 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
A.dot(B)
与A@B
因此,复杂的计算大约比浮动慢4倍。考虑到A
的平均值是平均值的两倍,这听起来很合理
dot
正确处理实数
,无需太多麻烦即可提取实数
@
有某种缺陷,发送速度很慢。或者pickle错误地丢弃了您的大部分数据。无法在我的系统上重现。两者都给了我大约0.2秒的期望值。@mCoding检查编辑。另外numpy.array.copy()
解决了这个问题。在我看来,这与pickle无关。如果不知道原始数组是如何创建的,我想我们帮不了忙。我想不出一个数组存储或属性会让事情变慢,而且不会通过pickle
或copy
@hpaulj,这基本上是我的问题。哪个属性可能导致此问题,而未通过pickle
或copy
?或我应该在原始数组中检查哪个属性,以允许我复制错误?我将尝试生成一个最小的工作示例,但问题似乎很难重现(即使它在我的代码中系统地发生),或者pickle错误地丢弃了大部分数据。无法在我的系统上重现。两者都给了我大约0.2秒的期望值。@mCoding检查编辑。另外numpy.array.copy()
解决了这个问题。在我看来,这与pickle无关。如果不知道原始数组是如何创建的,我想我们帮不了忙。我想不出一个数组存储或属性会让事情变慢,而且不会通过pickle
或copy
@hpaulj,这基本上是我的问题。哪个属性可能导致此问题,而未通过pickle
或copy
?或我应该在原始数组中检查哪个属性,以允许我复制错误?我将尝试生成一个最小的工作示例,但是这个问题似乎很难重现(即使它在我的代码中系统地发生),所以您认为这是由于@
的实现中的错误造成的?在问题的前一个版本中,我注意到.copy()
解决了这个问题。我的问题是…为什么?matmul
显然会根据输入的性质选择不同的评估路线。对于浮点数组的直接评估,它将任务直接传递给高度优化的BLAS类型例程。但它是编译的代码,所以更难探索。可能会有一些关于github Repository的讨论。因此,您认为这是由于@
的实现中的错误造成的?在问题的前一个版本中,我注意到.copy()
解决了这个问题。