前面我们讨论了内存的工作原理,也进行了一些性能相关的测试。那么今天开始我们来看几个在实践中的应用。首先我们先从PHP开始。
2015年,PHP7的发布可以说是在技术圈里引起了不小的轰动,因为它的执行效率比PHP5直接翻了一倍。PHP7在内存方面,你是否知道作者都进行了哪些优化?你是否能够深层次理解到作者优化思路的精髓?
让我们从几个核心的数据结构改进开始看起。
PHP7 zval变化
1、php5.3中的zval:
我们这里只讨论64位操作系统下的情况。该zval_struct 结构体 中的由四个成员构成,其中zvalue_value稍微复杂一些,是一个联合体。联合体中最长的成员是一个指针加一个int,8+4=12字节。但是默认情况下,会进行内存对齐,故zval_struct会占用16字节。 那么
最后再考虑下内存对齐,实际占用24字节。(如果算的有点晕话,感兴趣的同学可以写段简单的测试代码,使用 sizeof 查看一下)
2、PHP7.2中的zval
7.2中的zval_struct结构体里由3个成员构成,其中zend_value看起来比较复杂,实际上只是一个8字节的联合体。 u1也是一个联合体,占用是4个字节。u2也一样。这样zval_struct就实际占用16个字节。
PHP7 HashTable 变化
1、PHP5.3里的HashTable:
再5.3里HashTable就是一个大struct, 有点小复杂,我们拆开了细说,
- uint nTableSize 4字节
- uint nTableMask 4字节
- uint nNumOfElements 4字节,
- ulong nNextFreeElement 8字节 注意这前面的4个字节会被浪费掉,因为nNextFreeElement的开始地址需要对齐
- Bucket *pInternalPointer 8字节
- Bucket *pListHead 8字节
- Bucket *pListTail 8字节
- Bucket **arBuckets 8字节
- dtor_func_t pDestructor 8字节
- zend_bool persistent 1字节
- unsigned char nApplyCoun 1字节
- zend_bool bApplyProtection 1字节
最终
再加上结构体本身要对齐到8的整数倍,所以实际占用72字节。
2、PHP7.2里的HashTable:
在7.2里HashTable
- zend_refcounted_h gc 看起来唬人,实际就是个 long ,占用8字节
- union… u 占用4字节
- uint32_t 占用4字节
- Bucket* 指针占用8字节
- uint32_t nNumUsed 占用4字节
- uint32_t nNumOfElements 占用4字节
- uint32_t nTableSize 占用4字节
- uint32_t nInternalPointer 占用4字节
- zend_long nNextFreeElement 占用8字节
- dtor_func_t pDestructor 占用8字节
占用56字节,并且正好达到了内存对齐的状态,没有额外的浪费。
优化思路精髓
我们看了两个核心数据结构的结构体变化,这上面的优化都是什么含义呢? 拿HashTable举例,貌似从72字节优化到了56字节,这内存节约的也不是特别多嘛,才20%多而已!
这中间其实有两个原因。
第一、CPU在向内存要数据的时候是以Cache Line为单位进行的,而我们说过Cache Line的大小就是64字节。回过头来看HashTable,在7.2里的56字节,只需要CPU向内存进行一次Cache Line大小的burst IO,就够了。而在5.3里的72字节,虽然只比Cache Line大了那么一丢丢,但是对不起,必须得进行两次burst IO才可以。 所以,在计算机里,72字节相对56字节实际上是翻倍的性能提升!!
第二、CPU的L1、L2、L3的容量是固定的几十K或者几M。假设Cache的都是HashTable,那么Cache容量不变的条件下,PHP7里能Cache住的HashTable数量将会翻倍,缓存命中率提升一大截。要知道L1命中后只需要1ns多一点的耗时,而如果穿透到内存的话可能就需要40多 纳秒 的延时了,整整差了几十倍。
所以PHP内核的作者大牛深谙CPU与内存的工作原理,表面上看起来只是几个字节的节约,但是实际上爆发出了巨大的性能提升!!
相关文章
本站已关闭游客评论,请登录或者注册后再评论吧~