X Tutup
Skip to content

Latest commit

 

History

History
218 lines (179 loc) · 6.41 KB

File metadata and controls

218 lines (179 loc) · 6.41 KB

Pyhton 中的整数对象

PyIntObject 的定义:

typedef struct {
    PyObject_HEAD
    long ob_ival;
} PyIntObject;

ob_ival 存放 int 对象实际的值。

PyInt_Type 的定义:

PyTypeObject PyInt_Type = {
	PyObject_HEAD_INIT(&PyType_Type)
	0,
	"int",
	sizeof(PyIntObject),
	0,
	(destructor)int_dealloc,		/* tp_dealloc */
	(printfunc)int_print,			/* tp_print */
	0,					/* tp_getattr */
	0,					/* tp_setattr */
	(cmpfunc)int_compare,			/* tp_compare */
	(reprfunc)int_repr,			/* tp_repr */
	&int_as_number,				/* tp_as_number */
	0,					/* tp_as_sequence */
	0,					/* tp_as_mapping */
	(hashfunc)int_hash,			/* tp_hash */
        0,					/* tp_call */
        (reprfunc)int_repr,			/* tp_str */
	PyObject_GenericGetAttr,		/* tp_getattro */
	0,					/* tp_setattro */
	0,					/* tp_as_buffer */
	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
		Py_TPFLAGS_BASETYPE,		/* tp_flags */
	int_doc,				/* tp_doc */
	0,					/* tp_traverse */
	0,					/* tp_clear */
	0,					/* tp_richcompare */
	0,					/* tp_weaklistoffset */
	0,					/* tp_iter */
	0,					/* tp_iternext */
	int_methods,				/* tp_methods */
	0,					/* tp_members */
	0,					/* tp_getset */
	0,					/* tp_base */
	0,					/* tp_dict */
	0,					/* tp_descr_get */
	0,					/* tp_descr_set */
	0,					/* tp_dictoffset */
	0,					/* tp_init */
	0,					/* tp_alloc */
	int_new,				/* tp_new */
	(freefunc)int_free,           		/* tp_free */
};

int_as_number 结构体中定义了许多整数操作相关的函数,特别讨论一下其中的 int_add,代码如下:

static PyObject *
int_add(PyIntObject *v, PyIntObject *w)
{
	register long a, b, x;
	CONVERT_TO_LONG(v, a);
	CONVERT_TO_LONG(w, b);
	x = a + b;
	if ((x^a) >= 0 || (x^b) >= 0)
		return PyInt_FromLong(x);
	return PyLong_Type.tp_as_number->nb_add((PyObject *)v, (PyObject *)w);
}

int_add 这段代码有几个需要注意的点:

  • if ((x^a) >= 0 || (x^b) >= 0)

    判断两整数相加的结果是否溢出。整数相加只有在符号位相同时才会可能发生溢出,这里的 if 判断结果的符号位和两整数的符号位是否一致。

  • 如果相加结果溢出,则使用 PyLong_Type 的 nb_add 对两数求和。

创建整数对象

  • PyObject* PyInt_FromLong(long ival)

  • PyObject* PyInt_FromString(char *s, char **pend, int base)

  • PyObject*PyInt_FromUnicode(Py_UNICODE *s, int length, int base)

重点查看 PyInt_FromLong 的代码,其它两种最终都是调用 PyInt_FromLong 创建整数对象的。

小整数对象池

小整数的使用频率非常高,如果每次使用小整数时都需要初始化 PyIntObject,将导致频繁调用 malloc 分配内存,这显然会影响代码的效率。在 Python 中使用小整数对象池技术避免了这个问题。

Python 会预先将初始化好的小整数对象存放到一个数组中,代码如下:

// 定义小整数对象池

#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS		257
#endif
#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS		5
#endif
#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

在 Python2.5 中,正整数个数为 257,负整数的个数为 5。

大整数对象池

Python 内部使用 PyIntBlock 来管理大整数对象,一个 block 里面可以保存一定数量的整数。 PyIntBlock 的定义如下:

/* Integers are quite normal objects, to make object handling uniform.
   (Using odd pointers to represent integers would save much space
   but require extra checks for this special case throughout the code.)
   Since a typical Python program spends much of its time allocating
   and deallocating integers, these operations should be very fast.
   Therefore we use a dedicated allocation scheme with a much lower
   overhead (in space and time) than straight malloc(): a simple
   dedicated free list, filled when necessary with memory from malloc().

   block_list is a singly-linked list of all PyIntBlocks ever allocated,
   linked via their next members.  PyIntBlocks are never returned to the
   system before shutdown (PyInt_Fini).

   free_list is a singly-linked list of available PyIntObjects, linked
   via abuse of their ob_type members.
*/

#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;

PyInt_FromLong

PyInt_FromLong 的作用是将 C 语言中 long 类型的整数转换为 PyIntObject。代码如下:

[intobject.c]
PyObject* PyInt_FromLong(long ival)
{
     register PyIntObject *v;
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
     //[1] :尝试使用小整数对象池
     if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) {
        v = small_ints[ival + NSMALLNEGINTS];
        Py_INCREF(v);
        return (PyObject *) v;
    }
#endif
    //[2] :为通用整数对象池申请新的内存空间
    if (free_list == NULL) {
        if ((free_list = fill_free_list()) == NULL)
            return NULL;
    }
    //[3] : (inline) 内联 PyObject_New 的行为
    v = free_list;
    free_list = (PyIntObject *)v->ob_type;
    PyObject_INIT(v, &PyInt_Type);
    v->ob_ival = ival;
    return (PyObject *) v;
}

fill_free_list 代码如下:

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)
		q->ob_type = (struct _typeobject *)(q-1);
	q->ob_type = NULL;
	return p + N_INTOBJECTS - 1;
}

上面 fill_free_list 函数中,为了将 PyIntBlock.objects 转换成单向链表,对 objects 中元素进行强制类型转换,并将 ob_type 当做连接器(next),违反了类型安全。

X Tutup