python是解释器还是编译器,python编辑器和python解释器的区别
使用PHP8中新添加的JIT编译器
发布时间:2020-06-01 22:09:19
来源:易俗云
阅读量:751
作者:鸽子
php 8的JIT(justintime)编译器使用php集成的Opcache扩展作为扩展,在运行时直接从cpu指令转换一些操作码。
也就是说,使用JIT时,Zend VM不需要解释具体的操作码,这些指令直接作为CPU级指令执行。
PHP 8的JIT
8 PHP Justin Time(JIT)编译器的影响力毋庸置疑。然而,到目前为止,我发现JIT应该做什么还不清楚。
经过多次研究和放弃,我决定自己去查PHP源代码。结合我的C语言知识和我现在收集的所有零碎信息,我提出了这篇文章。我希望JIT能帮助我们更好地理解PHP。
简单来说,如果JIT按预期运行,代码就不会在Zend VM上运行,而是直接作为一组CPU级指令运行。
这是所有的想法。
然而,为了更好地理解这一点,我们需要考虑php内部是如何工作的。并不复杂,但需要一些介绍。
如何执行PHP的代码?
吵架的音频,PHP是解释性语言。这句话本身是什么意思?
每次执行PHP代码(命令行脚本或web APP应用程序)时,都会经过PHP解释器。最常用的是PHP-FPM和CLI解释器。
翻译的工作很简单。接收PHP代码,解释它,并返回结果。
一般的描述性语言就是这个过程。虽然有语言减少了几步,但总体思路是一样的。在PHP中,过程如下:
读取PHP代码,并将其解释为一组称为令牌的关键字。这个过程会让解释器知道每个程序写的是哪个代码。这一步被称为词法分析或标记化。获得令牌集合后,PHP解释器将尝试解析它们。通过称为解析的过程生成抽象语法树[ast]。其中AST是表示要执行的操作的节点集。比如“echo1”的实际含义是“打印1 ^ 1的结果”,或者更具体地说,“打印一个操作后,这个操作就变成1 ^ 1”。使用AST,您可以更容易地理解操作和优先级。为了将抽象语法树转换成CPU可执行的操作,需要转换表达式(IR)。它在PHP中被称为操作码。将AST转换成操作码的过程称为编译。有了操作码,有趣的部分就来了。执行代码!PHP有一个名为Zend VM的引擎,可以接收并运行一系列操作码。当所有操作码运行时,Zend VM将退出程序。
这张图可以让你更清楚:
简化的PHP解释过程概述。
如你所见。这里有一个问题。即使PHP代码没有改变,您是否希望每次运行该进程时都运行它?
让我们来看看操作码。没错!这就是Opcache扩展存在的原因。
Opcache扩展
Opcache扩展包含在PHP中,所以通常不需要禁用它。要使用PHP,最好打开Opcache。
它的功能是给操作码增加一个内存共享缓存层。它的工作是从AST中提取新生成的操作码,并缓存它们以供运行时使用。
您可以跳过词法分析/标记化和解析步骤。
这是包含Opcache扩展的进程的图像。
解释使用PHP Opcache的过程。如果文件已经被分析,PHP将检索缓存的操作码,而不是重新分析。
完美地跳过了词法分析/标记化、解析和编译的步骤。
边注:这是一个伟大的PHP 7.4预加载功能RFC!您可以指示PHP FPM分析代码库,将其转换为操作码,并在运行之前缓存它。
想知道JIT是怎么参与到这个讲解过程中的吗?这篇文章的解释。
准时制编译的效果如何?
在听了Zeev关于PHP内部消息的PHP和JIT广播后,我清楚了JIT实际上做了什么。
如果Opcache扩展可以更快地获取操作码,并将它们直接传递给Zend VM,那么JIT完全不需要使用Zend VM就可以运行。
Zend VM是用c写的程序,作为操作码和CPU之间的一层。由于JIT在运行时直接生成编译的代码,PHP可以
跳过Zend VM虚拟机,直接在CPU上运行。理论上性能会更好。
这听起来可能很奇怪。因为在编译成机器码之前,需要为每个结构类型创建一个特定的实现。但其实这也在情理之中。
PHP的JIT使用Dy这个名字
NASM(动态汇编程序)库,它将一组特定格式的CPU指令映射成许多不同CPU类型的汇编代码。所以编译器只需要用DynASM把操作码转换成特定结构的机器码。
但是,有一个问题困扰我很久了。
如果预加载可以在执行前将PHP代码解析成操作码,DynASM可以将操作码编译成机器码(刚好及时编译),那我们为什么不使用预运行编译(提前编译)立即编译PHP呢?
通过听Zeev的广播,我发现了一个原因,PHP是一种弱类型语言,这意味着在Zend VM试图执行某个操作码之前,PHP通常不知道某个变量的类型。
查看Zend_value联合类型,我们可以看到许多指针指向不同类型的变量。每当Zend VM试图从Zend_value中获取一个值时,它都会使用类似ZSTR_VAL的宏来获取一个指向联合类型中的字符串的指针。
例如,这个Zend VM处理程序处理“小于或等于”(=)表达式。看看它编码了这么多if else分支,仅仅是为了类型推断。
用机器码执行类型推理逻辑是不可行的,可能会变得更慢。
编译前进行评估并不是一个好的选择,因为编译成机器码是一个CPU密集型的任务。所以运行时什么都编译不好。
那么准时制是如何编译的呢?
现在我们知道,我们不能很好地推断类型来提前编译。我们也知道运行时编译的计算成本非常高。那么JIT对PHP有什么好处呢?
为了找到平衡,PHP的JIT试图只编译有价值的操作码。所以JIT会分析Zend VM要执行的操作码,检查可能的编译位置。(根据配置文件)
当一个操作码被编译后,它会把执行交给编译后的代码,而不是Zend VM。看起来是这样的:
PHP的JIT解释过程。如果编译,操作码将不会通过Zend VM执行。
因此,在Opcache扩展中,有两条检测指令来决定是否编译Opcode。如果是,编译器将使用DynASM将这个操作码转换成机器码,并执行这个机器码。
有趣的是,由于当前接口中编译的代码受到MB的限制(这也是可配置的),代码执行必须能够在JIT和解释代码之间无缝切换。
顺便说一下,Benoit Jacquemont关于the JIT的演讲帮助我理解了整个事情。
我现在还不确定编译部分什么时候有效,但是我想现在我真的不想知道。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。