Press "Enter" to skip to content

Yaf 3.2 发布

经过俩周多的重构,终于一咬牙今天发布了Yaf 3.2.0 beta, 要不然一直在想各种可能的优化点,不停的写,没完了, 🙂

这次的重构的最初出发点是把原来的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%   )

对比结果:

3.1.4 3.2.0 Detal
IR 232,666,798 171,236,262 -26%
I1 Misses 3,669,999 2,940,070 -20%
DR 102,960,876 75,584,870 -27%
D1 Misses 5,065,700 4,302,787 -15%
Branch 44,906,888 32,043,848 -29%
MisPred 4,112,611 3,033,847 -26%

我们可以看到,无论执行的指令数,访问的内存数,以及cache miss, branch mispred都有很明显的下降。
当然,我们也能看到D1 Misses想对于DR的下降不成比例,这部分的主要原因还在于用户脚本访问Yaf类对象的时候导致的新增差异,后续还要想想怎么提升这块。

总体来说,这次的重构获得了20%以上的性能提升,但这个只是针对框架本身,对实际使用Yaf的业务来说,那肯定没这么明显了,具体多少大家可以自行测试。

好了,就简单介绍这么多,具体详细变化可以参看Changelog, 欢迎下载测试Yaf-3.2 后续我来慢慢完善文档。

前面也说了,这次对版本代码量变更很大,几乎相当于重写了:

新增代码量就将近9000,鉴于此,3.2.0目前发布为beta,虽然我做了很多的测试,也做到了能想到的最大可能去保证行为不发生变化,但还是不能保证完全对以前的版本兼容,大家如果在使用的过程中有任何问题,欢迎即时在Github反馈。

15 Comments

  1. soen
    soen June 3, 2020

    pecl有时候安装扩展真的好慢,甚至打不开,有没有那种国内的源,鸟哥

  2. Stone
    Stone May 19, 2020

    PSR-4 Autoloading失败了,找不到类,有成功的同学吗?

    • Stone
      Stone May 19, 2020

      不好意思,目录写错了,新项目用上了,谢谢鸟哥!

  3. lee
    lee May 15, 2020

    正好新项目,3.22我先品一下

  4. json
    json April 21, 2020

    问下大神,开发扩展,怎么避免内存泄露呢,用Valgrind好像逻辑走不到的地方检测不到泄露

    • Laruence
      Laruence April 22, 2020

      没啥好办法,一般来说,处理好引用计数就好了,用的时候+1,用完了-1,经验多了就好了

      • HH
        HH May 13, 2020

        PHP的扩展里面用emalloc,其实就算不释放,在请求结束后PHP也会释放掉所有申请的非持久型内存吧。

        • laruence
          laruence May 13, 2020

          理论上是,但是内存如果不及时释放,会导致峰值内存变大,间接还是会影响性能

          • HH
            HH May 13, 2020

            嗯,这个到是的,如果确实短时间内找不到问题所在,可以设置一下max_request,再配合php自动内存释放,可以临时应急用用。当然最好还是能开着debug mode做测试,把大部分路径都覆盖到,这样基本上问题不大了。

  5. 曹号
    曹号 April 20, 2020

    鸟哥,您好,我去年在您的 yaf 框架上面提交了 orm 的部分,不知道您咋看待,期待回复,俺微信 ****

    • laruence
      laruence April 20, 2020

      好,我加你, 我把你微信号马赛克了哈

    • trualy
      trualy May 4, 2020

      在用你的ycdb

  6. reatang
    reatang April 19, 2020

    诶!咱PHP的namespace Acme\Foo\Bar 不是反斜杠嘛?

Leave a Reply

Your email address will not be published. Required fields are marked *