Hack on HHVM —— Facebook是如何优化PHP的

简介: Facebook周四正式发布了Hack编程语言,将静态类型以及一些现代的语言特性引入了PHP。这是Facebook对PHP优化之路上的新里程碑

Facebook周四正式发布了Hack编程语言,将静态类型以及一些现代的语言特性引入了PHP。这是Facebook对PHP优化之路上的新里程碑。

image.png

Facebook为何要优化PHP

这个问题可以从不同角度来回答。简单直接的回答是,Facebook的规模太大了。PHP的性能问题限制了Facebook的发展。从另一个角度来回答,则是要回答既然PHP不够用,为什么不干脆换掉?


把PHP换掉也有“整体换”和“局部换”的区别。最彻底的方案就是完全离开PHP,用别的语言重写一套。但是对于Facebook而言这个代价太高了。如果切换的话,多年来在PHP的积累就完全作废了。而且Facebook的业务逻辑非常复杂,据说PHP代码有2千万行…… 而且,Facebook员工众多,切换到一种新的语言,学习成本也不低。


既然整体换不可行,那就局部换吧。例如给PHP写C/C++扩展,可以提升性能。但是PHP扩展开发起来成本高,一般只适用于比较稳定的库,适用范围很有限。另一个方案将性能瓶颈的地方用其他语言实现,然后通过RPC(Remote Procedure Call,远程过程调用)在PHP和其他语言之间通讯。Twitter就用了这条路线,大量组件使用Scala和Java编写,通过RPC与展现层的Rails通讯。事实上,Facebook在这方面已经做了不少工作,为了减少RPC调用的开销,Facebook还专门开发了Thrift。然而,C++开发成本比PHP高很多,不适合用在需要快速修改的地方,而且大量RPC调用终究会影响性能。


整体换不现实,Thrift不够用,那么Facebook优化PHP就势在必行了。


Facebook要如何优化PHP

优化PHP,最先想到的是作性能分析,找出瓶颈,然后进行对应的优化。Facebook为此开发了XHProf工具。XHProf精确到函数层面,数据收集组件使用C开发(PHP扩展),报告组件用了PHP。支持PHP 5.2以上版本,对于定位性能瓶颈很有帮助。


但是PHP语言层面的优化限制太多,对Facebook而言还是不够用。所以Facebook需要对PHP语言的实现本身进行优化。


首先可以考虑的方案是改善PHP的官方实现。PHP的官方解释器运行PHP代码的过程可以分为两步:第一步,将PHP编译为bytecode;第二步,运行bytecode。那么改善PHP的官方实现就可以从这两个方面着手。


首先是优化编译PHP的步骤,这方面的工作已经有ZendOptimizerPlus做了。它会在内存中缓存编译好的bytecode,这样以后访问代码的时候就可以直接访问缓存好了的bytecode,省去了从磁盘读取再重新编译的开销。但是由于PHP语言的动态性,这个方法的效果一般,最好的情况下也只能提升20%的性能。


其次是优化运行bytecode的步骤。上面提到的ZendOptimizerPlus主要是优化编译PHP,但是也附带做了一些bytecode运行的优化。PHP有三种方式来运行bytecode:CALL、SWITCH和GOTO,默认使用CALL,也就是函数调用。优化函数调用,常用的方法就是内联(Inline function),也就是将函数展开,将函数体插入替换调用该函数的地方,这样可以节省每次调用函数带来的额外时间开支。但是这种做法其实是用“空间换时间”,如果内联过头了,空间开销会很大,得不偿失。在这方面进行调整,可以提升运行bytecode的性能。


此外还可以将整个PHP解释器用汇编重写,以快闻名的LuaJIT就是这么干的。

然而,无论是内联优化还是用汇编重写,代价都很大,而且如果优化官方实现的话,还要考虑PHP的向下兼容……

既然这个方案不太现实,那么不如把PHP搬到JVM上吧?JVM性能相当不错。


