2010年10月18日星期一

PyListObject与PyDictObject

昨天又接着阅读了PyListObject和PyDictObject两个内建对象的实现。其实在明白了python对象机制,阅读起着两个内建对象,的确简单了很多。其实我认为PyListObject和PyDictObject还是有很多相似之处的。


首先,PyListObject和PyDictObject的缓存池机制是一样的。都有对应的缓存池num_free_lists或者num_free_dicts,刚刚初始化的时候都是为空的数组(PyListObject * 或者PyDictObject *),却在对象销毁的时候,招兵买马。


其次,这两个对象保存基本数据的结构,都是一个数组。PyListObject的是ob_item(各个元素是PyObject *),PyDictObject是ma_smalltable(各个元素是PyDictEntry)。为什么都是数组,而不是链表?都是为了随机访问的时候访问,试想如果ob_item是个链表,那么l[2]如何迅速查询到,遍历链表是何等的慢。但是采用数组,在PyListObject插入或者删除的时候就麻烦了,大量的内存申请或者搬运工作。PyDictObject的ma_smalltable更不用说了,通过hash值以及探测函数,迅速搜索,那是dict的特性之一。


最后,这两个对象在初始化的时候,申请内存的时候,并不是用多少申请多少,而是在节省内存的前提下,多申请了一些,都是让内存管理更加高效。但是具体细节又有不同。


BTW,PyDictObject搜索的实现,完全是大学课程《数据结构》的那一套,有空复习下。

分布式系统工程化

  前天下午参加了珠三角技术沙龙2010Q4,对陈硕老师(@bnu_chensuo)讲的分布式系统的工程化开发方法很是感兴趣,也觉得陈硕老师讲的很入理,虽然我对分布式仅仅处于了解阶段,对于网络编程更是一窍不通。下面就谈谈通过这堂课,我的收获:

  • 分布式的场景:几十到几百台PC
  • 分布式的状况:还处于技术浪潮的前期,没有公认的成熟解决方案,虽有些开源组件但是可靠性有待考虑
  • 分布式的实现技术:勿在浮沙建高台,要是用成熟的技术,借鉴但不是照搬别人的“成功经验”
  • 分布式的设计原则:Desgin For Failure,高可用的关键在于不停机,恰恰在于可以随时重启。
  • 分布式需要实现监控:程序内置Naming Service,对各种异常报警,让人来处理
  • 分布式进程通信:为了能实现重启,所以只是用操作系统能自动回收的IPC(TCP),不用生命期大于进程的IPC(共享内存,mutex),不使用不能重建的IPC(pipe)
  • 心跳协议的设计:进程c依赖进程S,则进程S向进程C发送心跳(什么是依赖?为何这样?我还没有搞懂);不要另起线程发送心跳包,直接在工作线程发送,避免假心跳
  • 消息格式的设计:要想着以后的升级和兼容,不要用C struct或者bit fileds,考虑Google Protocal Buffer

2010年10月16日星期六

python源码初探

在阅读完《Apanche源代码全景分析》前6章后,对Apache体系结构以及模块开发有了比较深的认识,也开发了一个比较简单的Module(实现pv统计,用memcached保存page count,源代码链接),最近开始了新的阅读《python源码剖析》。在阅读完前3章(python对象初探,PyIntObject,PyStringObject),终于揭开python神秘的面纱,拉近我和python之间的距离。


python中一切皆对象,而对象机制的基石便是PyObject


typedef struct _object {
int ob_refcnt;
struct _typeobject *ob_type;
}PyObject;

ob_refcnt便是引用计数,ob_type便是类型信息。那么其他对象都可以在此基础上扩展,添加自身的信息,比如


typedef struct _object {
int ob_refcnt;
struct _typeobject *ob_type;
long ob_ival;
}PyIntObject;


当时阅读到这里,最疑惑的是python中的类型对象和实例对象都是怎么实现?stuct PyTypeObject的一个实例PyType_Type便是类型对象的基础,所有类型对象的ob_type都是指向PyType_Type,包括PyType_type本身,也就是说下面的表达式是成立的:


PyType_Type->ob_type == &PyType_Type

一切很简单,比如


a = 1

a是一个int类型,a便是struct PyIntObject数据,int便是PyInt_Type(PyInt_type并不是一个struct,也是一个struct PyTypeObject数据)。这便是python中“一切皆对象”的完美实现。


另外,还有几点说明,需要警惕下:



  • 在python的各种对象中,类型对象时超越引用计数的。类型对象“跳出三界外,不在五行中”,永远不会被析构。每个对象中指向类型对象的指针不会被视为类型对象的引用,就是说每个对象建立初始化化,并不会对类型对象的引用计数加一;销毁时并不会减一。估计类型对象的引用计数永远是1了。

  • 在python中,PyIntObject和PyStringObject都引用了内存对象池,因为对于这些用经常频繁用到的数据,会有大量的时间浪费是内存的申请和销毁上(如果不适用内存对象池的话)。PyIntObject不仅使用了小整数对象池(范围从NSMALLNEGINTS到NSAMLLPOSINTS),还有专门的PyIntBlock来用于申请大整数对象。因此对于一个整数,在NSAMLLNEGINTS和NSMALLPOSINTS之间,如果数值相等,其id便是相等,但是其他的id就不同了。PyStringObject也有字符串内存池,对于size为0或者1的字符串,都会经过intern机制缓存起来,其id肯定相同,但是大于1的就不一定了,这个或许跟创建PyStrngObject采用的方法有关吧,有可能是PySting_FromString,PyString_InternInPlace,也有可能是PyString_InternFromString。

  • 关于PyStringObject的一个效率问题。对于N个字符串的连接,如果采用直接相加的话,调用string_concat,就会有N-1的内存申请以及内存搬运工作。但是如果采用"".join([a, b, ..]的话,调用string_join,在开始时会一次性计算好内存,然后申请内存,将字符串拼接,生成PyStringObject即可。