Python 解释器实现:int类型处理
目录

为了加快Python中int类型对象操作, CPython解释器针对int类型统一申请多块大的空间(block), 在block存储新的int对象。这样防止每个int对象都进行内存申请和释放操作。 CPython这样做的代价是block得不到释放,block个数由程序int对象最大使用量决定。。

首先定义PyIntBlock结构,其中包含数组来存储PyIntObject类型。

#define BLOCK_SIZE      1000    /* 1K less typical malloc overhead */
#define BHEAD_SIZE      8       /* Enough for a 64-bit pointer */
#define N_INTOBJECTS    ((BLOCK_SIZE - BHEAD_SIZE) / sizeof(PyIntObject))

struct _intblock {
struct _intblock *next;
PyIntObject objects[N_INTOBJECTS];
};

typedef struct _intblock PyIntBlock;

static PyIntBlock *block_list = NULL;
static PyIntObject *free_list = NULL;

这些block使用单向链表连接,组成block_list。 已分配的block在python程序执行结束前,都不会被释放。

free_list指的是一个block中PyIntObject组成的空闲链表,指向第一个空闲位置。 创建一个free_list,即block,并trick地使用ob_type指向前一个PyIntObject, 这样在block创建完成后,内部的PyIntObject对象从后向前组成一个链表:

static PyIntObject *
fill_free_list(void)
{
PyIntObject *p, *q;
/* Python's object allocator isn't appropriate for large blocks. */
p = (PyIntObject *) PyMem_MALLOC(sizeof(PyIntBlock));
if (p == NULL)
return (PyIntObject *) PyErr_NoMemory();
((PyIntBlock *)p)->next = block_list;
block_list = (PyIntBlock *)p;
/* Link the int objects together, from rear to front, then return
the address of the last int object in the block. */
p = &((PyIntBlock *)p)->objects[0];
q = p + N_INTOBJECTS;
while (--q > p)
Py_TYPE(q) = (struct _typeobject *)(q-1);
Py_TYPE(q) = NULL;
return p + N_INTOBJECTS - 1;
}

对于同一个int数值,block中可能存在多个对象。 CPython对于int类型第二个特殊对待的处理是, 对于[-5,257)之间的Int类型,只保存一个对象,这些对象都放在small_ints数组中。

#define NSMALLPOSINTS           257
#define NSMALLNEGINTS           5

#if NSMALLNEGINTS + NSMALLPOSINTS > 0
/* References to small integers are saved in this array so that they
can be shared.
The integers that are saved are those in the range
-NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
*/
static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];
#endif

所以,创建一个PyIntObject如下,取出small_ints中对象或插入到free_list中。:

PyObject *
PyInt_FromLong(long ival)
{
register PyIntObject *v;
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
if (-NSMALLNEGINTS <= ival &amp;&amp; ival < NSMALLPOSINTS) {
v = small_ints[ival + NSMALLNEGINTS];
Py_INCREF(v);
return (PyObject *) v;
}
#endif
if (free_list == NULL) {
if ((free_list = fill_free_list()) == NULL)
return NULL;
}
/* Inline PyObject_New */
v = free_list;
free_list = (PyIntObject *)Py_TYPE(v);
PyObject_INIT(v, &amp;PyInt_Type);
v->ob_ival = ival;
return (PyObject *) v;
}

当int对象引用计数为0时(small_ints中int对象永远达不到),释放int对象到free_list头:

static void
int_dealloc(PyIntObject *v)
{
if (PyInt_CheckExact(v)) {
Py_TYPE(v) = (struct _typeobject *)free_list;
free_list = v;
}
else
Py_TYPE(v)->tp_free((PyObject *)v);
}

发表评论