如何加快这个numpy.arange循环的速度?

时间:2019-08-04 14:12:49

标签: python numpy

在python程序中,以下函数被另一个函数调用了大约20,000次,而另一个函数被执行了30次,被调用了大约1000次。因此,调用此特定功能的总次数约为600,000,000。在python中,它需要两个多小时(可能更长;可能会中止程序,而无需等待其完成),而用Java编写的相同任务基本上只需不到5分钟。如果我将20,000以上更改为400(不影响程序其余部分的其他所有内容),则总时间将减少到大约4分钟(这意味着此特定功能是元凶)。我该怎么做才能加快Python版本的速度,或者只是不可能?在此函数内没有操作任何列表(整个程序的其他地方都有列表,但是在那些地方,我尝试尽可能使用numpy数组)。我知道用numpy数组替换python列表可以加快速度,但是在我的程序中(不在此特定函数中)有些情况下,我必须使用append迭代地构建列表。而且这些必备列表是对象列表(不是浮点数或整数),因此即使我将这些对象列表转换为numpy数组,numpy也无济于事。

def compute_something(arr):      
      '''
            arr is received as a numpy array of ints and floats (I think python upcasts them to all floats,
             doesn’t it?).
             Inside this function, elements of arr are accessed using indexing (arr[0], arr[1], etc.), because
             each element of the array has its own unique use. It’s not that I need the array as a whole (as in
             arr**2 or sum(arr)). 
             The arr elements are used in several simple arithmetic operations involving nothing costlier than
             +, -, *, /, and numpy.log(). There is no other loop inside this function; there are a few if’s though.
             Inside this function, use is made of constants imported from other modules (I doubt the
             importing, as in AnotherModule.x is expensive). 
      '''
      for x in numpy.arange(float1, float2, float3):
              do stuff
      return a, b, c       # Return a tuple of three floats

编辑: 感谢所有的评论。这是函数的内部(为方便起见,我将变量名简称)。 ndarray数组arr中只有3个元素。您能提出任何建议吗?

def compute_something(arr):     
    a = Mod.b * arr[1] * arr[2]  + Mod.c
    max = 0.0
    for c in np.arange(a, arr[1] * arr[2] * (Mod.d – Mod.e),  Mod.f):
              i = c / arr[2]
              m1 = Mod.A * np.log( (i / (arr[1] *Mod.d)) +  (Mod.d/Mod.e))
              m2 = -Mod.B * np.log(1.0 - (i/ (arr[1] *Mod.d)) - (Mod.d /  
                    Mod.e))      
              V = arr[0] * (Mod.E - Mod.r * i / arr[1] - Mod.r * Mod.d  -                      
                  m1 – m2)
              p = c * V /1000.0
              if p > max:
                   max = p
                   vmp = V

    pen =   Mod.COEFF1 * (Mod.COEFF2 - max) if max < Mod.CONST else 0.0
    wo = Mod.COEFF3 * arr[1] * arr[0] + Mod.COEFF4 * abs(Mod.R5 - vmp) + 
         Mod.COEFF6 * arr[2]
    w = wo + pen
    return vmp, max, w

2 个答案:

答案 0 :(得分:1)

我建议使用range,因为它快大约2倍:

def python():
    for i in range(100000):
        pass
def numpy():
    for i in np.arange(100000):
        pass
from timeit import timeit
print(timeit(python, number=1000))
print(timeit(numpy, number=1000))

输出:

5.59282787179696
10.027646953771665

答案 1 :(得分:1)

Python支持代码分析。 (模块cProfile)。还可以选择使用line_profiler查找代码tool here中最昂贵的部分。 因此,您无需猜测哪一部分代码最昂贵。

在您假设的这段代码中,问题出在循环的用法上,该循环在对象类型之间生成许多转换。如果使用numpy,则可以向量化计算。

我尝试重写您的代码以向量化您的操作。您没有提供什么是Mod对象的信息,但是我希望它会起作用。

def compute_something(arr):     
    a = Mod.b * arr[1] * arr[2]  + Mod.c
    # start calculation on vectors instead of for lop
    c_arr = np.arange(a, arr[1] * arr[2] * (Mod.d – Mod.e),  Mod.f)
    i_arr = c_arr/arr[2]
    m1_arr = Mod.A * np.log( (i_arr / (arr[1] *Mod.d)) +  (Mod.d/Mod.e))
    m2_arr = -Mod.B * np.log(1.0 - (i_arr/ (arr[1] *Mod.d)) - (Mod.d /  
        Mod.e))      
    V_arr = arr[0] * (Mod.E - Mod.r * i_arr / arr[1] - Mod.r * Mod.d  -                      
        m1_arr – m2_arr)
    p = c_arr * V_arr / 1000.0
    max_val = p.max()  # change name to avoid conflict with builtin function
    max_ind = np.nonzero(p == max_val)[0][0]
    vmp = V_arr[max_ind]

    pen =   Mod.COEFF1 * (Mod.COEFF2 - max_val) if max_val < Mod.CONST else 0.0
    wo = Mod.COEFF3 * arr[1] * arr[0] + Mod.COEFF4 * abs(Mod.R5 - vmp) + 
         Mod.COEFF6 * arr[2]
    w = wo + pen
    return vmp, max_val, w