【发布时间】:2014-02-11 17:42:36
【问题描述】:
我正在编写一个大型代码,我发现自己需要加快其中的特定部分。我创建了一个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 术语。
有没有什么方法可以加快这段代码的速度而不必并行化它(我之前尝试过它,它给了我a lot of troubles)?我愿意使用任何软件包,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
【问题讨论】:
-
当你在顶部有一个 NumPy 依赖项时,为什么这段代码都是基于列表的?
-
没有特别的原因,这就是这段代码从另一段代码接收
list1和list2的方式。关于list3和list4,这是我能弄清楚如何填充它们的最佳方法。如果您认为这会有所作为,它们都可以转换为 numpy 数组。 -
@Gabriel:当然会有所作为。这就是使用
numpy的全部意义——如果您可以通过数组广播计算,则将 Python 循环替换为 C 循环,并删除每个算术计算周围的所有装箱/拆箱,这意味着您的代码通常从 4 -400 倍快。 -
+1 用于发布优秀的 MWE。一个很好的例子,说明了提出问题并因此得到了很好的答案。已添加书签,以便我以后可以链接到它。
-
最后一点说明:您不应该尝试使用
time.time自己计时。timeit模块(或者,如果你使用 IPython,%timeit魔法语句)确保选择正确的计时器,处理一系列你甚至不会想到的问题,让你重复测试并正确总结它们,并使事情更容易启动。 (当您的代码花费的时间比您预期的要长 100 倍时,这通常没什么大不了的,但值得养成始终使用timeit的习惯。)
标签: python performance numpy scipy