在PyPy中使用__slots__

8 投票
2 回答
1241 浏览
提问于 2025-04-18 02:43

我有一段简单的代码,用来测量带有 __slots__ 的类的性能(这个代码来自 这里):

import timeit

def test_slots():
    class Obj(object):
        __slots__ = ('i', 'l')

        def __init__(self, i):
            self.i = i
            self.l = []

    for i in xrange(1000):
        Obj(i)

print timeit.Timer('test_slots()', 'from __main__ import test_slots').timeit(10000)

如果我在 python2.7 下运行这段代码,结果大约是 6 秒 - 嗯,确实比没有使用 slots 的情况快很多(而且也更省内存)。

但是,如果我在 PyPy 下运行这段代码(使用的是 2.2.1 - 64位的 Mac OS/X),它就开始占用 100% 的 CPU,而且“永远”不会返回结果(我等了好几分钟 - 还是没有结果)。

这是怎么回事?我在 PyPy 下应该使用 __slots__ 吗?

如果我给 timeit() 传入不同的数字,会发生什么呢:

timeit(10) - 0.067s
timeit(100) - 0.5s
timeit(1000) - 19.5s
timeit(10000) - ? (probably more than a Game of Thrones episode)

提前谢谢你。


注意,如果我使用 namedtuple,也会出现同样的情况:

import collections
import timeit

def test_namedtuples():
    Obj = collections.namedtuple('Obj', 'i l')

    for i in xrange(1000):
      Obj(i, [])

print timeit.Timer('test_namedtuples()', 'from __main__ import test_namedtuples').timeit(10000)

2 个回答

8

直接回答标题中的问题:__slots__ 在 PyPy 中对性能没有什么意义(但也不会造成伤害)。

12

在大约10,000次的timeit代码运行中,每次都会重新创建这个类。创建类的过程在PyPy中可能不是很高效;更糟糕的是,这样做可能会丢掉JIT(即时编译器)对之前类的优化信息。PyPy在JIT还没有完全准备好之前通常会比较慢,所以如果你频繁做需要它准备好的事情,就会严重影响性能。

解决这个问题的方法很简单,就是把类的定义放到被测试代码之外。

撰写回答