14

Python 3.3 で実装されたPEP 412では、属性辞書の処理が改善され、クラス インスタンスのメモリ フットプリントが効果的に削減されます。 __slots__同じ目的で設計されたので、これ__slots__以上使用する意味はありますか?

自分で答えを見つけようとして、次のテストを実行しましたが、結果はあまり意味がありません。

class Slots(object):
    __slots__ = ['a', 'b', 'c', 'd', 'e']
    def __init__(self):
        self.a = 1
        self.b = 1
        self.c = 1
        self.d = 1
        self.e = 1  

class NoSlots(object):
    def __init__(self):
        self.a = 1
        self.b = 1
        self.c = 1
        self.d = 1
        self.e = 1

Python 3.3 の結果:

>>> sys.getsizeof([Slots() for i in range(1000)])
Out[1]: 9024
>>> sys.getsizeof([NoSlots() for i in range(1000)])
Out[1]: 9024

Python 2.7 の結果:

>>> sys.getsizeof([Slots() for i in range(1000)])
Out[1]: 4516
>>> sys.getsizeof([NoSlots() for i in range(1000)])
Out[1]: 4516

少なくとも Python 2.7 ではサイズが異なると予想していたので、テストに何か問題があると思います。

4

2 に答える 2

5

いいえ、PEP 412 は冗長にはなりません。__slots__


まず、Armin Rigo の言うとおり、正しく測定していません。測定する必要があるのは、オブジェクトのサイズ、値、および__dict__それ自体 (NoSlotsのみ) とキー (NoSlotsのみ) です。

または、彼が提案することを行うことができます:

cls = Slots if len(sys.argv) > 1 else NoSlots
def f():
    tracemalloc.start()
    objs = [cls() for _ in range(100000)]
    print(tracemalloc.get_traced_memory())
f()

これを OS X 上の 64 ビット CPython 3.4 で実行すると、 8824968forSlots25624872forが得られNoSlotsます。NoSlotsしたがって、インスタンスは 88 バイト、Slotsインスタンスは 256 バイトのように見えます。


これはどのように可能ですか?

__slots__と key-split の間にはまだ 2 つの違いがあるため__dict__です。

まず、辞書で使用されるハッシュ テーブルは 2/3 未満に保たれ、指数関数的に大きくなり、最小サイズになるため、余分なスペースを確保できます。また、適切にコメントされたソースを見ると、どのくらいのスペースがあるかを判断するのは難しくありません。5 つのスロット ポインターではなく、8 つのハッシュ バケットを使用することになります。

第二に、辞書自体は無料ではありません。これには、標準のオブジェクト ヘッダー、カウント、および 2 つのポインターがあります。大したことではないように聞こえるかもしれませんが、少数の属性しか持たないオブジェクトについて話している場合 (ほとんどのオブジェクトには少数の属性しかないことに注意してください…)、dict ヘッダーはハッシュ テーブルと同じくらい大きな違いを生む可能性があります。

そしてもちろん、あなたの例では値なので、ここで関係する唯一のコストはオブジェクト自体に加えて、5 つのスロットまたは 8 つのハッシュ バケットと dict ヘッダーであるため、違いはかなり劇的です。実生活では、それほど大きな利益に__slots__なることはめったにありません


最後に、PEP 412 は次のように主張していることに注意してください。

ベンチマークは、オブジェクト指向プログラムのメモリ使用量が 10% から 20% 削減されることを示しています

を使用する場所を考えてください__slots__。節約が非常に大きいため、使用しないの__slots__はばかげているか、残りの 15% を実際に絞り出す必要があります。または、誰が何を知っているかによってサブクラス化されると予想される ABC やその他のクラスを構築していて、サブクラスが節約を必要とする場合があります。いずれにせよ、そのような場合、 を使用せずに利益の半分__slots__、または 3 分の 2 を得るという事実でさえ、ほとんど十分ではありません。を使用する必要があります__slots__

真の勝利は、使用する価値がない場合にあり__slots__ます。無料で少額の利益を得ることができます。

(また、確実に を使いすぎているプログラマー__slots__もいます。運が良ければ、この変更により、それほど無関係ではない他の何かをマイクロ最適化することにエネルギーを注ぐようになる人もいるかもしれません。)

于 2014-08-03T05:08:07.757 に答える
4

問題はsys.getsizeof()、あなたが期待するものを返すことはめったにないということです。たとえば、この場合、オブジェクトのサイズを考慮せずに、オブジェクトの「サイズ」をカウントします__dict__。100,000インスタンスを作成する実際のメモリ使用量を測定して再試行することをお勧めします。

また、Python 3.3の動作はPyPyに触発されたものであり__slots__、違いはありません。したがって、Python3.3でも違いはないと思います。私の知る限り、今で__slots__はほとんど役に立たない。

于 2012-12-07T12:34:31.807 に答える