Python
Python-dev

Python 3.8 では空dictが小さくなる

Python 3.7 では {} で作った空 dict よりも、 dict.clear() で作った空 dict の方が省メモリでした。

>>> import sys

>>> d = {}
>>> sys.getsizeof(d)
240
>>> d.clear()
>>> sys.getsizeof(d)
72

これは dict オブジェクトを新規作成するときに最小サイズのハッシュテーブルを確保し初期化するのに対して、 dict.clear() は shared key dict の仕組みを使って空 dict 専用の共有テーブル(書き込み禁止)を利用しているからです。

空の dict の数は無視できないくらいにはあるので、Python 3.8 で新規に dict オブジェクトを作るときも dict.clear() の結果と同じ共有領域を使うようにしました。

https://github.com/python/cpython/pull/1080

>>> import sys

>>> sys.getsizeof({})
64

なお Python 3.7 の dict.clear() のときよりも8バイト小さくなっていますが、これは GC ヘッダを削減した効果で、これも私の功績です(ドヤ)。

参照: https://speakerdeck.com/methane/compact-gc-head

さて、パッチ自体はすごくシンプルなんですが、もちろんコミットするのは簡単ではありません。新規オブジェクトを作ったあとすぐに要素を挿入するケースも多くあり、その場合は最初から専有の、要素を追加できる状態の領域を持っていたほうが高速です。

そのため python/peformance というベンチマークスイートを実行し、性能が悪化しているベンチマークがあったら Linux の perf を駆使してそれが他の環境要因なのか今回の変更によるものなのかどうかを確認するといった地道な作業を裏でしています。