Int 可以说是Python里最简单的对象了,我们也应该可以想象的到,它应该是把C里面的int或者long包装了一下,那么,仅仅是包装一下而已吗?下面是PyIntObject 的定义
typedef struct { PyObject_HEAD long ob_ival; } PyIntObject;
这里我们主要关注的是Python Int 的对象池的实现。可以想象,我们平时使用int是多么的频繁,随便一个循环就会遍历好多int对象,那如果都是用到了再malloc,用完了就free的话,我想这效率应该任何人都忍受不了吧,所以才有了整数类型的对象池这一玩意~
Python里面的整数分为小整数对象和大整数对象。想想也对,我们经常会写
for(int i=0;i<XXX;i++){}这样的代码,那么0,1,2,3这种比较小的整数我们会使用的更加频繁,要是他们能一直留在内存里,就不用一直malloc和free了,岂不是好事?那怎样算是大整数对象呢?100算大么?10000算大么?很难定义,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];这是小整数对象池的定义(在intObject.c里面),也就是说,Python里面默认的是[-5,257)算是小整数(Python2.7版本是这样,Python3.3版本里面就没有intObject的定义,int和Long直接结合在一起,上面的代码也直接定义在了longObject.c里面),在init的时候,Python直接把这些数存在内存里面,将他们的地址存在small_ints这个数组里面,一旦要用的时候就直接来这个数组拿,完全省去了malloc和free的开销。这个small_ints自init之后就与天地同寿了:)我们也可以自己修改源代码来自己定义小整数对象,然后自己build self-defined python......
那对于大整数对象有什么优化呢?Python 里面有这么一个结构:
struct _intblock { struct _intblock *next; PyIntObject objects[N_INTOBJECTS]; };
typedef struct _intblock PyIntBlock; static PyIntBlock *block_list = NULL; static PyIntObject *free_list = NULL;
PyIntObject 是作为数组存在PyIntBlock里面的,一个block大约是存82个PyIntObject对象(也可以自己修改再重新build)。block_list是用来维护所有给整数对象分配的内存空间,而里面的哪些空间是空的可用的,则由free_list在维护。当free_list为NULL的时候,也就是没有空余空间的时候,Python会运行fill_free_list()这个函数在malloc一个block出来. 要注意的是,小整数对象也是生存在由block_list维护的内存上面的。至于怎么在插入删除元素的时候维护free_list,和我们平时写的链表操作差不多,这里不详谈。
总而言之,当数字较小的时候,就直接使用一直存在的,早就init好的小整数对象池,当数字较大的时候,就用通用的整数对象池。