Pythonで__slots__を使ってメモリを節約

先日こんな実験をして、大量のオブジェクトを作った時にはそのオブジェクトが持っている__dict__の1048バイトが無視できなくなってくることを確認した: Pythonでメモリ消費量のプロファイルを取る

今日はその解決編。先日のコードに1行書き足してみよう。__slots__で始まる行がそれだ。

from guppy import hpy
h = hpy()
N = 100000

class Hoge(object):
    __slots__ = ['x', 'y', 'z', 'a', 'b', 'c']
    def __init__(self):
        self.x = 1
        self.y = 1
        self.z = 1
        self.a = 1
        self.b = 1
        self.c = 1

x = [Hoge() for x in range(N)]

print h.heap()

これによって、変更前ではHogeの1インスタンスあたり1048 + 64 = 1112バイトを使っていたが変更後では96バイトしか使わなくなる。

変更前
$ time python tmp.py
Partition of a set of 225737 objects. Total size = 115386656 bytes.
 Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
     0 100000  44 104800000  91 104800000  91 dict of __main__.Hoge
     1 100000  44  6400000   6 111200000  96 __main__.Hoge
(...)
python tmp.py  3.90s user 0.23s system 89% cpu 4.596 total
変更後
$ time python tmp.py
Partition of a set of 125743 objects. Total size = 13788280 bytes.
 Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
     0 100000  80  9600000  70   9600000  70 __main__.Hoge
(...)
python tmp.py  1.51s user 0.07s system 90% cpu 1.747 total

この1行の追加で実行時間が半分以下に減っているのも興味深い。つまり実行時間の結構な部分をメモリの確保で使っていたということだ。

__slots__が何をするものかはこちらを参照: http://www.python.jp/doc/release/reference/datamodel.html#slots