优化Python for循环

优化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]

我有一个循环,这是我最大的时间吸为一个特定的功能,我想加快它。目前,这个单循环大约需要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可以是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的性能确实有了很大的提高。如果你说的是