PHP GR8:JIT将提高PHP 8性能



PHP是Badoo的主要开发语言之一。 在我们的数据中心中,成千上万个处理器核心正忙于执行数百万行PHP代码。 我们密切关注新闻,并积极寻找提高生产率的方法,因为即使对容量进行一点优化也可以节省大量资源。 PHP的主要性能新闻之一是JIT在该语言的第八个版本中的出现。 当然,这不能不引起我们的注意,我们翻译了有关什么是JIT,如何在PHP中实现,为什么决定这样做以及对它有什么期望的文章。

如果您没有离开洞穴或不是过去(在这种情况下,欢迎您),您已经知道PHP 8将具有JIT:有一天,投票是安静和平地进行的,绝大多数参与者投票赞成实施,因此,一切都已决定。

令人欣喜的是,您甚至可以在照片中描绘几个疯狂的动作(顺便说一下,这被称为“底特律JIT”:



现在,坐下来阅读有关揭穿神话的文章。 我想澄清与JIT是什么以及它如何有用相关的误解,并讨论它的工作原理(但不要太详细,以免您感到无聊)。

由于我不知道谁会读这篇文章,因此我将从简单的问题转向复杂的问题。 如果您已经知道标题中问题的答案,则可以安全地跳过相应的章节。

什么是准时制?


PHP是在虚拟机(我们称为Zend VM)的基础上实现的。 该语言将PHP源代码编译为虚拟机可以理解的指令(这称为编译阶段)。 在编译阶段获得的虚拟机指令称为操作码。 在运行时阶段,Zend VM执行操作码,从而执行所需的工作。

该电路效果很好。 另外,诸如APC(之前)和OpCache(今天)之类的工具会缓存编译阶段的结果,因此仅在必要时执行此阶段。

简而言之,JIT是一种及时的编译策略(在正确的时间),在该策略中,代码首先转换为中间表示形式,然后在执行过程中转变为与体系结构相关的机器代码。

在PHP中,这意味着JIT将把在编译阶段收到的虚拟机的指令视为中间表示,并给出不再由Zend VM执行而是由处理器直接执行的机器代码。

为什么PHP需要JIT?


在PHP 7.0出现之前不久,PHP团队的主要重点是语言性能。 PHP 7.0中的大多数主要更改都在PHPNG补丁中进行,该补丁大大改善了PHP使用内存和处理器的方式。 从那时起,我们每个人都必须看一眼语言的性能。

PHP 7.0发行后,性能继续得到改善:哈希表(PHP中的主要数据结构)得到了优化,Zend VM中某些操作码的特殊化以及编译器中某些序列的特殊化得到了实现,Optimizer(OpCache组件)得到了不断改进,并且实现了许多其他更改。

严酷的事实是,由于所有这些优化,我们正在迅速接近提高生产率的机会。

请注意:“限制改进的机会”是指必须为进一步改进而做出的取舍不再具有吸引力。 在优化性能时,我们总是在权衡取舍。 通常,为了提高生产率,我们不得不牺牲简单性。 每个人都想以为最简单的代码也是最快的,但是在C语言的现代世界中,事实并非如此。 最快的最常用的代码是准备利用架构的内部结构或平台/编译器中内置的结构的代码。 单靠简单并不能保证更好的性能。

因此,在此阶段,要从PHP挤出更多性能的最佳方法是实现JIT。

JIT会加速我的网站吗?


可能性很小,无关紧要。

这可能不是您期望的答案。 事实是,通常,PHP应用程序受输入/输出(I / O限制)限制,而JIT最好与受处理器(CPU限制)限制的代码一起使用。

“受I / O和处理器的限制”是什么意思?


为了描述某些代码或应用程序整体性能的特征,我们使用术语“受输入输出限制”和“受处理器限制”。

最简单的定义:

  • 如果我们找到一种改进(减少,优化)已执行的I / O操作的方法,那么受I / O限制的代码将可以更快地工作;
  • 如果我们找到一种方法来改进(减少,优化)处理器执行的指令或神奇地提高处理器时钟速度,那么处理器限制的代码将以更快的速度工作。

代码和应用程​​序可以受I / O,处理器或两者的限制。

通常,PHP应用程序往往受I / O限制:它们的主要瓶颈通常是I / O操作-连接,读取和写入数据库,缓存,文件,套接字等。

受处理器限制的PHP代码是什么样的?


可能由于大多数PHP应用程序的性质,某些PHP程序员对于处理器受限的代码还是陌生的:它们通常充当数据库或缓存的链接,拾取并产生少量HTML / JSON / XML响应。

您可以查看您的代码库,找到许多与I / O无关的代码,这些代码调用与I / O无关的函数。 您可能会感到困惑,尽管它的代码中有更多行不适用于I / O,但并不能使您的应用不受处理器的限制。

事实是PHP是最快的解释语言之一。 在Zend VM和机器代码中调用不使用I / O的函数之间没有明显的区别。 当然,会有一些区别,但是机器代码和Zend VM都使用调用约定,因此,无论您在操作码中还是在机器代码中调用-___()都没有关系-这不会产生明显的影响进行调用的整个应用程序的性能。

注意:简单来说,调用约定是进入另一个函数之前执行的一系列指令。 在这两种情况下,调用约定都将参数传递给堆栈。

您会问:“循环,尾部调用等等呢?” PHP足够聪明-启用OpCache的Optimizer后,您的代码将神奇地转换为所编写内容的更高效版本。

这里应该注意,JIT不会更改Zend VM的调用约定。 这样做是因为PHP必须能够随时在JIT和VM模式之间切换(因此,他们决定保留当前约定)。 结果,您在任何地方看到的使用JIT进行的呼叫都不会更快地工作。

如果要查看处理器限制的PHP代码是什么样的,请在此处查看: https : //github.com/php/php-src/blob/master/Zend/bench.php 。 这是一个极端的例子,但它表明JIT的所有出色之处都在数学中得以体现。

是否必须做出这样的极端妥协才能加快PHP中的数学计算速度?


不行 我们这样做是为了扩大语言的应用范围(并扩大有效范围)。

我们不想吹牛,但是PHP主导了网络。 如果您从事Web开发,并且不考虑在下一个项目中使用PHP,那么您做错了事(根据非常有偏见的PHP开发人员)。

乍一看,似乎PHP中数学计算的加速应用范围非常狭窄。 但是,这为我们打开了机器学习,3D渲染,2D渲染(GUI)和数据分析之路。

为什么不能在PHP 7.4中实现呢?


上面,我称JIT是一个极端的折衷方案,我真的这么认为:这是所有现有(即使不是最困难的)最困难的编译策略之一。 实施JIT会大大增加复杂性。

如果您询问JIT的作者Dmitry,他是否使PHP变得复杂,他将回答:“不,我讨厌复杂性”(这是引用)。

本质上,“复杂”是指“我们不了解的内容”。 如今,很少有语言开发人员真正了解JIT的现有实现。

PHP 7.4的工作进展迅速,此版本中JIT的引入将导致以下事实:只有少数人可以调试,修复和改进该语言。 对于那些在PHP 7.4中投票反对JIT的人来说,这是不可接受的。

在PHP 8发行之前,我们许多人都将了解JIT实现。 我们要实现一些功能,并且要为第八个版本重写一些工具,因此我们首先需要了解JIT。 我们需要这次,我们非常感谢大多数人投票给我们。

复杂不是可怕的代名词。 复杂性可以像星云一样美丽,这与JIT有关。 换句话说,即使我们团队中的20个人开始理解JIT并不比Dmitry差,这也不会改变JIT本质的复杂性。

PHP开发会变慢吗?


没有理由这样认为。 我们有足够的时间,所以可以说,到PHP 8准备就绪时,我们当中将有足够的人掌握JIT,足以在修复错误和开发PHP方面比今天有效。

当您尝试将其与JIT的原始复杂性的概念联系起来时,请记住,我们在介绍新功能上花费的大部分时间都花费在讨论这些功能上。 通常,在使用功能和修复错误时,编写代码需要几分钟或几小时,而讨论则需要数周或数月。 在极少数情况下,代码必须编写数小时或数天,但即使如此,讨论也总是会持续更长的时间。

这就是我想说的。

既然我们在谈论性​​能,所以我邀请我的同事Pavel Murzakov参加5月17日在PHP Russia会议上的报告 Pasha知道如何从PHP代码中挤出最后一个CPU秒!

Source: https://habr.com/ru/post/zh-CN448622/


All Articles