我正在尝试优化一些代码,所以我想我会仔细研究我的瓶颈所在。我有四个功能,包括彼此:
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())
我恢复测试为.085秒,test2为.125秒,test3为.171秒。这让我有两种困惑。 1)为什么不测试3 .21秒,2)为什么它更短而不是我的问题变得更长?
答案 0 :(得分:3)
大量的时间需要列表生成“范围(1000000)”(假设您使用的是python 2.X)。在test3中,您只创建此列表一次。当你总结时间时,你总结了2次创建列表。
您可以使用探查器了解花费时间http://docs.python.org/2/library/profile.html
的内容答案 1 :(得分:2)
由于你没有给我们代码来重现你原来的问题,所以除了猜测之外很难做任何事情......但我可以在这里做一些猜测。
当你编写两个非常小的函数时,运行频率越高,就越有可能将字节码放在缓存中的两个函数,全局变量和本地字典等中。
另一方面,当你编写两个非常大的函数时,每次内部函数运行时,你很可能会将部分外部函数从缓存中推出,所以你最终会花费更多时间在缓存重新填充中解释你的代码。
最重要的是,您忘记了调用函数的成本。在Python中,这不仅仅是一个函数调用 - 您通常通过其全局名称调用函数,而LOAD_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),但可以。
最后,您还没有向我们展示您是如何进行计时的,如何重复测试,如何汇总结果等等,因此您的测试很可能无效