优化Python for循环

时间:2011-07-26 14:23:32

标签: 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]

N可以是大约40,000到120,000之间的任何值,并且是显示的所有列表(in1,in2,listN)的长度。

有没有人知道一些Python技巧来加快这个速度?我已经尝试过使用map,因为我知道它会尝试编译为更高效的代码,但速度大约慢了250ms。

由于

9 个答案:

答案 0 :(得分:9)

假设list1list2等都是数字,请考虑使用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

当然,如果你的list1list2等是非数字的(即浮点数或整数以外的python对象列表),那么这将无济于事。

答案 1 :(得分:2)

最初有一点错误(见下文)这些错误被更好地缓存了。

# 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

答案 2 :(得分:1)

优化取决于编译器,但您可以尝试一些事项。很高兴看到你正在分析代码!

您可以尝试:

  1. 首先在变量中存储in1[ctr]和其他多次使用的表达式(尽管大多数编译器已经可以执行此操作,谁知道)。

  2. 循环裂变(http://en.wikipedia.org/wiki/Loop_fission),以防您遇到缓存问题,在大规模阵列之间交替。

答案 3 :(得分:1)

从我注意到的情况来看,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个列表推导,只需评论)

编辑:一些时间示例。

答案 4 :(得分:0)

您可以尝试将其重写为多个循环:

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]

它可能不像听起来那么愚蠢。测量它。这种代码的一个问题可能是引用的局部性。如果你在内存中跳跃,你可以对抗缓存。您可能会发现单独压缩数组可能在缓存上更友好。

您还可以考虑在并行线程中执行它们。

答案 5 :(得分:0)

itertools.count速度更快。 map在Python 2中生成一个列表,您需要itertools.imap

答案 6 :(得分:0)

如果您有随机访问权限,地图只会有帮助。在您的情况下,列表是正确的数据类型。

尝试从循环中提取常量in1[0] - ctr * c1in2[0] - ctr * c2 。哎呀。 ctr不是常数。您可以尝试x1 = c1,然后尝试x1 + = c1,但我不认为添加速度比现在的CPU更快。

然后,您应该查看array moduleNumpy。不要在代码中创建list3,而是创建in1的副本,反转所有元素(*-1),然后将c3添加到每个元素。 array / Numpy的质量变异方法将使这一过程更快。

除此之外,如果不触及剩下的代码,你几乎无能为力。例如,您可以创建在必要时返回值的对象,而不是实际计算list3list4。但我的猜测是你需要所有的价值观,所以这无济于事。

如果速度不够快,则必须使用其他语言或编写C模块。

答案 7 :(得分:0)

使用numpy。循环被数组的一些差异所取代,其评估在C中完成。

答案 8 :(得分:0)

使用列表推导来计算列表内容比使用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)]

然后是timeit结果:

% 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