【问题标题】:Improve performance of function without parallelization在没有并行化的情况下提高函数的性能
【发布时间】:2014-02-17 20:03:06
【问题描述】:

几周前,我发布了一个问题 (Speed up nested for loop with elements exponentiation),abarnert 给出了很好的回答。这个问题与那个问题有关,因为它利用了所述用户建议的性能改进。

我需要提高一个函数的性能,该函数涉及计算三个因子,然后对它们应用指数。

这是我的代码的MWE

import numpy as np
import timeit

def random_data(N):
    # Generate some random data.
    return np.random.uniform(0., 10., N)

# Data lists.
array1 = np.array([random_data(4) for _ in range(1000)])
array2 = np.array([random_data(3) for _ in range(2000)])

# Function.
def func():
    # Empty list that holds all values obtained in for loop.    
    lst = []
    for elem in array1:
        # Avoid numeric errors if one of these values is 0.            
        e_1, e_2 = max(elem[0], 1e-10), max(elem[1], 1e-10)
        # Obtain three parameters.
        A = 1./(e_1*e_2)
        B = -0.5*((elem[2]-array2[:,0])/e_1)**2
        C = -0.5*((elem[3]-array2[:,1])/e_2)**2
        # Apply exponential.
        value = A*np.exp(B+C)
        # Store value in list.
        lst.append(value)

    return lst

# time function.
func_time = timeit.timeit(func, number=100)
print func_time

是否可以加速func 而不必重复并行化?

【问题讨论】:

  • 你在第一次迭代后从你的函数中返回,这是故意的吗?
  • 呸,没有抱歉的缩进。我马上修。感谢您的提醒!
  • 但是像这样,你只使用最后一次迭代的 A,B,C。
  • 您可能还想在函数内移动lst = []。像这样,你继续添加到同一个列表中,每次你做 timeit 时它都会继续增长。
  • 这篇文章应该会给你一些好的想法:ianozsvald.com/…

标签: python arrays performance numpy


【解决方案1】:

这是我目前所拥有的。我的方法是在 numpy 数组中进行尽可能多的数学运算。

优化:

  • 在numpy中计算As
  • 通过将 BC 拆分为因子来重新计算它们,其中一些可以在 numpy 中计算

代码:

def optfunc():
    e0 = array1[:, 0]
    e1 = array1[:, 1]
    e2 = array1[:, 2]
    e3 = array1[:, 3]

    ar0 = array2[:, 0]
    ar1 = array2[:, 1]

    As = 1./(e0 * e1)
    Bfactors = -0.5 * (1 / e0**2)
    Cfactors = -0.5 * (1 / e1**2)

    lst = []
    for i, elem in enumerate(array1):
        B = ((elem[2] - ar0) ** 2) * Bfactors[i]
        C = ((elem[3] - ar1) ** 2) * Cfactors[i]

        value = As[i]*np.exp(B+C)

        lst.append(value)

    return lst

print np.allclose(optfunc(), func())

# time function.
func_time = timeit.timeit(func, number=10)
opt_func_time = timeit.timeit(optfunc, number=10)
print "%.3fs --> %.3fs" % (func_time, opt_func_time)

结果:

True
0.759s --> 0.485s

此时我被卡住了。我设法完全没有 python for 循环,但它比上面的版本慢,原因我还不明白:

def optfunc():
    x = array1
    y = array2

    x0 = x[:, 0]
    x1 = x[:, 1]
    x2 = x[:, 2]
    x3 = x[:, 3]

    y0 = y[:, 0]
    y1 = y[:, 1]

    A = 1./(x0 * x1)
    Bfactors = -0.5 * (1 / x0**2)
    Cfactors = -0.5 * (1 / x1**2)

    B = (np.transpose([x2]) - y0)**2 * np.transpose([Bfactors])
    C = (np.transpose([x3]) - y1)**2 * np.transpose([Cfactors])

    return np.transpose([A]) * np.exp(B + C)

结果:

True
0.780s --> 0.558s

但是请注意,后者会为您提供np.array,而前者只会为您提供 Python 列表...这可能会导致差异,但我不确定。

【讨论】:

  • 非常感谢@Claudiu,我已经到了每一个优化都很重要的地步,所以你的回答肯定会对我有所帮助。我已从您的答案中删除了两个未使用的因素。干杯。
  • 次要评论:在我的问题中,我用max(elem[0], 1e-10)e_2 相同)定义了e_1,以避免任何一个为零时出现数字错误。在这种情况下你的回答会发生什么?
  • @Gabriel:注意我的输出打印了np.allclose(optfunc(), func()),得到了True——这意味着我们的两个答案都在一定的公差范围内。如果其中一个为零,我不确定您期望什么答案,因为您要除以零然后得到无穷大..但无论如何,如果您想要完全相同的行为,只需执行e0 = array1[:, 0]; e0[e0 < 1e-10] = 1e-10 将所有元素设置为@ 987654335@转1e-10
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-05
  • 2021-09-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多