文章目录
本文分成3部分:
①PHP 和 Java 的主要区别有哪些? - eechen的回答 - 知乎
https://www.zhihu.com/question/20377398/answer/141328982
② h4cd 发布于 2019年04月01日-开源中国
https://www.oschina.net/news/105566/php-8-will-support-jit
③乔·沃特金斯-Musings, ninja ones-思考,忍者
Saturday, 30 March 2019
PHP GR8
https://blog.krakjoe.ninja/2019/10/
①eechen的回答 - 知乎
PHP暂时还不支持像Java那样JIT运行时编译热点代码,
(但PH8确认将其引入:PHP 8 中是否会支持 JIT(Just-In-Time)即时编译执行功能,最新消息是官方已经确认将其引入。)
但是PHP具有opcache机制,能够把脚本对应的opcode缓存在内存,
PHP7中还支持配置opcache.file_cache导出opcode到文件.
第三方的Facebook HHVM也支持JIT.
另外PHP官方基于LLVM围绕opcache机制构建的Zend JIT分支也正在开发测试中.
在php-src/Zend/bench.php测试显示,PHP JIT分支速度是PHP 5.4的10倍.
https://github.com/zendtech/php-src/tree/zend-jit/ext/opcache/jit
https://www.phpclasses.org/blog/post/493-php-performance-evolution.html
PHP的库函数用C实现,而Java核心运行时类库(jdk/jre/lib/rt.jar,大于60MB)用Java编写(jdk/src.zip), 所以Java应用运行的时候,用户编写的代码以及引用的类库和框架都要在JVM上解释执行. Java的HotSpot机制,直到有方法被执行10000次(-XX:CompileThreshold=10000)才会触发JIT编译, 在此之前运行在解释模式下,以避免出现JIT编译花费的时间比方法解释执行消耗的时间还要多的情况.
PHP内置模板引擎,自身就是模板语言.而Java Web需要使用JSP容器如Tomcat或第三方模板引擎.
PHP内置HTTP服务器和SQLite数据库,以及Apache模块实现libphp.so和FastCGI服务PHP-FPM.而Java Web开发时普遍需要使用第三方的Servlet容器Tomcat等.
PHP内置的单进程HTTP服务器(可用于快速开发和测试):
php -S 127.0.0.1:8080 -t /www
PHP-FPM跟Nginx一样,是多进程的架构,worker进程处理请求,master进程不处理请求,只负责维护worker进程,比如定量重启,崩溃重启等.PHP-FPM支持进程池的特性,不同进程池相互隔离,互不影响.比如你可以配置一个监听9000端口的进程池www和一个监听9001的进程池io来分离IO密集脚本:
nginx.conf: 访问io.php的请求都交给监听9001的PHP-FPM进程池处理
location = /io.php {
include fastcgi_params;
fastcgi_pass 127.0.0.1:9001;
fastcgi_param SCRIPT_FILENAME documentrootfastcgi_script_name;
}
php-fpm: 正常脚本由静态www池处理,阻塞脚本由动态io池处理
[www]
;名为www的进程池监听9000端口,常驻进程数量为固定4个
listen = 127.0.0.1:9000
pm = static
pm.max_children = 4
[io]
;名为io的进程池监听9001端口,进程数常驻4个,最大8个
listen = 127.0.0.1:9001
pm = dynamic
pm.max_children = 8
pm.start_servers = 4
pm.min_spare_servers = 4
pm.max_spare_servers = 4
其中I/O密集这个进程池[io]采用动态的prefork进程,比如这里是繁忙时8个,空闲时4个.
利用PHP-FPM提供的池的隔离性,分离计算密集和I/O密集操作,可以减少阻塞对整个PHP应用的影响.
也就是说,PHP通过多进程利用多核实现并发,而Java普遍通过多线程实现并发,因为一个JVM实例就是一个进程.
另外,PHP也可以运行在多线程模式下,比如Apache的event MPM和Facebook的HHVM都是多线程架构.不管是多进程还是多线程的PHP Web运行模式,都不需要PHP开发者关心和控制,也就是说PHP开发者不需要写代码参与进程和线程的管理,这些都由PHP-FPM/HHVM/Apache实现.
PHP-FPM进程管理和并发实现并不需要PHP开发者关心,而Java多线程编程需要Java开发者编码参与.PHP一个worker进程崩溃,master进程会自动新建一个新的worker进程,并不会导致PHP服务崩溃.而Java多线程编程稍有不慎(比如没有捕获异常)就会导致JVM崩溃退出.
对于PHP-FPM和Apache MOD_PHP来说,服务进程常驻内存,但一次请求释放一次资源,这种内存释放非常彻底. PHP基于引用计数的GC甚至都还没发挥作用程序就已经结束了. 而且,在PHP脚本中用unset显式释放内存也是立竿见影的,不会有延时.而Java的内存回收严重依赖GC机制,高并发下的Full GC会导致Java服务雪崩:JVM忙于用GC回收内存无法处理请求,而新请求又源源不断地到来.
PHP的运行模式决定了PHP天然支持热部署,而Java要实现热部署并不容易.这也是为什么在虚拟主机托管环境里PHP占绝对优势的原因,因为开发者通过FTP上传PHP文件到虚拟空间就实现了代码更新和部署.
PHP跨进程共享数据,除了使用基于文件的session机制和鸟哥开发的无锁共享内存缓存扩展Yac.Linux上还可以使用内存文件系统(tmpfs)上的SQLite(如/dev/shm/data.sqlite3).而Java程序的生命周期随JVM常驻内存,线程可以访问共享数据.
PHP不存在数据库访问速度比Java慢的问题.PHP的数据库驱动如mysqlnd等都是C实现的驱动,而Java的数据库驱动JDBC是Java实现的驱动,PHP的驱动性能并不吃亏.而且PHP同样支持数据库持久连接,也就是多个请求能复用一个数据库连接,并不需要每个请求都打开一个数据库连接.比如下图就是两个PHP-FPM工作进程跟MySQL保持的两个长连接:
PHP跟Java都诞生于1995年,没有PHP相对Java是后起之秀的说法,反倒是PHP一开始就是用于Web开发,而Java不是.Java的前身Oak语言,是为了嵌入式软件开发而设计.
C实现的PHP后来吸收了C++的对象编程思想,加入了对象编程支持.既可以用过程式,也可以用对象,更灵活.而Java必须完全面向对象编程,甚至还要把类名和文件名挂钩.
PHP能不能开发大型应用,取决于使用者是否因地制宜地使用PHP. 比如,PHP就不适合用来开发数据库引擎(大多都是C/C++实现)等计算密集型应用.Java在计算密集型应用上相比PHP更有优势,比如HBase数据库使用了Java实现.不过大多数Web应用都是I/O密集型应用,这里面包括网络I/O,文件系统I/O,数据库I/O.
PHP是C实现的Web快速开发框架,不依赖第三方框架也能实现快速开发.而Java Web开发普遍依赖Spring等第三方框架.
补充:
回复
@沟槽
数据库持久连接很容易用,mysqli里host参数传递p:127.0.0.1就能开启持久连接,pdo_mysql里把PDO::ATTR_PERSISTENT设为true也可以开启持久连接.而且PHP的数据库持久连接也不依赖PHP-FPM,我的截图不过是举例说明,其实用Apache也一样可以.只是PHP-FPM的进程数或者Apache进程/线程数最好配置为固定数量,而且要求数量不能超过MySQL最大连接数(max_connections默认是151).
进程间共享数据,除了Yac,我不是还说了Linux内存文件系统tmpfs上的SQLite么?SQLite的锁机制够用了呀,连事务都支持,你担心什么?而且内存上也不需要担心SQLite的读写性能限制,完全是SQLite引擎在内存上的计算密集操作.能利用Linux的tmpfs机制,能利用SQLite的锁机制,而不依赖PHP实现,我觉得很有优势很稳定.
这里讨论的不是Swoole这类CLI下实现的PHP服务,而是PHP传统的FastCGI模式.长时间后台常驻运行的PHP脚本,当然需要GC.对于一些要求实时的高并发应用,我觉得就不应该使用GC机制.PHP可以配置zend.enable_gc=off来禁用GC,并且自己通过unset来手动释放内存.不过运行在FastCGI下的PHP脚本生命周期很短,其实本来就不依赖GC.再次强调,高并发实时类应用,GC绝不是一个优势,而是一个劣势.
PHP常见的SAPI有这么几种:
php(cli,cli-server)
php-cgi(cgi-fcgi)
php-fpm/hhvm(fpm-fcgi)
libphp7.so/php7apache2_4.dll(apache2handler)
fpm-fcgi和apache2handler下,无论是多进程模式还是多线程模式,进程和线程的实现和管理都不需要PHP开发者关心(这是优势),而是由php-fpm/hhvm/apache实现.PHP开发者如果要参与多进程或多线程编程,完全可以在php-cli下实现,相关PECL扩展包括pcntl多进程,pthreads多线程,libevent事件驱动等等,相关的项目实现有WorkerMan.另外峰哥的Swoole也需要用php-cli跑,但其服务的进程和线程控制也是由Swoole实现而不太需要PHP开发者关心.
PHP从5.4内置的单进程HTTP服务器,目的就是用于快速的开发和测试,我觉得是一个很方便的工具,开发者不需要安装和配置Apache或者Nginx之类Web服务器就能进行入门开发.而且把PHP解释器交叉编译到Android手机或者OpenWRT无线路由就能用PHP这个省资源的HTTP服务器在局域网内编程并提供服务,很方便.
JIT在大量计算上有优势,bench.php脚本就是用来测试计算性能.真实应用如WordPress等,JIT能带来的性能提升肯定不会这么明显,具有JIT机制的HHVM和没有JIT的PHP7,在压力测试WordPress时体现出同一水平,就说明了这个问题.而且我强调过,Web应用大多是I/O密集型应用,编译型语言不会在I/O密集型应用里也具有数量级的优势.所以说,对于大多数PHP开发者来说,就算是没有JIT机制的PHP7,性能也够用了.
回复
@残风
mem php-fpm && siege -c10 -t1M http://www.example.com/app/punbb/index.php > /dev/null && mem php-fpm
其中mem是我定义在~/.bashrc里的一个用于快速根据名称查看程序情况的函数:
mem() {
top -n1 -b | head -n7 | sed ‘1,6d’ && top -n1 -b | sed ‘1,7d’ | grep -i $1 | grep -v grep;
ps aux | grep -i $1 | grep -v grep | awk -F " " ‘{ sum += $6 } END { printf “Total Resident Set Size: %.1f MB (%d KB)\n”, sum/1024, sum}’;
}
测试程序PunBB是一个MySQL驱动的PHP轻量级论坛.
用siege并发10,压测1分钟,PHP-FPM工作进程的内存(RES)都是13.4MB,并不存在你所谓的PHP-FPM内存占用会不断随请求数量而增长的问题,有图有真相:
②h4cd-开源中国
一直以来,大家都在讨论 PHP 8 中是否会支持 JIT(Just-In-Time)即时编译执行功能,最新消息是官方已经确认将其引入。
Include JIT into PHP 8?
Real name | Yes | No |
---|---|---|
ab (ab) | √ | |
ashnazg (ashnazg) | √ | |
beberlei (beberlei) | √ | |
brandon (brandon) | √ | |
bwoebi (bwoebi) | √ | |
carusogabriel (carusogabriel) | √ | |
cmb (cmb) | √ | |
cpriest (cpriest) | √ | |
dams (dams) | √ | |
danack (danack) | × | |
derick (derick) | √ | |
diegopires (diegopires) | √ | |
dmitry (dmitry) | √ | |
duncan3dc (duncan3dc) | √ | |
emir (emir) | √ | |
galvao (galvao) | √ | |
guilhermeblanco (guilhermeblanco) | √ | |
jhdxr (jhdxr) | √ | |
jmikola (jmikola) | √ | |
jpauli (jpauli) | √ | |
jwage (jwage) | √ | |
kalle (kalle) | √ | |
klaussilveira (klaussilveira) | √ | |
krakjoe (krakjoe) | √ | |
laruence (laruence) | √ | |
lcobucci (lcobucci) | √ | |
levim (levim) | × | |
malukenho (malukenho) | √ | |
mariano (mariano) | √ | |
mbeccati (mbeccati) | √ | |
mike (mike) | √ | |
narf (narf) | √ | |
neeke (neeke) | √ | |
nikic (nikic) | √ | |
ocramius (ocramius) | √ | |
pajoye (pajoye) | √ | |
peehaa (peehaa) | √ | |
petk (petk) | √ | |
pmmaga (pmmaga) | √ | |
pollita (pollita) | √ | |
remi (remi) | √ | |
reywob (reywob) | √ | |
rtheunissen (rtheunissen) | √ | |
salathe (salathe) | √ | |
sammyk (sammyk) | √ | |
stas (stas) | √ | |
svpernova09 (svpernova09) | √ | |
tianfenghan (tianfenghan) | √ | |
wjx (wjx) | √ | |
yunosh (yunosh) | √ | |
zeev (zeev) | √ | |
zimt (zimt) | √ | |
Final result: | 50 | 2 |
JIT 的好处
主要是 JIT 带来的性能提升,以及性能提升对整个语言使用场景的扩展及语言生态发展的支持。
目前已经很难通过常规手段提升 PHP 的性能,JIT 基本上是目前性能提升的唯一手段;
JIT 带来的性能提升可以让 PHP 在更多使用场景( CPU 密集)中发挥作用;
可以使用 PHP 来开发内置函数,而不用担心性能方面的问题。这一方面可以加速语言的发展(更多人可以参与进来),同时也可以减少目前使用 C 开发容易出现的内存管理、溢出等问题
PHP 实现了一个虚拟机 Zend VM,它会将人类可读脚本编译成虚拟机理解的指令,也就是操作码,这个执行阶段就是“编译时(Compile Time)”;在“运行时(Runtime)”执行阶段,虚拟机 Zend VM 会执行这些编译好的操作码。
通常编译时与运行时两个阶段是独立分开的,脚本编译完成后,像 APC 与 OPCache 这样的字节码缓存组件会缓存这些操作码。而 JIT 去掉了编译时阶段,它将这编译时与运行时两个阶段合为一体,实现即时编译与执行。
JIT 是一种编译器策略,它将代码表述为一种中间状态,在运行时将其转换为依赖于体系结构的机器码,并即时执行。在 PHP 中,这意味着 JIT 将为 Zend VM 生成的指令视为中间表述,并以依赖于体系结构的机器码执行,也就是说托管代码的不再是 Zend VM,而是更为底层的 CPU。
虽然自 PHP 7.0 以来,通过优化核心数据结构 HashTable、强化 Zend VM 中某些操作码与持续改进 OPCache 的 Optimizer 组件等具体措施,PHP 性能得到了显著提升,但是实际上这些优化似乎已经走到极限了。现在 JIT 从底层着手,被认为是目前提升 PHP 性能的最佳出路。
关于是否引入 JIT 的官方投票结果已于近日公布,因为大部分核心开发者投了赞成票,所以 PHP 8 中将会支持 JIT。
另外值得一提的是,PHP JIT 对于使用 PHP 的网站来说提速可能并不明显,因为 JIT 在 CPU 密集型的代码上效果最好,而一般情况下,用 PHP 编写的程序都是 I/O 密集型的。
简单来说就是,PHP 程序往往受限于 I/O 而不是 CPU,使 PHP 代码运行速度变慢的因素往往是它们正在执行的 I/O 操作,包括连接、读取和写入数据库、高速缓存、文件与套接字等。
PHP 中 CPU 密集型代码的一个例子是 Zend/bench.php。
那么 PHP 中的 JIT 将会在哪里发挥作用呢?答案是数学领域。
关于 PHP JIT 的详细介绍,可以查看:https://blog.krakjoe.ninja/2019/03/php-gr8.html
③乔·沃特金斯-Musings, ninja ones-思考,忍者
Saturday, 30 March 2019
PHP GR8
除非您生活在一块岩石上,或者是过去的(在这种情况下,欢迎您),否则您将意识到JIT即将到PHP 8:今天的投票悄无声息地结束,绝大多数人赞成合并到PHP 8中,所以它是正式的。
为庆祝而抛出一些疯狂的形状,如图1所示,它甚至被称为“(底特律)准时制”……
现在坐下来阅读下面的神话破坏文章,我们将消除对这些东西的困惑。 JIT是什么,它将带来什么好处,并深入研究它的工作原理(但只有一点点,因为我不想让您感到无聊)。
由于我不知道我在跟谁说话,所以我将首先从简单的问题开始,然后再处理复杂的问题,如果您已经确定知道标题中的问题的答案,您可以跳过该部分…
什么是准时制?
PHP实现了一个虚拟机,一种虚拟处理器-我们称之为Zend VM。PHP将您可读的脚本编译为虚拟机可以理解的指令(我们称它们为操作码),此执行阶段称为“编译时间”。在执行的“运行时”阶段,虚拟机(Zend VM)将执行您的代码指令(操作码)。
这一切都很好,并且诸如APC(过去)和OPCache(今天)之类的工具会缓存代码的指令(操作码),以便“编译时间”仅在必须时发生。
首先,用一行来解释一般的JIT:即时是一种编译器策略,它采用中间代码表示并将其在运行时转换为与体系结构相关的机器代码-即时执行。
在PHP中,这意味着JIT将为Zend VM生成的指令视为中间表示,并发出与体系结构相关的机器代码,因此,代码的宿主不再是Zend VM,而直接是您的CPU。
为什么PHP需要JIT?
从Facebook HHVM项目的健康竞争开始,自PHP 7.0出现之前就一直是PHP内部社区的关注焦点。PHP 7.0的大多数核心更改都包含在PHPNG补丁中,该补丁大大改善了PHP在其核心中利用内存和CPU的方式,此后我们每个人都被迫密切关注性能。
从PHP 7.0开始,已经进行了一些性能改进,对HashTable(PHP的核心数据结构)进行了优化,在Zend VM中对某些操作码进行了专门化,对编译器进行了对某些序列的专门化,以及对Optimizer组件的持续改进OPCache …以及其他很多东西,太无聊了。
荒谬的事实是,这些优化只能将我们带走,而我们正在迅速接近或可能已经遇到了进一步改进它的能力。
警告:当我们说诸如“我们无法进一步改进”之类的东西时,我们真正的意思是,“我们必须做出的进一步改进的权衡不再具有吸引力”……每当我们谈论时关于性能优化,我们正在谈论取舍。通常,要在性能方面进行权衡取舍。我们都想认为最简单的代码是最快的代码,但是在现代C编程世界中情况并非如此。最快的代码通常是准备利用依赖于体系结构的内在函数或依赖于平台(编译器)的内置函数的代码。简单性并不能保证最佳性能…
目前,将PHP转换为JIT的能力似乎是从PHP压缩性能的最佳方法。
JIT可以使我的网站更快吗?
可能性不大。
也许不是您所期望的答案:通常,用PHP编写的应用程序受I / O约束,而JIT在受CPU约束的代码上效果最佳。
“ I / O和CPU绑定”到底是什么意思?
当我们要描述一段代码或应用程序的一般性能特征时,我们使用术语I / O绑定和CPU绑定。
用最简单的术语来说:
如果我们可以改进(减少,优化)它正在执行的I / O,则一段受I / O约束的代码会更快。
如果我们可以改善(减少,优化)CPU正在执行的指令,或者(神奇地)提高CPU的时钟速度,则CPU约束的代码段将更快。
一段代码或一个应用程序可能受I / O约束,CPU约束或同等地绑定到CPU和I / O。
通常,PHP应用程序倾向于受I / O约束-放慢它们的速度是它们正在执行的I / O-连接,读取和写入数据库,缓存,文件,套接字等。
CPU绑定的PHP是什么样的?
由于大多数PHP应用程序的本质,CPU绑定代码并不是很多PHP程序员都会熟悉的东西-他们的工作往往是连接到某个数据库,或者可能是缓存,进行一些轻松的工作并吐出html / json / xml响应。
您可能会四处查看代码库,发现许多与I / O无关的代码,甚至正在调用完全与I / O断开连接的函数的代码,并且很困惑我似乎暗示这并没有使即使与非I / O相比,处理非I / O的代码行可能更多,但仍受应用程序CPU的限制。
PHP实际上相当快,它是世界上最快的解释语言之一。Zend VM调用与I / O无关的函数与在机器代码中进行相同的调用之间没有显着差异。显然有区别,但是事实是机器代码具有调用约定,而Zend VM具有调用约定,机器代码具有序言,而Zend VM具有序言:您是否在Zend Opcodes或机器代码中调用some_c_level_function()不会对进行调用的应用程序的性能产生重大影响-尽管似乎可以对该调用产生重大影响。
注意:调用约定 (大致) 是在进入另一个函数之前执行的指令序列,而序言是在进入另一个函数*时执行的指令序列:在两种情况下,调用约定都将参数压入堆栈,序言将它们从堆栈中弹出。
关于循环,尾部调用和XI的问题是什么呢:PHP实际上非常聪明,启用了OPCache的Optimizer组件后,您的代码就可以像魔术一样转换为您可能编写的最高效的形式。
现在必须注意,JIT不会从VM建立的约定中更改Zend函数的调用约定-Zend必须能够随时在JIT和VM模式之间切换,因此决定保留调用约定由VM建立。结果,在JIT中,您到处都可以看到的那些呼叫并没有明显地更快。
如果您想查看CPU绑定的PHP代码是什么样子,请查看Zend / bench.php …这显然是CPU绑定代码的一个极端示例,但是应该可以理解JIT真正发挥作用的地方是数学领域。
PHP是否做出了最终的权衡以提高数学速度?
否。我们这样做是为了扩展PHP的范围,并且相当大地如此。
不希望自己大声疾呼,我们已经覆盖了整个网络-如果您是2019年的Web程序员,并且尚未考虑将PHP用于下一个项目,那么您做错了Web-在这个非常偏颇的PHP开发人员看来。
乍一看,要提高在PHP中更快地执行数学的能力,范围似乎很狭窄。
但是,这实际上为诸如机器学习,3d渲染,2d(gui)渲染和数据分析之类的事情打开了大门。
为什么在PHP 7.4中不能使用它?
我只是将JIT称为“最终的折衷”,我认为是:它可以说是有史以来发明的最复杂的编译器策略之一,也许是最复杂的。引入JIT会带来相当大的复杂性。
如果您问Dmitry(JIT的作者)是否使PHP变得复杂,他会说“不,我讨厌复杂性”(这是直接引述)。
从根本上讲,复杂是我们所不了解的一切,目前,很少有真正了解我们所拥有的JIT实现的内部开发人员(少于少数)。
PHP 7.4很快出现,与PHP 7.4合并将为我们提供一个PHP版本,很少有人可以调试,修复或改进(从任何实际意义上来说)。对于那些拒绝加入PHP 7.4的人来说,这只是不可接受的情况。
在从现在到PHP 8的这段时间里,我们中的许多人将在业余时间工作以了解JIT:我们仍然具有要实现的功能以及需要为PHP 8重写的工具,首先我们必须了解JIT。我们需要这段时间,非常感谢大多数选民认为有资格将它赠送给我们。
复合体不是可怕的代名词:复合体可以像星云一样美丽,而JIT就是这种复合体。原则上,您可以完全理解复杂的事物,而只能稍微减少该事物的表面复杂性。换句话说,即使有20位内部开发人员像Dmitry一样熟悉JIT,也并不能真正改变JIT的复杂性。
PHP的开发会变慢吗?
没有理由认为会。我们有足够的时间可以自信地说,到PHP 8普遍可用时,我们中已经有足够的人熟悉JIT,至少在今天和我们在修复错误和推动PHP前进方面一样好。
当试图将JIT本质上是复杂的观点与之相提并论时,请考虑我们花在介绍新功能上的大部分时间实际上是在讨论该功能。对于大多数功能,甚至修复程序,编写代码可能需要几分钟或几小时的时间,而讨论则可能需要数周或数月的时间。在极少数情况下,某个功能的代码编写可能需要花费数小时或数天的时间,但是在那些极少数情况下,讨论总是会花费更长的时间。
这就是我要说的…
周末愉快。