库 itertools 与 python 代码的性能比较

2023-12-10

作为我问题的答案查找两个列表相同的从 1 开始的位置我得到了使用 C 库 itertools 来加快速度的提示。

为了验证我使用 cProfile 编写了以下测试:

from itertools import takewhile, izip

def match_iter(self, other):
    return sum(1 for x in takewhile(lambda x: x[0] == x[1],
                                        izip(self, other)))

def match_loop(self, other):
    element = -1
    for element in range(min(len(self), len(other))):
        if self[element] != other[element]:
            element -= 1
            break
    return element +1

def test():
    a = [0, 1, 2, 3, 4]
    b = [0, 1, 2, 3, 4, 0]

    print("match_loop a=%s, b=%s, result=%s" % (a, b, match_loop(a, b)))
    print("match_iter a=%s, b=%s, result=%s" % (a, b, match_iter(a, b)))

    i = 10000
    while i > 0:
        i -= 1
        match_loop(a, b)
        match_iter(a, b)

def profile_test():
    import cProfile
    cProfile.run('test()')

if __name__ == '__main__':
    profile_test()

函数 match_iter() 使用 itertools,函数 match_loop() 是我在使用普通 python 之前实现的函数。

函数 test() 定义了两个列表,打印包含两个函数结果的列表以验证其是否正常工作。两个结果的预期值都是 5,即列表的长度相等。然后它对这两个函数循环 10,000 次。

最后使用 profile_test() 对整个事情进行分析。

我了解到 izip 没有在 python3 的 itertools 中实现,至少在我使用的 debian wheezy 中没有实现。所以我用 python2.7 运行测试

结果如下:

python2.7 match_test.py
match_loop a=[0, 1, 2, 3, 4], b=[0, 1, 2, 3, 4, 0], result=5
match_iter a=[0, 1, 2, 3, 4], b=[0, 1, 2, 3, 4, 0], result=5
         180021 function calls in 0.636 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.636    0.636 <string>:1(<module>)
        1    0.039    0.039    0.636    0.636 match_test.py:15(test)
    10001    0.048    0.000    0.434    0.000 match_test.py:3(match_iter)
    60006    0.188    0.000    0.275    0.000 match_test.py:4(<genexpr>)
    50005    0.087    0.000    0.087    0.000 match_test.py:4(<lambda>)
    10001    0.099    0.000    0.162    0.000 match_test.py:7(match_loop)
    20002    0.028    0.000    0.028    0.000 {len}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
    10001    0.018    0.000    0.018    0.000 {min}
    10001    0.018    0.000    0.018    0.000 {range}
    10001    0.111    0.000    0.387    0.000 {sum}

让我想知道的是,查看 cumtime 值,我的普通 python 版本的 10,000 个循环的值为 0.162 秒,而 match_iter 版本需要 0.434 秒。

一方面,Python 非常快,非常棒,所以我不必担心。但是,C 库完成这项工作所需的时间是简单 Python 代码的两倍以上,这种说法是否正确?还是我犯了一个致命的错误?

为了验证我还使用 python2.6 运行了测试,这似乎更快,但循环和 itertools 之间具有相同的差异。

谁有经验并且愿意提供帮助?


  • 首先,感谢您对某些事情的实际计时。
  • 其次,可读性通常比编写快速代码更重要。如果你的代码运行速度快了 3 倍,但你每 3 周花 2 周的时间来调试它,那么就不值得你花时间。
  • 第三,你还可以使用timeit对小段代码进行计时。我发现这种方法比使用更容易profile. (profile不过对于发现瓶颈很有好处)。

itertools一般来说,速度相当快。然而,尤其是在这种情况下,您的takewhile会减慢速度,因为 itertools 需要为沿途的每个元素调用一个函数。 python 中的每个函数调用都有一定量的与之相关的开销,因此这可能会稍微减慢您的速度(首先还有创建 lambda 函数的成本)。请注意sum使用生成器表达式也会增加一点开销。但最终,在这种情况下,基本循环似乎总是获胜。

from itertools import takewhile, izip

def match_iter(self, other):
    return sum(1 for x in takewhile(lambda x: x[0] == x[1],
                                        izip(self, other)))

def match_loop(self, other):
    cmp = lambda x1,x2: x1 == x2

    for element in range(min(len(self), len(other))):
        if self[element] == other[element]:
            element += 1
        else:
            break

    return element

def match_loop_lambda(self, other):
    cmp = lambda x1,x2: x1 == x2

    for element in range(min(len(self), len(other))):
        if cmp(self[element],other[element]):
            element += 1
        else:
            break

    return element

def match_iter_nosum(self,other):
    element = 0
    for _ in takewhile(lambda x: x[0] == x[1],
                       izip(self, other)):
        element += 1
    return element

def match_iter_izip(self,other):
    element = 0
    for x1,x2 in izip(self,other):
        if x1 == x2:
            element += 1
        else:
            break
    return element



a = [0, 1, 2, 3, 4]
b = [0, 1, 2, 3, 4, 0]

import timeit

print timeit.timeit('match_iter(a,b)','from __main__ import a,b,match_iter')
print timeit.timeit('match_loop(a,b)','from __main__ import a,b,match_loop')
print timeit.timeit('match_loop_lambda(a,b)','from __main__ import a,b,match_loop_lambda')
print timeit.timeit('match_iter_nosum(a,b)','from __main__ import a,b,match_iter_nosum')
print timeit.timeit('match_iter_izip(a,b)','from __main__ import a,b,match_iter_izip')

但请注意,最快的版本是循环+itertools 的混合体。这个(显式)循环izip也恰好更容易阅读(在我看来)。所以,我们可以由此得出结论takewhile是缓慢的部分,不一定itertools一般来说。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

库 itertools 与 python 代码的性能比较 的相关文章

随机推荐