【问题标题】:Why there's the difference between creating class in python 2.7 and python 3.4 performance为什么在 python 2.7 中创建类和 python 3.4 性能之间存在差异
【发布时间】:2014-12-30 01:22:03
【问题描述】:
from timeit import Timer as T

def calc(n):
    return T("class CLS(object): pass").timeit(n)

print(calc(90000))
print(calc(90000))
print(calc(90000))

# python3.4
1.1714721370008192
1.0723806529986177
1.111804607000522

# python2.7
15.7533519268
16.7191421986
16.8397979736

为什么使用不同版本的python创建类的时间会有这么大的差异? 在同一台机器上测试:

  • i5-3450 CPU @ 3.10GHz
  • 8GB 内存

【问题讨论】:

  • 我想对象与它有关
  • 我已将问题缩小到timeit 调用gc.disable()。由于类总是会产生很多循环,因此禁用收集器意味着每个 CLS 实例都不能被释放。您可以通过调用gc.get_objects() 获取跟踪对象的列表。如果您不禁用 GC,而只是将每个 CLS 实例附加到列表以保持引用,则性能相似。看起来令人困惑的是为什么_PyObject_GC_Malloc 在这种情况下对于 Python 2 的表现要差得多。
  • 使用您的代码 sn-p,我在 Python 2.7.3 中得到 10.4693160057,在 Python 3.2.3 中得到 10.087862968444824。通过删除“对象”,我在 Python 2.7.3 中得到了 0.0276899337769。所以“对象”不太可能与它有任何关系。 Python 3.2 和 Python 3.4 之间一定有性能改进,这会有所不同。 Python 版本之间的类创建时间似乎也有很多细微差别,因此可能无法将其缩小到单个更改。
  • 你可能想看看dis模块,看看操作上有什么不同。

标签: python performance python-2.7 python-3.x


【解决方案1】:

timeit 禁用垃圾收集器,否则会破坏保持类对象活动的循环。因此,在timeit 完成之前,没有一个类会被释放。

object.__subclasses__() 通过弱引用的内部集合引用这些类。 tp_subclasses 的旧的基于列表的实现每次都会搜索整个列表以找到可以替换的死引用。对于每个附加的子类,此过程需要更多时间。另一方面,3.4中新的基于dict的设计可以在恒定时间内添加引用。见issue 17936


感谢@MichaelYounkin 指出这在 3.2 中也很慢。最初我试图将性能差异缩小到 2.x 和 3.x 之间的小对象分配器的变化,但在阅读了他的评论后,我发现即使是 3.3 也比 3.4 慢得多。所以我浏览了typeobject.c filelog 以查看最近的更改。

【讨论】:

    【解决方案2】:

    嗯,问题似乎在于 python 2.7 中的旧式与新式类。

    在 python 3.4 中你可以看到使用对象和不使用对象的区别只是符号的加载(不是那么重要):

    C:\TEMP>C:\Python34\python.exe
    Python 3.4.2 (v3.4.2:ab2c023a9432, Oct  6 2014, 22:15:05) [MSC v.1600 32 bit (Intel)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>>
    >>> def a():
    ...   class A(object): pass
    ...
    >>> def b():
    ...   class B(): pass
    ...
    >>> import dis
    >>> dis.dis(a)
      2           0 LOAD_BUILD_CLASS
                  1 LOAD_CONST               1 (<code object A at 0x020B8F20, file "<stdin>", line 2>)
                  4 LOAD_CONST               2 ('A')
                  7 MAKE_FUNCTION            0
                 10 LOAD_CONST               2 ('A')
                 13 LOAD_GLOBAL              0 (object)   # Extra step, not that expensive.
                 16 CALL_FUNCTION            3 (3 positional, 0 keyword pair)
                 19 STORE_FAST               0 (A)
                 22 LOAD_CONST               0 (None)
                 25 RETURN_VALUE
    >>> dis.dis(b)
      2           0 LOAD_BUILD_CLASS
                  1 LOAD_CONST               1 (<code object B at 0x020B8D40, file "<stdin>", line 2>)
                  4 LOAD_CONST               2 ('B')
                  7 MAKE_FUNCTION            0
                 10 LOAD_CONST               2 ('B')
                 13 CALL_FUNCTION            2 (2 positional, 0 keyword pair)
                 16 STORE_FAST               0 (B)
                 19 LOAD_CONST               0 (None)
                 22 RETURN_VALUE
    >>>
    

    在 Python 2.7 上,您还有另一个涉及 LOAD_TUPLE 的步骤:

    C:\Users\jsargiot\Downloads\so>C:\Python27\python.exe
    Python 2.7.8 (default, Jun 30 2014, 16:03:49) [MSC v.1500 32 bit (Intel)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>>
    >>> def a():
    ...   class A(object): pass
    ...
    >>> def b():
    ...   class B(): pass
    ...
    >>> import dis
    >>> dis.dis(a)
      2           0 LOAD_CONST               1 ('A')
                  3 LOAD_GLOBAL              0 (object)   # First extra step (just like 3.4)
                  6 BUILD_TUPLE              1            # Second extra step, expensive
                  9 LOAD_CONST               2 (<code object A at 01EAEA88, file "<stdin>", line 2>)
                 12 MAKE_FUNCTION            0
                 15 CALL_FUNCTION            0
                 18 BUILD_CLASS
                 19 STORE_FAST               0 (A)
                 22 LOAD_CONST               0 (None)
                 25 RETURN_VALUE
    >>> dis.dis(b)
      2           0 LOAD_CONST               1 ('B')
                  3 LOAD_CONST               3 (())
                  6 LOAD_CONST               2 (<code object B at 01EB8EC0, file "<stdin>", line 2>)
                  9 MAKE_FUNCTION            0
                 12 CALL_FUNCTION            0
                 15 BUILD_CLASS
                 16 STORE_FAST               0 (B)
                 19 LOAD_CONST               0 (None)
                 22 RETURN_VALUE
    >>>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-03
      • 1970-01-01
      • 2015-02-12
      • 2016-03-23
      • 2017-07-08
      相关资源
      最近更新 更多