PHP7相对于PHP5提升了将近一倍的性能,下面我将针对PHP7与PHP5的变量结构实现进行对比,分析为什么PHP7性能提升。当然变量结构修改只是PHP7性能提升中的一部分原因,还有很多原因比如HashTable结构变更、减少内存分配次数、多使用栈内存等等,本文只对变量结构进行分析。
本文源码基于PHP7.2.3以及PHP5.6.34
PHP作为弱类型语言,所有变量都是zval结构体来保存,PHP7与PHP5的zval结构也不相同,下面将分析两者zval结构的不同之处。
PHP5
PHP5的zval定义于Zend\zend.h
typedef struct _zval_struct zval; //此段代码在Zend\zend_types.h中
...
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;
zend_ast *ast;
} 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;
};
可以看到zval结构体有四个成员:
zvalue_value:共用体,储存变量的值
refcount__gc:引用计数,用于垃圾回收,默认值1
type:变量的具体类型
is_ref__gc:是否引用,用于垃圾回收,默认值0
zvalue_value共用体用来储存变量的值:
long lval成员用于储存整型与布尔型。
double dval成员用于储存 浮点型。
结构体str用于存储字符串,之所以使用结构体是用来储存字符串的长度,实时获取字符串的长度的时间复杂度是O(n),在PHP中字符串的操作非常频繁,这样能节省大量的时间。
HashTable *ht用来存储数组,PHP中的数组非常强大,这一切都基于哈希表结构。
zend_object_value obj用来存储对象。在面向对象编程中,这也是非常重要的一部分。
zend_ast *ast zend抽象语法树,zend内核使用。
zval结构体中的type成员有以下几种类型(代码位于Zend\zend.h):
/* data types */
/* All data types <= IS_BOOL have their constructor/destructors skipped */
#define IS_NULL 0 #define IS_LONG 1 #define IS_DOUBLE 2 #define IS_BOOL 3 #define IS_ARRAY 4 #define IS_OBJECT 5 #define IS_STRING 6 #define IS_RESOURCE 7 #define IS_CONSTANT 8 #define IS_CONSTANT_AST 9 #define IS_CALLABLE 10
PHP7
PHP7的zval定义于Zend\zend_types.h
typedef 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,/* active type */
zend_uchar type_flags,
zend_uchar const_flags,
zend_uchar reserved) /* call info for EX(This) */
} v;
uint32_t type_info;
} u1;
union {
uint32_t next; /* hash collision chain */
uint32_t cache_slot; /* literal cache slot */
uint32_t lineno; /* line number (for ast nodes) */
uint32_t num_args; /* arguments number for EX(This) */
uint32_t fe_pos; /* foreach position */
uint32_t fe_iter_idx; /* foreach iterator index */
uint32_t access_flags; /* class constant access flags */
uint32_t property_guard; /* single property guard */
uint32_t extra; /* not further specified */
} u2;
};
可以看到PHP7的代码相对PHP5的代码更加符合规范,所以更便于理解。
在_zval_struct结构体中:
zend_value共用体用于储存变量的值
u1共用体,用于存储变量的类型,以及一些辅助值
u2共用体,辅助值,由于内存对齐,_zval_struct结构体总共16字节大小,zend_value为8字节,u1为4字节,剩下的4字节防止浪费,用来作为辅助值,具体辅助用来干嘛代码的注释已经说明了。
u2共用体的辅助值成员一直在增加,我记得当时PHP7刚发布的时候才4个辅助值,现在已经9个了。
zend_value共用体就不用解释了,相比PHP5,PHP7的代码结构更加清晰、规范,一眼就能看出共用体的成员都用来储存什么类型的数据,我们可以发现PHP7 zend_value共用体大部分数据都是存储的指针,由于指针占的内存小,所以PHP7的zend_value占的内存比PHP5的zend_value要小。
对比
我们可以看到虽然PHP7的变量结构看起来更复杂,但是由于使用共用体,所以其实内存占用并不多,PHP5中的zval占用24个字节,PHP7中的zval占用16个字节。内存占用减少了1/3,而且PHP中的代码逻辑都是基于变量来实现的,所以对性能的影响非常大。可以看到PHP7中的代码规范比PHP5好了很多,并且PHP7的变量结构中辅助值的使用充分利用了内存,同时提高了性能。
结语
PHP7主要是优化性能,其实这个和互联网的发展也有关系。在之前,访问量和性能并不是最主要的,PHP凭借敏捷开发能在编程语言中占据一席之地。然而随着互联网的发展,网民数量的增多,以及各种编程语言的出现,大多数大型网站追求的是高并发以及访问速度,这个时候PHP的劣势就展示出来了。当然可以感受到PHP社区对这门语言的热情,包括PHP7的性能提升,swoole扩展的流行,以及正在开发中的PHP JIT版本,相信PHP会发展的越来越好。