我发现创建类比实例化类慢。
>>> from timeit import Timer as T
>>> def calc(n):
... return T("class Haha(object): pass").timeit(n)
<<After several these 'calc' things, at least one of them have a big number, eg. 100000>>
>>> calc(9000)
15.947055101394653
>>> calc(9000)
17.39099097251892
>>> calc(9000)
18.824054956436157
>>> calc(9000)
20.33335590362549
是的,创建9000个类需要16秒,并且在后续调用中变得更慢。
而且:
>>> T("type('Haha', b, d)", "b = (object, ); d = {}").timeit(9000)
给出了类似的结果。
但实例化不会受到影响:
>>> T("Haha()", "class Haha(object): pass").timeit(5000000)
0.8786070346832275
500秒内不到一秒的实例。
是什么让这个创作变得昂贵?
为什么创作过程会变慢?
修改
如何重现:
开始一个新的python进程,最初的几个“calc(10000)”在我的机器上给出了0.5的数字。并尝试一些更大的值,calc(100000),它甚至不能以10秒结束,中断它,而计算(10000),给出15秒。
修改
其他事实:
如果你在'calc'变慢之后gc.collect(),你可以在开始时获得'正常'的速度,但是后续调用的时间会增加
>>> from a import calc
>>> calc(10000)
0.4673938751220703
>>> calc(10000)
0.4300072193145752
>>> calc(10000)
0.4270968437194824
>>> calc(10000)
0.42754602432250977
>>> calc(10000)
0.4344758987426758
>>> calc(100000)
^CTraceback (most recent call last):
File "<stdin>", line 1, in <module>
File "a.py", line 3, in calc
return T("class Haha(object): pass").timeit(n)
File "/usr/lib/python2.7/timeit.py", line 194, in timeit
timing = self.inner(it, self.timer)
File "<timeit-src>", line 6, in inner
KeyboardInterrupt
>>> import gc
>>> gc.collect()
234204
>>> calc(10000)
0.4237039089202881
>>> calc(10000)
1.5998330116271973
>>> calc(10000)
4.136359930038452
>>> calc(10000)
6.625348806381226
答案 0 :(得分:32)
这可能会给你直觉:
>>> class Haha(object): pass
...
>>> sys.getsizeof(Haha)
904
>>> sys.getsizeof(Haha())
64
类对象 比该类的实例更复杂,更昂贵的结构。
答案 1 :(得分:24)
Ahahaha!疑难杂症!
这是否在没有this patch的Python版本上完成? (提示: IT是)
如果需要证明,请检查行号。
Marcin是对的:当结果看起来很糟糕时,你可能会得到一个棘手的基准。运行gc.disable()
,结果会自行复制。它只是表明当你禁用垃圾收集时会得到垃圾结果!
更清楚的是,运行长基准的原因是:
timeit
禁用垃圾收集,因此过大的基准测试需要更长时间(指数级)
timeit
没有恢复异常的垃圾收集
您使用异步异常退出长时间运行的进程,关闭垃圾回收
答案 2 :(得分:10)
快速执行以下功能:
def a():
class Haha(object):
pass
def b():
Haha()
给出:
2 0 LOAD_CONST 1 ('Haha')
3 LOAD_GLOBAL 0 (object)
6 BUILD_TUPLE 1
9 LOAD_CONST 2 (<code object Haha at 0x7ff3e468bab0, file "<stdin>", line 2>)
12 MAKE_FUNCTION 0
15 CALL_FUNCTION 0
18 BUILD_CLASS
19 STORE_FAST 0 (Haha)
22 LOAD_CONST 0 (None)
25 RETURN_VALUE
和
2 0 LOAD_GLOBAL 0 (Haha)
3 CALL_FUNCTION 0
6 POP_TOP
7 LOAD_CONST 0 (None)
10 RETURN_VALUE
相应。
从它的外观来看,它在创建一个类时会做更多的事情。它必须初始化类,将其添加到dicts,以及其他任何地方,而Haha()
只是调用函数。
正如你注意到当垃圾收集速度太慢而进行垃圾收集时,所以Marcin正确地说这可能是内存碎片问题。
答案 3 :(得分:3)
它不是:只有你的人为测试才会显示创建速度慢的类。事实上,正如@Veedrac在他的回答中所显示的那样,这个结果是一个禁用垃圾收集的timeit工件。
Downvoters:告诉我一个非常人为的例子,其中课程创建很慢。
在任何情况下,您的时间都会受到当时系统负载的影响。它们实际上只对几乎同时进行的比较有用。我获得了大约0.5秒的9000级创作。实际上,即使重复执行,它在ideone上也只有0.3秒:http://ideone.com/Du859。甚至没有上升趋势。
因此,总而言之,它在您的计算机上比其他计算机慢得多,并且其他计算机上没有向上趋势进行重复测试(根据您的原始声明)。测试大量实例化确实显示速度减慢,可能是因为该过程消耗了大量内存。您已经证明分配大量内存会减慢进程。做得好。
完整的想法代码:
from timeit import Timer as T
def calc(n):
return T("class Haha(object): pass").timeit(n)
for i in xrange(30):
print calc(9000)