php7之hashtable

作为php的重要数据结构,本博记录一下hashtable的结构,存储,hash定位等问题。

  • hashtable结构,是有56个字节组成等struct,如下:
struct _zend_array {
    zend_refcounted_h gc;
    union {
        struct {
            ZEND_ENDIAN_LOHI_4(
                zend_uchar    flags,
                zend_uchar    nApplyCount,
                zend_uchar    nIteratorsCount,
                zend_uchar    reserve)
        } v;
        uint32_t flags;
    } u;
    uint32_t          nTableMask;
    Bucket           *arData;
    uint32_t          nNumUsed;
    uint32_t          nNumOfElements;
    uint32_t          nTableSize;
    uint32_t          nInternalPointer;
    zend_long         nNextFreeElement;
    dtor_func_t       pDestructor;
};

最重要的就是arData,是指向Bucket类型的指针,Bucket的结构定义如下:

typeof struct _Bucket{
    zval val;
    zend_ulong h;
    zend_string *key;
} Bucket;

Bucket不再使用指向zval类型的指针,而是直接使用数据本身,arData的结构如下:
这里写图片描述
可以看出arData中数据都是顺序存储的,这样有几个好处,因为在内存中是相邻的,迭代器逻辑变得简单,直接便利arData,内存相邻,可以极大的利用CPU缓存。

  • 数据增加删除
    新增加数据都是顺序存入arData中,每新增一个,ht->nNumUsed++,当达到hashtable最大值,触发压缩扩容算法。数据删除时设置值为UNDEF的zval。

  • hash定位
    因为arData是顺序存储,所以经过hash的值不能作为arData的索引,而是通过一张转化表,将hash值转化为arData的索引,如图:
    这里写图片描述
    转化表是以arData起始指针为起点做镜面映射,所以在分配arData时,反射表也同时分配了。

  • hash冲突
    php采用链地址法解决hash冲突,但和普通链表不一样的是,直接读取整个arData,因为内存相邻,极大的利用cpu缓存,这也是php7性能提升的重要原因。

  • 转化表和哈希表初始化
    是分两次初始化,先只创建2个转化表节点,当有数据插入时才会完整初始化,这也是为什么极大节省了创建空数组开销的原因。

–hash表碎片重组扩容
因为顺序存储,删除的数据只标记为UNDEF,所以存在大量碎片,每次hash表扩容都是成倍增加,会浪费大量空间。

参考http://blog.csdn.net/heiyeshuwu/article/details/57083833

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值