PHP JIT

要点:
0、opcode虽然能解决一部分性能问题,但是依然解决不了需要Zend VM来运行产生的性能问题,所以要用JIT来直接执行机器码,进一步提升PHP脚本的执行效率。 img
1、PHP JIT依赖opcache扩展,或者说PHP JIT是opcache的一部分;
2、底层的编译字节码使用的是lua的dynasm代码生成器,LLVM其实也在考虑范围内,但由于编译速度不佳放弃了,LUA的就很好,并且已经成功应用到openresty中,PHP和LUA更像一些,没毛病;
3、配置中的opcache.jit_buffer_size实际上是用来存储编译后的字节码的空间长度(dasm_size),所以这个值开大了浪费,开小了则会导致频繁编译,需要根据实际业务场景搞一下; 因为JIT是opcache的一部分所以使用 opcache_get_status 函数就可以直接查看 jit 的使用容量了;(PS:经过测试如果设置该值为0,也可以有效禁用JIT)
4、严格模式+定义变量类型可以帮助JIT更快的完成代码生成工作;
5、JIT有两种模式 FUNCTION 和 TRACING,前者简单粗暴,直接编译函数,后者会复杂一些。根据一些阈值编译经常使用的代码块。opcache可选项:

  • disable 完全禁用jit;
  • off 关闭,但运行时可以开启;
  • on 开启,默认tracing模式;
  • tracing 开启tracing(1205)模式;
  • function 开启function(1254)模式;
  • 直接四位数字;
    7、opcache.jit允许JIT被用精细化的控制使用(12x5),关于x的含义;
  • 0:脚本加载时编译所有函数; //快
  • 1:首次执行时编译; //快,但慢于0,与计时方式有关
  • 2:分析第一个请求然后编译最热的函数; //超慢
  • 3:动态分析编译最热的函数; // 快
  • 5:动态分析编译最热的代码块; // 最快 但不是很稳

在实验1245的时候发生了一个问题;

php8 -d opcache.jit=1245  ./Zend/bench.php
Assertion failed: (0), function zend_jit_script, file /Users/abel/opt/php-8.0.0/ext/opcache/jit/zend_jit.c, line 3922.
[1]    30645 abort      php8 -d opcache.jit=1245 ./Zend/bench.php

源码中发现 @jit 方式并没有被使用,但明没有在特性发布页和相关8.0特性DOC中找到任何相关的资料,

#define ZEND_JIT_ON_SCRIPT_LOAD    0
#define ZEND_JIT_ON_FIRST_EXEC     1
#define ZEND_JIT_ON_PROF_REQUEST   2     /* compile the most frequently caled on first request functions */
#define ZEND_JIT_ON_HOT_COUNTERS   3     /* compile functions after N calls or loop iterations */
#define ZEND_JIT_ON_DOC_COMMENT    4     /* compile functions with "@jit" tag in doc-comments 猝。。发现并没有任何一个位置使用这个常量*/
#define ZEND_JIT_ON_HOT_TRACE      5     /* trace functions after N calls or loop iterations */

遂找到一个PR https://github.com/php/php-src/pull/6128 开发组应该是为PHP8.1留了一个商讨注解使用方式的 buffer(那个可丑可丑的#[Attribute]),总之这版是没有这个特性的。

tracing模式下如何被定义为“热”,todo:阈值细节有待深入;
opcache.jit_hot_loop(64)
opcache.jit_hot_func(127)
opcache.jit_hot_return (8)
opcache.jit_hot_side_exit(8)
8、开启JIT后应该注意什么?

  • 脚本程序最好分三个阶段执行,统一组织数据->统一处理数据->统一收集数据集。既将IO集中处理,也就是多拿一些数据,拼装好后统一进行业务运算,运算的过程中尽量不产生IO,逻辑部分的性能提升至少是10倍,如果不可避免的需要出现IO则尽量分好块,不要东一个西一个,tracing 模式下会自动侦测 hot code。
  • 纯预算脚本可以直接使用1205,或者在提取数据运算数据函数级别清晰的情况下直接使用1205,粗暴生成函数级的 machine code 。
  • FPM环境下最好使用 tracing 模式,fpm环境下基本每个action都是需要和数据库或者IO进行打交道的,理论上来说数据与逻辑隔离好后也可以带来不弱的效率提升(取决于具体的业务场景)。但FPM模式下始终避免不了 Loading for this request 的问题,更应该关注一下preloading的特性而不是JIT,总体而言还是尽量用swoole+hyperf。

Categories:

Updated: