为什么在Python中创建一个类比实例化一个类要慢得多?

时间:2012-04-09 11:18:27

标签: python performance class memory-management

我发现创建类比实例化类慢。

>>> 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

4 个答案:

答案 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)