优化Python for循环
我有一个循环,这是我最大的时间吸为一个特定的功能,我想加快它。目前,这个单循环大约需要400ms,而函数其余部分的执行大约需要610ms 代码是:优化Python for循环,python,optimization,Python,Optimization,我有一个循环,这是我最大的时间吸为一个特定的功能,我想加快它。目前,这个单循环大约需要400ms,而函数其余部分的执行大约需要610ms 代码是: for ctr in xrange(N): list1[ctr] = in1[ctr] - in1[0] - ctr * c1 list2[ctr] = in2[ctr] - in2[0] - ctr * c2 list3[ctr] = c3 - in1[ctr] list4[ctr] = c4 - in2[ctr]
for ctr in xrange(N):
list1[ctr] = in1[ctr] - in1[0] - ctr * c1
list2[ctr] = in2[ctr] - in2[0] - ctr * c2
list3[ctr] = c3 - in1[ctr]
list4[ctr] = c4 - in2[ctr]
N可以是40000到120000之间的任意值,是显示的所有列表(in1、in2、listN)的长度
有人知道一些Python技巧来加速这个过程吗?我已经尝试过使用map,因为我知道它试图编译成更高效的代码,但是速度慢了大约250毫秒
谢谢您可以尝试将其重新编写为多个循环:
for ctr in xrange(N):
list1[ctr] = in1[ctr] - in1[0] - ctr * c1
for ctr in xrange(N):
list2[ctr] = in2[ctr] - in2[0] - ctr * c2
for ctr in xrange(N):
list3[ctr] = c3 - in1[ctr]
for ctr in xrange(N):
list4[ctr] = c4 - in2[ctr]
这可能不像听起来那么愚蠢。测量一下。这种代码的一个问题可能是引用的局部性。如果你在内存中跳跃,你可以使用缓存。您可能会发现在缓存中单独压缩阵列可能更友好
您还可以考虑在并行线程中执行这些操作。更快。在Python2中生成一个列表,您需要它。只有当您具有随机访问权限时,映射才有帮助。在您的情况下,列表是正确的数据类型 尝试从循环中提取1[0]-ctr*c1和2[0]-ctr*c2中的常数。啊
ctr
不是一个常数。你可以先试试x1=c1,然后再试试x1+=c1,但我认为在今天的CPU上,加法并不比乘法快多少
那么,你应该看看手术室。不要像在代码中那样创建list3
,而是在1中创建一个副本,反转所有元素(*-1
),然后将c3
添加到每个元素中。array/Numpy的大规模变异方法将大大加快这一速度
除此之外,如果不涉及代码的其余部分,您几乎无能为力。例如,您可以创建在必要时返回值的对象,而不是实际计算list3
和list4
。但我的猜测是,你需要所有的价值观,所以这不会有帮助
如果速度不够快,您将不得不使用其他语言或编写C模块。使用。循环被几个不同的数组所代替,数组的计算是在C中完成的。优化取决于编译器,但有几件事可以尝试。很高兴看到您正在分析代码
您可以尝试:
先将存储在1[ctr]
中,然后将其他乘法表达式存储在变量中(尽管大多数编译器已经可以这样做了,谁知道呢)
环裂变(http://en.wikipedia.org/wiki/Loop_fission)如果存在缓存问题,请在大规模阵列之间交替使用
最初出现了一点错误(请参见下文),它们被更正确地缓存了
# These can be cached as they do not change.
base_in1 = in1[0]
base_in2 = in2[0]
for ctr in xrange(N):
# these are being looked up several times. Look-ups take time in almost every
# language. Look them up once and then use the new value.
cin1 = in1[ctr]
cin2 = in2[ctr]
list1[ctr] = cin1 - base_in1 - ctr * c1
list2[ctr] = cin2 - base_in2 - ctr * c2
list3[ctr] = c3 - cin1
list4[ctr] = c4 - cin2
(错误如下):
最初我认为这可以通过缓存常量来解决:
# these values never change
ctr1 = ctr * c1
ctr2 = ctr * c2
in10 = ctr1 + in1[0]
in20 = ctr2 + in2[0]
for ctr in xrange(N):
# these are being looked up several times. That costs time.
# look them up once and then use the new value.
cin1 = in1[ctr]
cin2 = in2[ctr]
list1[ctr] = cin1 - in10
list2[ctr] = cin2 - in20
list3[ctr] = c3 - cin1
list4[ctr] = c4 - cin2
<>但是正如提姆指出的,在我最初的尝试中,我错过了<代码> CTR <代码> .p> >假设<代码> List1, List2等,都是数值的,考虑使用NUMPY数组而不是列表。对于大的整数或浮点数序列,您将看到巨大的加速
如果你走这条路线,上面的循环可以这样写:
ctr = np.arange(N)
list1 = n1 - n1[0] - ctr * c1
list2 = n2 - n2[0] - ctr * c2
list3 = c3 - ctr
list4 = c4 - ctr
作为一个完整的独立计时示例:
import numpy as np
N = 100000
# Generate some random data...
n1 = np.random.random(N)
n2 = np.random.random(N)
c1, c2, c3, c4 = np.random.random(4)
ctr = np.arange(N)
list1 = n1 - n1[0] - ctr * c1
list2 = n2 - n2[0] - ctr * c2
list3 = c3 - ctr
list4 = c4 - ctr
当然,如果您的list1
、list2
等是非数字的(即除float或int之外的python对象列表),那么这将没有帮助。从我注意到的情况来看,python在连续的数学表达式方面很差,速度会大大降低。您最好的选择可能是像其他人所说的那样使用numpy,因此代码在C中运行。另一个Python优化尝试是使用列表理解。列表理解通常比地图快
in = in1[0]
list1 = [x - in - i * c1 for i, x in enumerate(in1)]
这种方法根本不涉及使用xrange(使用Python非常强大的迭代函数)
使用timeit的示例
>>> import timeit
>>> timeit.timeit(stmt="[x * 2 for x in xrange(1000)]", number=10000)
8.27007...
>>> timeit.timeit(stmt="map(lambda x: x * 2, xrange(1000))", number=10000)
19.5969...
>>> timeit.timeit(stmt="""lst=[0]*1000
for x in xrange(1000):
lst[x] = x * 2
""", number=10000)
13.7785...
# this last one doesn't actually do what you want it to do, but for comparison
# it's faster because it doesn't have to store any data from the computation
>>> timeit.timeit(stmt="for x in xrange(1000): x * 2", number=10000)
6.98619...
(如果您需要帮助构建其他4个列表理解,请发表评论)
编辑:一些timeit示例。使用列表理解来计算列表内容比使用for循环要快一些
import random
N = 40000
c1 = 4
c2 = 9
c3 = 11
c4 = 8
in1 = [random.randint(1, 50000) for _ in xrange(N)]
in2 = [random.randint(1, 50000) for _ in xrange(N)]
list1 = [None for _ in xrange(N)]
list2 = [None for _ in xrange(N)]
list3 = [None for _ in xrange(N)]
list4 = [None for _ in xrange(N)]
in1_0 = in1[0]
in2_0 = in2[0]
def func():
for ctr in xrange(N):
list1[ctr] = in1[ctr] - in1_0 - ctr * c1
list2[ctr] = in2[ctr] - in2_0 - ctr * c2
list3[ctr] = c3 - in1[ctr]
list4[ctr] = c4 - in2[ctr]
def func2():
global list1, list2, list3, list4
list1 = [(in1[ctr] - in1_0 - ctr * c1) for ctr in xrange(N)]
list2 = [(in2[ctr] - in2_0 - ctr * c2) for ctr in xrange(N)]
list3 = [(c3 - in1[ctr]) for ctr in xrange(N)]
list4 = [(c4 - in2[ctr]) for ctr in xrange(N)]
然后它的结果是:
% python -mtimeit -s 'import flup' 'flup.func()'
10 loops, best of 3: 42 msec per loop
% python -mtimeit -s 'import flup' 'flup.func2()'
10 loops, best of 3: 34.1 msec per loop
xrange()
比itertools.count()
:-/创建四个范围比创建一个范围要花费更多的时间,因此速度会较慢。使用四个线程可能会有所帮助。但我担心全局解释器锁会消耗大部分性能。这完全取决于数据,不是吗?由于GIL,使用线程没有意义(没有GIL的语言可能没有意义。400毫秒对于创建线程的成本来说可能仍然太短,但您必须分析才能弄清楚这一点)。正如@Joe所说的,试试看它是否有效(制作xrange的成本几乎为零)。将它们分成多个环路会使速度再慢100毫秒左右。不过,我还没有尝试穿线,我会让你知道它是如何影响它的。不是真的。所有操作都发生在内存中,没有线程执行任何阻塞IO,这意味着几乎没有并行执行。使用np代替ctr*c1
和ctr*c2
。linspace
可能更快。或者它可能不是,一些东西需要通过分析来确定。@Michael J.Barber-内部linspace
只做np.arange(0,num)*step+start
,所以我怀疑它会比ctr*c1
等更快。当然,正如你所说的,这最好通过分析来确定,我还没有检查它。无论如何,在上面的代码片段中仍然有很大的优化空间。这一次将总执行时间从600毫秒提高到了185毫秒。这对我来说已经足够快了,因为有几个FFT刚刚超过这个速度需要100毫秒,我知道我不能加快速度。谢谢实际上,像这样的查找实际上只在一些解释语言(如python)中花费,而不需要在例如Java中这样做。但是python的性能确实有了很大的提高。如果你说的是