【问题标题】:numpy array multiplication slower than for loop with vector multiplication?numpy数组乘法比带有向量乘法的for循环慢?
【发布时间】:2015-08-31 17:46:19
【问题描述】:

我在乘以 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=2max_counts = 400,第一种方法比第二种方法快得多(即time_nptime_for 小得多)。但是,当我切换到例如n_pix=1000max_counts = 400 时,我发现方法2 更快(time_fortime_np 小得多)。我原以为方法 1 总是会更快,因为方法 2 显式运行循环,而方法 1 使用 np.multiply

所以,我有两个问题:

  1. 为什么对于固定的max_counts,计时会以这种方式作为n_pix 的函数?

  2. 编写此代码以使其对所有n_pix 都能快速运行的最佳方法是什么?

也就是说,任何人都可以提出方法3吗?在我的项目中,让这段代码在大大小小的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

【问题讨论】:

    标签: python arrays performance numpy cython


    【解决方案1】:

    首先,您可以轻松替换:

    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
    

    【讨论】:

      【解决方案2】:

      这可能是因为您的 numpy-only 版本需要创建/分配新的 ndarray(temp_vecn_array),而您的其他方法不需要。

      创建新的 ndarray 非常慢,如果您可以修改代码以不再需要不断创建它们,我希望您可以从该方法中获得更好的性能。

      【讨论】:

        猜你喜欢
        • 2020-12-19
        • 2021-11-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-03-18
        • 1970-01-01
        • 2018-11-29
        相关资源
        最近更新 更多