[Python] Listのcapacityを取得する
とある事情から、Pythonのlistがどれくらいのメモリを確保しているのか知りたくなったので、listのcapacityを取得する関数を作ってみました。
C言語拡張を利用して取得する
Pythonコードから確保済みメモリの大きさを知るすべを知らないので、CPython向けのC言語拡張を利用してリストの構造体(PyListObject
)から確保済みのメモリの大きさを取得してみたいと思います。
CPython3.9.0の時点でPyListObject
は以下のように定義されています。
typedef struct {
PyObject_VAR_HEAD
Py_ssize_t allocated; // 確保済みのメモリに入る要素数を管理するメンバ
} PyListObject;
https://github.com/python/cpython/blob/master/Include/cpython/listobject.h より抜粋
確保済みの要素数はallocated
メンバで管理されているので、この値を返却する関数を作成します。
こんな感じになりました。
static PyObject *
list_capacity(PyObject *self, PyObject *args) {
PyObject* o;
if (!PyArg_ParseTuple(args, "O", &o)){
return NULL;
}
if (!PyList_Check(o)) {
PyErr_SetString(PyExc_TypeError, "capacity excepted list object.");
return NULL;
}
PyListObject* list = (PyListObject*)o;
long allocated = list->allocated; // 確保済みの要素数を取得
PyObject* capacity = PyLong_FromLong(allocated);
if (capacity == NULL) {
return NULL;
}
return capacity;
}
list-reserve
折角作ったので、お手軽に利用できるようにライブラリにして公開してみました。
https://github.com/ChanTsune/list-reserve
pip install list-reserve
capacity
確保済みのメモリに入る最大の要素数を返却します。
from list_reserve import capacity
l = [1, 2, 3]
print(capacity(l)) # 3
返却する値は、確保済みのメモリに入る最大の要素数
なので実際に確保しているメモリのサイズは、64bit環境であればcapacity * 8
byte、32bit環境であれば要素数 * 4
byteになるはずです。
reserve
リストのメモリを確保します。
from list_reserve import reserve, capacity
l = []
reserve(l, 10)
print(len(l)) # 0
print(capacity(l)) # 10
ただし、リストの要素数の二倍以上確保してしまうと、Pythonのリストのメモリの自動管理の都合で、次の操作で確保済みのメモリが開放されてしまうので要注意です。
shrink_to_fit
メモリの大きさをリストの要素数まで切り詰めます。
from list_reserve import capacity, shrink_to_fit
l = list(range(100))
print(capacity(l)) # 118
shrink_to_fit(l)
print(capacity(l)) # 100
C++のstd::vector
のshrink_to_fit
メソッドと同様の働きをします。