为什么 PHP 使用操作码缓存,而 Java 编译为字节码文件


Why does PHP use opcode caches while Java compiles to bytecode files?

在我看来,PHP和Java都有类似的结构。首先编写一些高级代码,然后必须将其转换为更简单的代码格式,以便由 VM 执行。一个区别是,PHP 直接从源代码文件工作,而 Java 将字节码存储在.class文件中,VM 可以从中加载它们。

如今,对快速执行PHP的要求越来越高,这使人们认为最好直接使用操作码,而不是在每次用户点击文件时都进行编译步骤。

解决方案似乎是所谓的加速器的负载,它基本上将编译的结果存储在缓存中,然后使用缓存的操作码而不是再次编译。

Facebook完成的另一种方法是将PHP代码完全编译为不同的语言。

所以我的问题是,为什么PHP世界中没有人做Java所做的事情?是否有一些动态元素真的需要每次重新编译或类似的东西?否则,当代码投入生产时编译所有内容,然后使用它会更聪明。

最重要的区别在于 JVM 有一个完全覆盖字节码的显式规范。这使得字节码文件具有可移植性,并且不仅可用于特定 JVM 实现的执行。

PHP甚至没有语言规范。PHP 操作码是特定 PHP 引擎的实现细节,因此您无法对它们做任何有趣的事情,并且使它们更可见几乎没有意义。

PHP 操作码与 Java 类文件不同。Java 类文件是明确指定的,并且可以在机器之间移植。PHP 操作码不能以任何方式移植。例如,它们具有内置的内存地址。严格来说,它们是PHP解释器的实现细节,不应被视为Java字节码之类的东西。

一定要这样吗?不,可能不是。但是PHP源代码是一团糟,PHP内部社区既没有实现这一目标的愿望,也没有政治意愿。我认为有人谈论将操作码缓存烘焙到 PHP 6 中,但 PHP 6 死了,我不知道这个想法的状态。

参考:我写了phc,所以我在PHP实现/编译中深入了几年。

PHP世界中没有人在做Java所做的事情,这并不完全正确。像Alexey Zakhlestin的应用程序服务器这样的项目提供了一定程度的持久性,更类似于java servlet容器(尽管他的灵感更多的是Ruby的Rack和Python的WSGI,而不是Java(。

PHP 不使用操作码的标准机制。我希望它要么坚持到堆栈虚拟机(python,java(要么注册虚拟机(x86,perl6等(。但它使用的东西绝对是土生土长的,而且存在摩擦。

它使用内存中的连接列表,导致每个操作码具有 ->op1 ->op2 和 ->result。现在,它们中的每一个都是常量或临时表中的条目等。这些指针不能以任何理智的方式序列化。

现在,人们已经使用像pecl/bcompiler这样的项目来完成了这一点,它确实将流转储到磁盘中。

但是这些类使这变得更加复杂,这意味着存在潜在的代码片段,例如

if(<conditon>)
{
   class XYZ() { }
}
else 
{
   class XYZ() { }
}
class ABC extends XYZ {}

这意味着关于类和函数的大量决策只能在运行时完成 - 像Java这样的东西会阻塞两个具有相同名称的类,这两个类在运行时有条件地定义。基本上,APC的继承和类缓存代码可能是代码库中最复杂和最容易出错的部分。每当缓存类时,必须先清除所有父继承成员,然后才能将其保存到操作码缓存中。

指针问题并非不可克服。有一个我从未费心修复的apc_bindump,以便在重新启动时直接从磁盘上加载整个缓存条目。但是调试所有这些以获得仍然需要找到所有系统指针的东西是很痛苦的 - apache 案例太容易了,因为由于分叉行为,所有 php 进程都具有相同的系统指针。旧的fastcgi版本较慢,因为它们过去先分叉,然后再初始化php - php-fpm通过相反的方式解决这个问题。

但最终,PHP中真正缺少的是发明字节码格式的意愿,扔掉当前的引擎和所有模块 - 使用堆栈VM重写它并构建JIT。我希望我有时间 - fb 家伙几乎和他们的嘻哈 HHVM 在那里。这牺牲了 eval(( 以获得更快的性能 - 这是一个公平的牺牲:)

PS:我是找不到时间正确更新APC 5.4的人

我认为你们所有人都被误导了。HHVM不是编译器,而是另一种语言本身就是虚拟机。混乱是因为Facebook用来编译到C ++,但这种方法是为了满足开发人员的要求(十分钟编译只是为了测试一些小东西(。