Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 使用元素求幂加速嵌套for循环_Python_Performance_Numpy_Scipy - Fatal编程技术网

Python 使用元素求幂加速嵌套for循环

Python 使用元素求幂加速嵌套for循环,python,performance,numpy,scipy,Python,Performance,Numpy,Scipy,我正在编写一个大型代码,我发现自己需要加快一个特定的速度。我创建了一个MWE,如下所示: import numpy as np import time def random_data(N): # Generate some random data. return np.random.uniform(0., 10., N).tolist() # Lists that contain all the data. list1 = [random_data(10) for _ in

我正在编写一个大型代码,我发现自己需要加快一个特定的速度。我创建了一个
MWE
,如下所示:

import numpy as np
import time

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

# Lists that contain all the data.
list1 = [random_data(10) for _ in range(1000)]
list2 = [random_data(1000), random_data(1000)]

# Start taking the time.
tik = time.time()

list4 = []
# Loop through all elements in list1.
for elem in list1:

    list3 = []
    # Loop through elements in list2.
    for elem2 in zip(*list2):

        A = np.exp(-0.5*((elem[0]-elem2[0])/elem[3])**2)
        B = np.exp(-0.5*((elem[1]-elem2[1])/elem[3])**2)
        list3.append(A*B)

    # Sum elements in list3 and append result to list4.
    sum_list3 = sum(list3) if sum(list3)>0. else 1e-06
    list4.append(sum_list3)

# Print the elapsed time.
print time.time()-tik
list1
list2
的奇怪格式是因为这段代码就是这样接收它们的

显然,大部分时间都花在了
A
B
项的递归计算上

有没有什么方法可以在不需要并行化的情况下加速这段代码(我以前尝试过,但它给了我很多好处)?我愿意使用任何软件包,
numpy
scipy
,等等


添加

这是应用abarnert优化的结果,也是Jaime建议只进行一次幂运算的结果。优化后的功能在我的系统上平均快约60倍

import numpy as np
import timeit

def random_data(N):
    return np.random.uniform(0., 10., N).tolist()

# Lists that contain all the data.
list1 = [random_data(10) for _ in range(1000)]
list2 = [random_data(1000), random_data(1000)]

array1 = np.array(list1)
array2 = np.array(zip(*list2))


# Old non-optimezed function.
def func1():
    list4 = []
    # Process all elements in list1.
    for elem in list1:
        # Process all elements in list2.
        list3 = []
        for elem2 in zip(*list2):
            A = np.exp(-0.5*((elem[0]-elem2[0])/elem[3])**2)
            B = np.exp(-0.5*((elem[1]-elem2[1])/elem[3])**2)
            list3.append(A*B)
        # Sum elements in list3 and append result to list4.
        sum_list3 = sum(list3) if sum(list3)>0. else 1e-06
        list4.append(sum_list3)

# New optimized function.
def func2():
    list4 = []
    # Process all elements in list1.
    for elem in array1:

        # Broadcast over elements in array2.
        A = -0.5*((elem[0]-array2[:,0])/elem[3])**2
        B = -0.5*((elem[1]-array2[:,1])/elem[3])**2
        array3 = np.exp(A+B)

        # Sum elements in array3 and append result to list4.
        sum_list3 = max(array3.sum(), 1e-10)
        list4.append(sum_list3)


# Get time for both functions.
func1_time = timeit.timeit(func1, number=10)
func2_time = timeit.timeit(func2, number=10)

# Print hom many times faster func2 is versus func1.
print func1_time/func2_time

您希望逐渐将其从使用列表和循环转换为使用阵列和广播,首先获取最简单和/或时间最关键的部分,直到足够快

第一步是不要一次又一次地使用
zip(*list2)
(尤其是在使用Python2.x时)。当我们使用它时,我们不妨将其存储在一个数组中,并对
list1执行相同的操作-您现在仍然可以对它们进行迭代。因此:

array1 = np.array(list1)
array2 = np.array(zip(*list2))
# …
for elem in array1:
    # …
    for elem2 in array2:
这不会在我的机器上加快很多速度,从14.1秒到12.9秒,但它给了我们开始工作的地方

您还应该删除双重计算
总和(列表3)


同时,有点奇怪的是,您想要
值,为什么当您在顶部有一个NumPy依赖项时,此代码都是基于列表的?没有特别的原因,这就是此代码从另一段代码接收
list1
list2
的方式。关于
list3
list4
,这是我能找出如何填写它们的最佳方法。如果你认为这会有所不同,它们都可以转换成numpy数组。@Gabriel:当然会有所不同。这就是使用
numpy
-如果可以通过数组广播计算,则可以用C循环替换Python循环,并删除围绕每个算术计算的所有装箱/拆箱,这意味着您的代码通常会快4-400倍。发布好的MWE需要+1。这是一个很好的例子,可以让你投入工作,提出一个问题,然后得到一个精彩的答案。书签,以便我将来可以链接到它。最后一个旁注:你不应该尝试自己使用
time.time
。该模块(或者,如果您使用的是IPython,
%timeit
magic语句)确保选择正确的计时器,处理您甚至没有想到的一系列问题,允许您重复测试并正确总结它们,并使启动更简单。(当你的代码比你预期的要长100倍时,这通常没什么大不了的,但要养成经常使用
timeit
的习惯是值得的)求幂是昂贵的:而不是取
np.exp
两次,然后相乘,将这两个值相加,然后取
np.exp
@Jaime:我们已经有了99%的加速,我怀疑他还需要10%。(我估计快速跳过
exp
会将时间缩短到.79x,因此用一个而不是两个大概是.9x。)但我敢打赌,如果将其移出一个级别,您可以获得更大的改进(一次在所有行上广播
exp
,然后对所有行进行
sum
而不是每行执行一次),因此,我认为,如果现有的改进还不够,那么通过
array1
array4
进行广播应该是第一步,然后寻求优化它们内部的数学。如果你将全部内容矢量化,你将获得额外的20%,我不得不同意,在最初的99%中可以忽略不计。但这是我最讨厌的事情之一,比如如果你所做的事情已经足够让距离平方了,就不去求距离的平方根,我就是忍不住,为噪音感到抱歉…@Jaime:这不是噪音;在评论中进行额外的优化是值得的,以防OP需要它们,也只是为了让OP从中学习。我刚才解释了为什么我不认为它需要回答,这可能是一个很好的评论。加布里埃尔:人们经常这样说,而不考虑,但考虑到:这是值得2个小时的工作,以加快您的代码,例如,每运行180微秒的增益?这样做10000次,您最多节省了3598.2秒的时间,并且增加了错误的可能性,以及调试它所需的时间(特别是当您发现新版本更难理解时)。
sum_list3 = sum(list3)
sum_list3 = sum_list3 if sum_list3>0. else 1e-06
sum_list3 = max(array3.sum(), 1e-06)
# Broadcast over elements in list2.
A = np.exp(-0.5*((elem[0]-array2[:,0])/elem[3])**2)
B = np.exp(-0.5*((elem[1]-array2[:, 1])/elem[3])**2)
array3 = A*B

# Sum elements in list3 and append result to list4.
sum_list3 = max(array3.sum(), 1e-06)

list4.append(sum_list3)