python语法类似PHP,python的语法类似于php
PHP 8的Just In Time是Opcache扩展的一部分,目的是在运行时将一些操作码编译成CPU指令。也就是说,使用JIT时,Zend VM不需要解释具体的操作码,这些指令直接作为CPU级指令执行。
PHP 8 JIT
PHP 8带来的最受欢迎的特性之一是Justintime(JIT)编译器。许多博客和社区都在谈论它,这无疑吸引了很多关注,但我们发现迄今为止JIT内部的细节很少。
经过多次研究和放弃,我决定自己去查PHP源代码。结合自己的C语言知识和目前收集的所有零散资料,提出了这篇文章。我希望这也有助于更好地理解PHP的JIT。
简单来说,如果JIT按预期运行,代码就不会在Zend VM上运行,而是直接作为一组CPU级指令运行。
这是JIT的核心思想。
然而,为了更好地理解这一点,我们需要考虑php的内部角色。并不复杂,但需要一些介绍。
如何执行PHP代码?
我知道php是一种解释性语言。但是这到底是什么意思呢?
每次执行PHP代码(比如一个代码片段或者整个web APP应用程序),都必须经过PHP解释器。
最常用的是PHP FPM和CLI解释器。
他们的工作很简单。接收php代码,解释它,然后把结果倒着吐出来。
这通常发生在所有语言的口译中。虽然有些语言可能会删除一些步骤,但总体思路是一样的。
在PHP中,情况如下:
PHP代码将被读取并转换成一组关键字“标签”。这个过程使解释器能够理解程序的哪个部分写了哪个代码。第一步叫做词法分析或符号化。
如果令牌存在,PHP解释器将分析这个令牌集合并试图理解它们。因此,抽象语法树(AST)是通过称为分析的过程生成的。AST是一组指示要执行的操作的节点。比如“echo1”其实应该是指“打印1 ^ 1的结果”,或者更实际一点,应该是指“打印一个操作,操作是1 ^ 1”。
3.有了Ast,理解操作和优先级就容易多了。将这个树转换成可执行文件需要中间表示(IR),在PHP中称为opcode。将AST转换成操作码的过程称为编译。
现在,如果有操作码,剩下的就是执行代码了。PHP有一个名为Zend VM的引擎,可以接收和运行操作码列表。运行完所有操作码后,Zend VM存在,程序退出。
使用下图中的标记会使步骤更加清晰。
直接,希望你能理解。但是瓶颈就在这里:如果php代码变化不那么频繁,那么每次运行代码时进行词法分析又有什么意义呢?
最后,你只关心操作码,对吗?没错!因此,Opcache扩展是存在的。
Opcache扩展
Opcache扩展是PHP自带的,通常没有理由禁用它。如果使用PHP,必须打开Opcache。
它旨在为操作码在内存中添加一个共享缓存层。它的任务是缓存AST新生成的操作码,从而跳过词法分析和分析阶段。
Opcache扩展进程映像:
解释使用PHP Opcache的过程。如果文件已经被解析,php将检索缓存的操作码,而不是重新解析。
Opcache完美地跳过了词法分析、语法分析和编译的步骤。
注意:这是PHP 7.4预加载功能的亮点。这可以指示PHP FPM解析代码库,将其转换为操作码,并在执行任何操作之前缓存它。
JIT编译器有效地做什么?
如果Opcache能更快的获取操作码,直接传递给Zend VM,JIT就必须跳过Zend VM,让它运行。
Zend VM是用C写的程序,作为操作码和CPU本身之间的一层。JIT在运行时生成编译好的代码,这样php就可以跳过Zend VM直接去找CPU。理论上,我们应该从中获得性能提升。
起初,我觉得很奇怪,因为为了编译机器码,我需要为每种类型的架构创建一个非常具体的实现。但其实挺有道理的。
PHP的JIT实现使用了一个叫做动态汇编器(dyna sm)的库。这个库将特定格式的CPU指令集映射到许多不同CPU类型的汇编代码。因此,Just In Time编译器使用DynASM将操作码转换为特定于体系结构的计算机代码。
然而,有一个想法困扰了我很久.
如果preload可以将php代码解析成操作码后再执行,DynASM可以将操作码编译成机器码,为什么不通过预编译直接编译php呢?
听Zeev这一集的线索之一就是PHP是弱类型。这意味着PHP在试图用Zend VM运行操作码之前通常不知道变量的类型。
通过查看zend_value联合类型,可以看到有许多不同类型的指针指向一个变量。来自Zen的Zen虚拟机
D_value,它将使用类似ZSTR_VAL的宏来尝试从值的并集访问字符串指针。
例如,这个Zend VM处理程序应该处理一个“小于或等于”(=)表达式。看看它如何分支到许多不同的代码路径,只是猜测操作数类型。
用机器码复制这种类型的推理逻辑是不可行的,可能会让事情变得更慢。
计算完类型后再编译也不是一个好的选择,因为编译成机器码是一个CPU密集型的任务。所以在运行时编译一切也是不好的。
JIT编译器是如何工作的?
现在我们知道,在编译之前,我们无法推断出一个足够好的类型。我们也知道在运行时编译是昂贵的。JIT对PHP有什么好处?
为了平衡这个等式,PHP的JIT只试图编译一些它认为会有回报的操作码。为此,它对Zend VM正在执行的操作码进行汇总分析,并检查哪些操作码可以编译。(根据您的配置)
当编译一个操作码时,它会将执行委托给编译后的代码,而不是Zend VM。看起来是这样的:
PHP的JIT解释过程。如果编译,操作码将不会通过Zend VM执行。
因此,在Opcache扩展中有一些指令来检查是否应该编译一个操作码。如果是,则编译器使用DynASM将操作码转换为机器码,并执行新生成的机器码。
有趣的是,由于当前实现中编译的代码有MB限制(这也是可配置的),代码执行必须能够在JIT和解释代码之间无缝切换。
我还不确定编译部分什么时候会有效发生,但是我想我现在真的不想知道。
所以性能提升可能不会很大。
为什么每个人都说大多数php应用程序不会从使用Just In Time编译器中获得很大的性能好处?我希望我现在能清楚地知道这一点。以及为什么Zeev建议这是分析应用程序和试验不同JIT配置的最佳方式。
如果你使用的是PHP FPM,编译后的操作码通常会在多个请求之间共享,但它仍然不能改变游戏规则。
这是因为JIT优化了cpu绑定操作,现在大部分php应用都是I/O绑定的。如果必须访问磁盘或网络,无论处理操作是否编译。时机也很相似。
这篇文章是从https://thephp.website/en/issue/php-8-jit/翻译过来的
5.0
02
帖子视图:
942
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。