1

我正在尝试优化一些代码,所以我想我会仔细研究我的瓶颈所在。我有四个相互包装的函数,例如:

return f1(f2(f3(f4())))

所以我单独测试了每一个,也测试了一个整体。当单独时,我基本上预先计算了前一个函数。但是,我认为它们会加起来总时间。但他们没有,当我把它们结合起来时,它显着增长。所以我决定在更小的范围内看它。我写这个来测试

def f1():
    return 2

def f2(num):
    return num*num

def test():
    for i in range(1000000):
        f1()
def test2():
    for i in range(1000000):
        f2(2)
def test3():
    for i in range(1000000):
        f2(f1())

我得到了0.085秒的测试,0.125秒的test2和0.171秒的test3。这在两个方面让我感到困惑。1)为什么不是 test3 .21 秒,以及 2)为什么它更短,而不是我的问题变得更长?

4

2 回答 2

3

大量时间需要列表生成“范围(1000000)”(假设您使用的是 python 2.X)。在 test3 中,您只创建此列表一次。当您对时间求和时,您将创建列表的 2 次相加。

您可以使用分析器来了解需要时间http://docs.python.org/2/library/profile.html

于 2013-06-18T23:09:11.980 回答
2

由于您没有给我们提供重现原始问题的代码,因此除了猜测之外很难做任何事情……但我可以在这里做出一些猜测。


当您编写两个非常小的函数时,运行它的频率越高,您就越有可能将这两个函数的字节码、全局字典和局部字典等都放在缓存中。

另一方面,当您组合两个非常大的函数时,您很可能每次内部函数运行时都会将部分外部函数推出缓存,因此您最终会花费更多时间在缓存重新填充上而不是实际解释您的代码.


最重要的是,您忘记了调用函数的成本。在 Python 中,这不仅仅是一个函数调用——你通常通过它们的全局名称调用函数,并且 aLOAD_GLOBAL可能非常慢。如果你写过这样的玩具作文:

def test3():
    for i in range(1000000):
        f2(f1())

……您不必像执行以下操作那样频繁地为该查找付费:

def f2():
    return 2 * f1()
def test3():
    for i in range(1000000):
        f2()

...但是您可以通过复制f1到适当的locals. 对于上面的两个例子:

def test3():
    _f1 = f1
    for i in range(1000000):
        f2(_f1())

def f2(_f1=f1):
    return 2 * _f1()
def test3():
    for i in range(1000000):
        f2()

您的测试功能包括您正在计时的设置成本。

例如,如果您使用的是 Python 2.x,则range(1000000)可能会占用总时间的很大一部分。但test1 + test2只做两次,而test3只做一次。因此,节省的费用test3足以在玩具测试中引起注意,这是完全合理的。但是在你的实际测试中,每个循环的时间要长 100 倍,range调用的成本是微不足道的。

还值得注意的是,如果您创建了足够的内存,最终可能会触发malloc调用甚至虚拟机交换——这分别是缓慢和令人麻木的缓慢,而且与通常的运行成本相比,它们的可变性和不可预测性都大得多循环中的代码。这可能不是仅仅创建和销毁几个 1M 项目列表(应该是 20-80MB 的数量级)的问题,但它可能是。


最后,您还没有向我们展示您如何进行计时、如何重复测试、如何汇总结果等,因此您的测试很可能是无效的。

于 2013-06-18T23:25:58.703 回答