把PHP搬到JVM的工作,有人已经做过了。例如,IBM的P8(已死)Quercus(半死不活)。Facebook也研究过这个方案,2012年的时候,还有Facebook迁移到JVM的传闻。其实Facebook早已放弃这条路线。根据Facebook的研究,Quercus的性能和Zend+APC相比,差不了太多。这一方案效果不理想的原因可能是,JVM本身性能的优化是针对Java做的,其他语言在JVM上实现,不一定能用到这些优化。动态语言尤为如此。因为Java本身是静态类型的,所以很多优化JVM就没必要做,而在JVM上跑的动态语言需要这些优化。


既然JVM是为Java优化的,搬上去不合适,那不如针对PHP开发一个VM?这样就可以作大量针对性地优化了。然而开发VM可没有那么容易,成本不小,所以Facebook最初的选择是将PHP编译成C/C++之类性能优异的语言。也就是HHVM的前身——HPHPc。具体的做法是将PHP翻译为C++,然后再编译。


相比VM,这样的实现比较简单,而且能放手做优化(因为是离线编译,所以可以用时间换性能)。但是PHP的很多动态内容编译成C++比较麻烦,因此HPHPc禁掉了eval()之类的特性,即使这样,还是带来了一些问题,特别是由于需要将动态include的文件都编译在一起,最终的部署文件体积太庞大了,都过G了。


和HPHPc类似的项目有Roadsendphc,前者已经不维护了,后者也是命运坎坷。


编译到C++的效果不好,所以Facebook最终决定,还是写一个VM吧。


HHVM

FaceBook开发HHVM的阵容相当豪华,其中包括

  • Andrei Alexandrescu, 《C++ Coding Standards》的作者。
  • Drew Paroski,改进了.NET虚拟机的JIT。
  • Jason Evans,jemalloc的开发者(jemalloc将Firefox的内存消耗降低了一半)。
  • Keith Adams,VMware核心架构。
  • Sara Golemon,《Extending and Embedding PHP》作者,PHP内核领域的专家。


值得注意的是,Keith Adams给HHVM的影响很大。HHVM使用了JIT技术,一般的代码通过解释器执行(因为JIT也是有开销的),而常用的代码则使用JIT优化。通常而言,VM判断是否需要进行JIT优化是通过以下两种策略的一种:method-at-a-time(如果函数的执行超过了阈值,就进行JIT优化)和tracing (如果循环的执行超过了阈值,就进行JIT优化)。但是HHVM使用的是一种独特的策略,basic-block-at-a-time,这个策略和VMware的x86 hypervisor相似。使用这个策略与Facebook希望支持类型推导的闭包有关。


Hack

上面提到了类型推导。事实上,Facebook推出了一个运行在HHVM上的PHP改良语言——Hack。Hack里加入了类型的支持:

<?hh

classMyClass {

 constint MyConst = 0;

 privatestring$x = '';

 publicfunctionincrement(int$x): int {

   $y = $x + 1;

   return$y;

 }

}

加了类型之后,除了方便大型团队协作,避免编程中出现的错误之外,还有一个重要的原因就是能够让HHVM更好地优化性能。JIT优化最主要的方面就是根据类型来生成特定的指令,这样可以减少大量的指令和条件判断。而对于PHP这样的动态语言,要推断清楚类型是非常困难的,所以Hack就直接让程序员写上了。


兼容性

HHVM除了作为Hack的VM之外,还可以运行原生的PHP。兼容性测试表明,HHVM对PHP的兼容度已经达到98.58%了。由于HHVM使用了独特的JIT优化策略,因此Facebook自行开发了tracelet辅助库,这个库只支持x86 64bit系统,所以HHVM也只能在64位系统上使用——不过这个问题不大,现在的服务器硬件基本都支持64位了。需要考虑的是PHP扩展的问题。由于PHP语言包含非常之多的扩展,而Facebook的HHVM只实现了自家用到的扩展,所以可能有为HHVM重写PHP扩展的需要。好在相比为官方PHP实现写扩展,为HHVM写扩展比较容易,对性能要求不高的扩展可以使用纯PHP编写,然后编译到HHVM二进制文件中即可,详见HHVM wiki。还有一个要小心的问题就是HHVM是常驻内存的,所以如果某处PHP代码有内存泄露问题的话,可能拖慢整个HHVM服务的速度,甚至导致HHVM挂掉。

