这次的重构的最初出发点是把原来的Yaf对象从PHP的原生对象,改成了自定义的对象:
结构重构
之前的Yaf版本,比如我们拿Yaf_Request为例,它原来是一个PHP的对象,它有属性,用起来也跟用户在PHP中定义的对象一样,比如在之前的版本的Yaf中,在扩展中如果要获取Request对象的uri属性,那么:
zend_read_property(yaf_request_ce, obj, “uri”, sizeof(“uri”)-1, 1, NULL);
去”读取“这个request对象的uri属性,一定程度上这个有一定的历史原因,毕竟这个Yaf是十年前写的。
而在3.2.0以后,Yaf_Request对象成为了一个纯粹的C结构体:
typedef struct { zend_uchar flags; zend_string *method; zend_string *module; zend_string *controller; zend_string *action; zend_string *base_uri; zend_string *uri; zend_string *language; zend_array *params; zend_array *properties; zend_object std;} yaf_request_object;
这样的定义如果大家看过我之前的文章深入理解PHP7内核之Object应该不会陌生,当用户在PHP脚本端获取到Yaf_Request对象的时候,对应的就是yaf_request_object.std这个成员。
这样以来,如果要获取uri的话,只需要根据偏移量,从std获取到yaf_request_object的地址,然后(yaf_request_object)->uri,就可以了。这样性能就会有较为明显的提升。
所有的Yaf提供的类都被重写成这种形式,但这样以来就需要模拟PHP的对象行为,让用户在PHP脚本中也想获取Yaf_Request对象的uri属性的时候,也可以正常访问。
另外,就是对缓存友好的提升,除了尽量降低内存使用以外,举一个例子:比如原来的时候,对全局变量的使用较多, 主要是指YAF_G(),这样对于一些很热点的函数来说,如果使用了太多的全局变量,对缓存也不够友好,于是把很多flag类的字段,收敛到了对象本身,比如对于Yaf_Loader, 他需要关注是否启用了use_spl_autoload, name_suffix, name_separtor以及lowercase_path等多个flag, 原来都是需要访问Yaf_G(). 现在统一都收敛到了yaf_loader_object->flags中。(实际上是复用了std.properties_table[0].u2.var_flags字段作为flags,没有新增内存)
#define YAF_LOADER_FLAGS(loader) YAF_VAR_FLAGS(loader->std.properties_table[0])typedef struct { zend_object std; zend_string *library; zend_string *glibrary; zend_array *properties;} yaf_loader_object
PSR-4 Autoloading
另外这次对自动加载器新增了部分PSR-4的支持,用户现在可以申明一个namespace的加载路径:
Yaf_Loader::getInstance()->registerNamespace(“\Foo\Bar”, “/var/lib/foo”);
那么所有在\Foo\Bar命名空间下的类就会从/var/lib/foo目录下查找:
\Foo\Bar\Dummy -> /var/lib/foo/Dummy.php
当然你也可以在配置文件中注册namespace path:
application.library.namespace./Foo/Bar=“/var/lib/foo”
不过为了兼容老的加载协议,下划线还是会被认为是目录分割符
\Foo\Bar\Ver_Don -> /var/lib/foo/Ver/Don.php
性能提升
这次的重构本质上还是为了性能提升, 那实际效果如何呢?
为了能较为真实的反应测试,我采用了Yaf代码下的demo生成器tool/cg/yaf_cg来生成一个完整的Yaf应用框架,然后通过cachegrind来详细对比。
tools/cg/yaf_cg -d yaf
首先php-7.4 , yaf-3.1.4, 跑1000次 :
valgrind --tool=cachegrind /path-to-php74/bin/php-cgi -T 1000 index.php
Elapsed time: 6.020169 sec==11203====11203== I refs: 232,666,798==11203== I1 misses: 3,669,999==11203== LLi misses: 10,163==11203== I1 miss rate: 1.57%==11203== LLi miss rate: 0.00%==11203====11203== D refs: 102,960,876 (61,041,772 rd + 41,919,104 wr)==11203== D1 misses: 5,065,700 ( 2,386,201 rd + 2,679,499 wr)==11203== LLd misses: 909,412 ( 363,850 rd + 545,562 wr)==11203== D1 miss rate: 4.9% ( 3.9% + 6.3% )==11203== LLd miss rate: 0.8% ( 0.5% + 1.3% )==11203====11203== LL refs: 8,735,699 ( 6,056,200 rd + 2,679,499 wr)==11203== LL misses: 919,575 ( 374,013 rd + 545,562 wr)==11203== LL miss rate: 0.2% ( 0.1% + 1.3% )==11203====11203== Branches: 44,906,888 (42,750,345 cond + 2,156,543 ind)==11203== Mispredicts: 4,112,611 ( 3,492,036 cond + 620,575 ind)==11203== Mispred rate: 9.1% ( 8.1% + 28.7% )
然后php-7.4 yaf-3.2.0 也跑1000次:
Elapsed time: 4.658264 sec==18814====18814== I refs: 171,236,262==18814== I1 misses: 2,940,070==18814== LLi misses: 10,007==18814== I1 miss rate: 1.71%==18814== LLi miss rate: 0.00%==18814====18814== D refs: 75,584,870 (44,264,393 rd + 31,320,477 wr)==18814== D1 misses: 4,302,787 ( 1,956,414 rd + 2,346,373 wr)==18814== LLd misses: 908,224 ( 363,351 rd + 544,873 wr)==18814== D1 miss rate: 5.6% ( 4.4% + 7.4% )==18814== LLd miss rate: 1.2% ( 0.8% + 1.7% )==18814====18814== LL refs: 7,242,857 ( 4,896,484 rd + 2,346,373 wr)==18814== LL misses: 918,231 ( 373,358 rd + 544,873 wr)==18814== LL miss rate: 0.3% ( 0.1% + 1.7% )==18814====18814== Branches: 32,043,848 (30,444,567 cond + 1,599,281 ind)==18814== Mispredicts: 3,033,847 ( 2,460,365 cond + 573,482 ind)==18814== Mispred rate: 9.4% ( 8.0% + 35.8% )
对比结果:
我们可以看到,无论执行的指令数,访问的内存数,以及cache miss, branch mispred都有很明显的下降。
当然,我们也能看到D1 Misses想对于DR的下降不成比例,这部分的主要原因还在于用户脚本访问Yaf类对象的时候导致的新增差异,后续还要想想怎么提升这块。
总体来说,这次的重构获得了20%以上的性能提升,但这个只是针对框架本身,对实际使用Yaf的业务来说,那肯定没这么明显了,具体多少大家可以自行测试。
好了,就简单介绍这么多,具体详细变化可以参看Changelog, 欢迎下载测试Yaf-3.2 后续我来慢慢完善文档。
前面也说了,这次对版本代码量变更很大,几乎相当于重写了:
新增代码量就将近9000,鉴于此,3.2.0目前发布为beta,虽然我做了很多的测试,也做到了能想到的最大可能去保证行为不发生变化,但还是不能保证完全对以前的版本兼容,大家如果在使用的过程中有任何问题,欢迎即时在Github反馈。
来源:https://www.laruence.com/2020/04/18/5832.html
·END·
PHP开源社区进阶·提升·涨薪