numpy 数组乘法比带向量乘法的循环慢?
numpy array multiplication slower than for loop with vector multiplication?
我在乘以 numpy 数组时遇到了以下问题。在下面的示例中(与我正在处理的真实版本相比略有简化),我从一个几乎为空的数组 A
和一个完整的数组 C
开始。然后我使用递归算法来填写 A
.
下面,我以两种不同的方式执行此算法。第一种方法涉及操作
n_array = np.arange(0,c-1)
temp_vec= C[c-n_array] * A[n_array]
A[c] += temp_vec.sum(axis=0)
而第二种方法涉及for循环
for m in range(0, c - 1):
B[c] += C[c-m] * B[m]
请注意,数组A和B是相同的,但是使用两种不同的方法填充它们。
在下面的示例中,我计算了使用每种方法执行计算所需的时间。我发现,例如,对于 n_pix=2
和 max_counts = 400
,第一种方法比第二种方法快得多(也就是说,time_np
比 time_for
小得多)。但是,当我切换到例如 n_pix=1000
和 max_counts = 400
时,我发现方法 2 快得多(time_for
比 time_np
小得多)。我原以为方法 1 总是会更快,因为方法 2 显式地运行循环,而方法 1 使用 np.multiply
.
所以,我有两个问题:
对于固定的 max_counts
,为什么计时会这样作为 n_pix
的函数?
编写此代码以使其对所有 n_pix
都能快速运行的最佳方法是什么?
也就是说,谁能推荐方法三?在我的项目中,这段代码在大大小小的n_pix
范围内快速执行是非常重要的。
import numpy as np
import time
def return_timing(n_pix,max_counts):
A=np.zeros((max_counts+1,n_pix))
A[0]=np.random.random(n_pix)*1.8
A[1]=np.random.random(n_pix)*2.3
B=np.zeros((max_counts+1,n_pix))
B[0]=A[0]
B[1]=A[1]
C=np.outer(np.random.random(max_counts+1),np.random.random(n_pix))*3.24
time_np=0
time_for=0
for c in range(2, max_counts + 1):
t0 = time.time()
n_array = np.arange(0,c-1)
temp_vec= C[c-n_array] * A[n_array]
A[c] += temp_vec.sum(axis=0)
time_np += time.time()-t0
t0 = time.time()
for m in range(0, c - 1):
B[c] += C[c-m] * B[m]
time_for += time.time()-t0
return time_np, time_for
这可能是因为您的 numpy-only 版本需要 creation/allocation 个新的 ndarrays(temp_vec
和 n_array
),而您的其他方法不需要。
创建新的 ndarrays 非常慢,如果您可以修改代码使其不再需要连续创建它们,我希望您可以从该方法中获得更好的性能。
首先,您可以轻松替换:
n_array = np.arange(0,c-1)
temp_vec= C[c-n_array] * A[n_array]
A[c] += temp_vec.sum(axis=0)
与:
A[c] += (C[c:1:-1] * A[:c-1]).sum(0)
这要快得多,因为使用数组进行索引比切片要慢得多。但是 temp_vec
仍然隐藏在那里,在求和完成之前创建。这导致了使用 einsum
的想法,这是最快的,因为它不会生成临时数组。
A[c] = np.einsum('ij,ij->j', C[c:1:-1], A[:c-1])
时机。对于小型阵列:
>>> return_timing(10,10)
numpy OP 0.000525951385498
loop OP 0.000250101089478
numpy slice 0.000246047973633
einsum 0.000170946121216
对于大号:
>>> return_timing(1000,100)
numpy OP 0.185983896255
loop OP 0.0458009243011
numpy slice 0.038364648819
einsum 0.0167834758759
我在乘以 numpy 数组时遇到了以下问题。在下面的示例中(与我正在处理的真实版本相比略有简化),我从一个几乎为空的数组 A
和一个完整的数组 C
开始。然后我使用递归算法来填写 A
.
下面,我以两种不同的方式执行此算法。第一种方法涉及操作
n_array = np.arange(0,c-1)
temp_vec= C[c-n_array] * A[n_array]
A[c] += temp_vec.sum(axis=0)
而第二种方法涉及for循环
for m in range(0, c - 1):
B[c] += C[c-m] * B[m]
请注意,数组A和B是相同的,但是使用两种不同的方法填充它们。
在下面的示例中,我计算了使用每种方法执行计算所需的时间。我发现,例如,对于 n_pix=2
和 max_counts = 400
,第一种方法比第二种方法快得多(也就是说,time_np
比 time_for
小得多)。但是,当我切换到例如 n_pix=1000
和 max_counts = 400
时,我发现方法 2 快得多(time_for
比 time_np
小得多)。我原以为方法 1 总是会更快,因为方法 2 显式地运行循环,而方法 1 使用 np.multiply
.
所以,我有两个问题:
对于固定的
max_counts
,为什么计时会这样作为n_pix
的函数?编写此代码以使其对所有
n_pix
都能快速运行的最佳方法是什么?
也就是说,谁能推荐方法三?在我的项目中,这段代码在大大小小的n_pix
范围内快速执行是非常重要的。
import numpy as np
import time
def return_timing(n_pix,max_counts):
A=np.zeros((max_counts+1,n_pix))
A[0]=np.random.random(n_pix)*1.8
A[1]=np.random.random(n_pix)*2.3
B=np.zeros((max_counts+1,n_pix))
B[0]=A[0]
B[1]=A[1]
C=np.outer(np.random.random(max_counts+1),np.random.random(n_pix))*3.24
time_np=0
time_for=0
for c in range(2, max_counts + 1):
t0 = time.time()
n_array = np.arange(0,c-1)
temp_vec= C[c-n_array] * A[n_array]
A[c] += temp_vec.sum(axis=0)
time_np += time.time()-t0
t0 = time.time()
for m in range(0, c - 1):
B[c] += C[c-m] * B[m]
time_for += time.time()-t0
return time_np, time_for
这可能是因为您的 numpy-only 版本需要 creation/allocation 个新的 ndarrays(temp_vec
和 n_array
),而您的其他方法不需要。
创建新的 ndarrays 非常慢,如果您可以修改代码使其不再需要连续创建它们,我希望您可以从该方法中获得更好的性能。
首先,您可以轻松替换:
n_array = np.arange(0,c-1)
temp_vec= C[c-n_array] * A[n_array]
A[c] += temp_vec.sum(axis=0)
与:
A[c] += (C[c:1:-1] * A[:c-1]).sum(0)
这要快得多,因为使用数组进行索引比切片要慢得多。但是 temp_vec
仍然隐藏在那里,在求和完成之前创建。这导致了使用 einsum
的想法,这是最快的,因为它不会生成临时数组。
A[c] = np.einsum('ij,ij->j', C[c:1:-1], A[:c-1])
时机。对于小型阵列:
>>> return_timing(10,10)
numpy OP 0.000525951385498
loop OP 0.000250101089478
numpy slice 0.000246047973633
einsum 0.000170946121216
对于大号:
>>> return_timing(1000,100)
numpy OP 0.185983896255
loop OP 0.0458009243011
numpy slice 0.038364648819
einsum 0.0167834758759