Python簡単実験:長大なリストのメモリ消費量


2016年 12月 12日

Pythonは、整数もオブジェクトになることが分かった。
今回は、要素が整数の長大なリストを作って、メモリがどのように消費するかを調べてみる。

個々のオブジェクトのバイトサイズは sys.getsizeof( <オブジェクト> ) で求まることは既に説明した。
今回は、プロセスのメモリがどういう状態になっているかを調べてみよう。

今まで、ipython3 を使って説明していたが、今回は python3 で説明する。ipythonの方が対話性能を頑張っている分、余計にメモリを食ったり、もろもろのことがあるので、よりシンプルなpython3で実験する。

>>> import os
>>> os.getpid()
15896
次に、psutilをインポートし、pidからカレントプロセスのオブジェクトを作る。
>>> import psutil
>>> current_process = psutil.Process(os.getpid())
これで、プロセスの様々な情報を得る準備ができた。 今回はメモリに注目するので、プロセスのmemory_info()を使う。 それでもたくさんの情報が出てきてウザイので、rssだけを表示しよう。
>>> current_process.memory_info()
pmem(rss=12869632, vms=70696960, shared=5763072, text=4096, lib=0, data=6778880, dirty=0)
>>> current_process.memory_info().rss
12869632
ここまでが準備だ。
プロセス関連の情報を得る方法を知っていると、色々便利なので、いざという時に使えるようになっておくことは重要だ。 さて、では巨大なリストを作ってみよう。とりあえず、0が10000000個(1千万個、10M個)のリストを作ろう。 作る前と後で、使用メモリサイズを計測する。
>>> sz0=current_process.memory_info().rss
>>> a = [0]*10000000
>>> sz1=current_process.memory_info().rss
>>> sz1-sz0
80326656
要素数10Mのリストを作って、使用メモリが80MB増加しているので、1要素あたり8バイト増加していることになる。リストの中身は全部0なので、同じ0オブジェクトが使われている訳で、整数のオブジェクトは1つ作られただけで、消費メモリに影響しない。

次に、全て違う値を入れてみよう。
>>> b = [i for i in range(10000000)]
>>> sz2=current_process.memory_info().rss
>>> sz2-sz1
405139456
10M個に対して約400MB増加しているので、1要素あたり40バイト増加したことになる。 リストの要素自体のサイズは8バイトなので、整数オブジェクトのサイズは32バイトのようだ。
>>> c = [i for i in range(10000000)]
>>> sz3=current_process.memory_info().rss
>>> sz3-sz2
405295104
bと同じ内容のリストcを作って、ほぼ同じ400MB増加した。 あれ、bとcで使われている整数はまったく同じだから、cを作るとき、新たな整数オブジェクトを用意する必要はないので、リストaと同じ80MB程度しか増えないはずでは? はて、何が起きているんだろう。