php7性能原理,PHP7内存性能优化的思想精髓

2015年,PHP7的发布可以说是在技术圈里引起了不小的轰动,因为它的执行效率比PHP5直接翻了一倍。PHP7在内存方面,你是否知道作者都进行了哪些优化?几个核心结构体的改进只是表面上看起来优化的几个字节那么简单?让我们从几个核心的数据结构改进开始看起。

e18e353a10187e4bec1d3ea515a44ead.png

1PHP7 zval的变化1、php5.3中的zval:typedef unsigned int zend_object_handle;

typedef struct _zend_object_value {

zend_object_handle handle;

zend_object_handlers *handlers;

} zend_object_value;

typedef union _zvalue_value {

long lval;                  /* long value */

double dval;                /* double value */

struct {

char *val;

int len;

} str;

HashTable *ht;              /* hash table value */

zend_object_value obj;

} zvalue_value;

struct _zval_struct {

/* Variable information */

zvalue_value value;     /* value */

zend_uint refcount__gc;

zend_uchar type;    /* active type */

zend_uchar is_ref__gc;

};

我们这里只讨论64位操作系统下的情况。该zval_struct结构体中的由四个成员构成,其中zvalue_value稍微复杂一些,是一个联合体。联合体中最长的成员是一个指针加一个int,8+4=12字节。但是默认情况下,会进行内存对齐,故zval_struct会占用16字节。 那么。

_zval_struct总的字节 = value(16)+ refcount__gc(4)+ type(1)+ is_ref__gc(1)= 占用22字节。

最后再考虑下内存对齐,实际占用24字节。(如果算的有点晕话,感兴趣的同学可以写段简单的测试代码,使用sizeof查看一下)

2、PHP7.2中的zvaltypedef struct _zval_struct     zval;

typedef union _zend_value {

zend_long         lval;             /* long value */

double            dval;             /* double value */

zend_refcounted  *counted;

zend_string      *str;

zend_array       *arr;

zend_object      *obj;

zend_resource    *res;

zend_reference   *ref;

zend_ast_ref     *ast;

zval             *zv;

void             *ptr;

zend_class_entry *ce;

zend_function    *func;

struct {

uint32_t w1;

uint32_t w2;

} ww;

} zend_value;

struct _zval_struct {

zend_value        value;            /* value */

union {

struct {

ZEND_ENDIAN_LOHI_4(

zend_uchar    type,

zend_uchar    type_flags,

zend_uchar    const_flags,

zend_uchar    reserved)

} v;

int type_info;

} u1;

union {  ...... } u2;

};

7.2中的zval_struct结构体里由3个成员构成,其中zend_value看起来比较复杂,实际上只是一个8字节的联合体。 u1也是一个联合体,占用是4个字节。u2也一样。这样zval_struct就实际占用16个字节。

2PHP7 HashTable的变化1、PHP5.3里的HashTable:typedef struct _hashtable {

uint nTableSize;

uint nTableMask;

uint nNumOfElements;   //注意这里:浪费ing

ulong nNextFreeElement;

Bucket *pInternalPointer;       /* Used for element traversal */

Bucket *pListHead;

Bucket *pListTail;

Bucket **arBuckets;

dtor_func_t pDestructor;

zend_bool persistent;

unsigned char nApplyCount;

zend_bool bApplyProtection;

} 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字节

最终,总字节数 = 4+4+4+4(nNextFreeElement前面这四个字节会留空)+8+8+8+8+8+8+1+1+1 = 67字节。再加上结构体本身要对齐到8的整数倍,所以实际占用72字节。

2、PHP7.2里的HashTable:typedef struct _zend_array HashTable;

struct _zend_array {

zend_refcounted_h gc;

union {

struct {

ZEND_ENDIAN_LOHI_4(

zend_uchar    flags,

zend_uchar    nApplyCount,

zend_uchar    nIteratorsCount,

zend_uchar    consistency)

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

};s

在7.2里HashTablezend_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字节

总占用字节数 = 8+4+4+8+4+4+4+4+8+8 = 56字节,并且正好达到了内存对齐的状态,没有额外的浪费。另外还有PHP源代码里经常出镜的Buckets也从72下降到了32字节,这里我就不翻源代码了。

3优化思想精髓当当当,敲黑板,重点来了!我们看了两个核心数据结构的结构体变化,这上面的优化都是什么含义呢? 拿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才可以。 所以,在计算机里,56字节相对72字节实际上是翻倍的性能提升!!

第二、CPU的L1、L2、L3的容量是固定的几十K或者几十M。假设Cache的都是HashTable,那么Cache容量不变的条件下,能Cache住的HashTable将会翻倍,缓存命中率提升一大截。要知道L1命中后只需要1ns多一点的耗时,而如果穿透到内存的话可能就需要40多纳秒的延时了,整整差了几十倍。

所以PHP内核的作者大牛深谙CPU与内存的工作原理,表面上看起来只是几个字节的节约,但是实际上爆发出了巨大的性能提升!!

标签:字节,精髓,zend,value,zval,内存,PHP7,uint32,struct

来源: https://blog.51cto.com/15057824/2627784

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值