相关文章
|
26天前
|
存储 缓存 搜索推荐
优化PHP数组性能
优化PHP数组性能
40 8
|
25天前
|
监控 PHP Apache
优化 PHP-FPM 参数配置:实现服务器性能提升
优化PHP-FPM的参数配置可以显著提高服务器的性能和稳定性。通过合理设置 `pm.max_children`、`pm.start_servers`、`pm.min_spare_servers`、`pm.max_spare_servers`和 `pm.max_requests`等参数,并结合监控和调优措施,可以有效应对高并发和负载波动,确保Web应用程序的高效运行。希望本文提供的优化建议和配置示例能够帮助您实现服务器性能的提升。
51 3
|
3月前
|
设计模式 算法 数据库连接
PHP中的设计模式:如何优化你的代码结构
在本文中,我们将深入探讨PHP中的设计模式。设计模式是解决常见软件设计问题的最佳实践。它们不是具体的代码,而是一种编程经验的总结。掌握设计模式可以帮助你写出更高效、灵活和可维护的代码。本文将介绍几种常见的设计模式,并通过示例展示如何在PHP项目中应用这些模式。无论你是PHP初学者还是有经验的开发者,都能从本文中获得启发和实用的技巧。
|
3月前
|
缓存 监控 测试技术
php-fpm优化调优
php-fpm优化调优
|
3月前
|
缓存 监控 算法
分析慢日志文件来优化 PHP 脚本的性能
分析慢日志文件来优化 PHP 脚本的性能
|
4月前
|
安全 前端开发 PHP
构建与验证表单:传统PHP与Laravel框架的比较分析——探索Web开发中表单处理的优化策略和最佳实践
【8月更文挑战第31天】在 Web 开发中,表单构建与数据验证至关重要。传统 PHP 方法需手动处理 HTML 表单和数据验证,而 Laravel 框架则提供了一种更现代、高效的解决方案。本文通过对比传统 PHP 和 Laravel 的方法,探讨表单构建与验证的最佳实践。Laravel 通过简洁的语法糖、内置的数据过滤和验证机制,显著提升了代码的安全性和可维护性,适用于大型项目或需要快速开发的场景。然而,在追求灵活性的小型项目中,直接使用 PHP 仍是不错的选择。了解两者的优劣,有助于开发者根据项目需求做出最佳决策。
40 0
|
4月前
|
安全 Java 云计算
JSF 应用究竟何去何从?云端部署能否成为其全新突破点?快来一探究竟!
【8月更文挑战第31天】本文介绍了将JavaServer Faces(JSF)应用部署到云平台的过程。首先,根据成本、功能、可靠性和安全性选择合适的云平台。接着,展示了构建简单JSF应用的示例代码。最后,以AWS Elastic Beanstalk为例,详细说明了部署流程。部署至云端可提升应用的可用性、扩展性和安全性。
50 0
|
3月前
|
安全 关系型数据库 MySQL
PHP与MySQL交互:从入门到实践
【9月更文挑战第20天】在数字时代的浪潮中,掌握PHP与MySQL的互动成为了开发动态网站和应用程序的关键。本文将通过简明的语言和实例,引导你理解PHP如何与MySQL数据库进行对话,开启你的编程之旅。我们将从连接数据库开始,逐步深入到执行查询、处理结果,以及应对常见的挑战。无论你是初学者还是希望提升技能的开发者,这篇文章都将为你提供实用的知识和技巧。让我们一起探索PHP与MySQL交互的世界,解锁数据的力量!
|
3月前
|
NoSQL 关系型数据库 MySQL
不是 PHP 不行了,而是 MySQL 数据库扛不住啊
【9月更文挑战第8天】这段内容讨论了MySQL在某些场景下面临的挑战及其原因,并指出这些问题不能完全归咎于MySQL本身。高并发读写压力、数据量增长以及复杂查询和事务处理都可能导致性能瓶颈。然而,应用程序设计不合理、系统架构不佳以及其他数据库选择和优化策略不足也是重要因素。综合考虑这些方面才能有效解决性能问题,而MySQL通过不断改进和优化,仍然是许多应用场景中的可靠选择。
156 9