1.原点
PHP に関して、多くの人の直観的な印象として、PHP は豊富なライブラリ クラスを備えた柔軟なスクリプト言語であり、使いやすく、安全で、WEB 開発に非常に適していますが、パフォーマンスは低いです。 PHP のパフォーマンスは本当に誰もが感じているほど悪いのでしょうか?この記事ではそのようなトピックに焦点を当てます。ソース コード、アプリケーション シナリオ、ベンチマーク パフォーマンス、比較分析などの側面から PHP パフォーマンスの問題を詳細に分析し、実際のデータを通じて語ります。
2. PHPのパフォーマンスを原理から分析する
PHP のパフォーマンスを原理から、主にメモリ管理、変数、関数、動作メカニズムの側面から分析します。
2.1 メモリ管理
Nginx のメモリ管理方法と同様に、PHP も内部的にメモリ プールに基づいており、メモリ プールのライフサイクル概念を導入しています。メモリ プールに関しては、PHP は PHP スクリプトと拡張機能のメモリ関連のすべての操作を管理します。大規模メモリと小規模メモリの管理には、さまざまな実装方法と最適化が使用されます。詳細については、https://wiki.php.net/internals/zend_mm のドキュメントを参照してください。メモリの割り当てとリサイクルのライフサイクル中に、PHP は初期化アプリケーション + 動的拡張 + メモリ識別リサイクル メカニズムを使用し、各リクエストの後にメモリ プールを直接再マスクします。
2.2 変数
ご存知のとおり、PHP は弱い変数型の言語であるため、PHP 内では、すべての PHP 変数は Zval 型に対応し、これは具体的に次のように定義されます。
図 1 PHP 変数
変数に関しては、PHP は参照カウントやライター メカニズムのコピーなど、多くの最適化作業を行ってきました。これにより、メモリ使用量が確実に最適化され、メモリ コピーの数が削減されます (http://blog.xiuwz.com/2011/11/09 /php-using-internal-zval/ を参照してください)。配列に関しては、PHP は効率的なハッシュテーブルを内部で使用して実装します。
2.3 機能
PHP 内では、すべての PHP 関数が内部関数ポインターに変換されます。例えば拡張機能
リーリー
リーリー
2.4 動作メカニズム
PHP のパフォーマンスについて話すとき、多くの人は「C/C++ はコンパイルされ、JAVA はセミコンパイルされ、PHP はインタープリタされます」と言うでしょう。つまり、PHP は最初に動的に解析されてからコードが実行されるため、この観点から見ると、PHP のパフォーマンスは非常に悪いはずです。
実際、PHP スクリプトの実行による出力は、動的解析とコード実行のプロセスです。具体的には、PHP スクリプトの実行メカニズムは次の図に示すとおりです。
図2 PHPの動作仕組み
PHP の実行フェーズも 3 つのフェーズに分かれています:
解析します。構文解析段階。
2.5 動的操作
上記の分析から判断すると、PHP はメモリ管理、変数、関数、操作メカニズムなどで多くの作業を行ってきました。したがって、原理的な観点からは、PHP にパフォーマンスの問題はなく、そのパフォーマンスは少なくとも同等であるはずです。 Java に比較的近いので良いです。
ここで、PHP の動的言語の特性によって引き起こされるパフォーマンスの問題について説明する必要があります。PHP は動的ランタイムであるため、すべての変数、関数、オブジェクト呼び出し、スコープの実装などは実行フェーズで決定されます。これは、PHP のパフォーマンスで変更するのが難しいいくつかのことを根本的に決定します。C/C++ の静的コンパイル段階で決定できる変数と関数は、PHP の動的実行で決定する必要があり、これにより、PHP 中間コードを直接実行できないことも決定されます。 Zend Engine で実行する必要があります。
PHP 変数の具体的な実装に関しては、もう 1 つ、ハッシュテーブルについて話さなければなりません。 Hashtable は PHP の魂の 1 つと言え、変数シンボル スタック、関数シンボル スタックなど、すべて Hashtable をベースにした PHP 内で広く使用されています。
PHP 変数を例として、PHP の動的な操作特性を説明します。たとえば、次のコードです。
<?php
该代码的执行结果就是在变量符号栈(是一个hashtable)中新增一个项
当要使用到该变量时候,就去变量符合栈中去查找(也就是变量调用对出了一个hash查找的过程)。
同样对于函数调用也基本上类似有一个函数符号栈(hashtable)。
其实关于动态运行的变量查找特点,在PHP的运行机制中也能看出一些。PHP代码通过解释、编译后的流程下图:
图3 PHP运行实例
从上图可以看出,PHP代码在compile之后,产出的了类符号表、函数符号表、和OPCODE。在真正执行的时候,zend Engine会根据op code去对应的符号表中进行查找,处理。
从某种程度上,在这种问题的上,很难找到解决方案。因为这是由于PHP语言的动态特性所决定的。但是在国内外也有不少的人在寻找解决方案。因为通过这样,能够从根本上完全的优化PHP。典型的列子有facebook的hiphop(https://github.com/facebook/hiphop-php)。
2.6结论
从上面分析来看,在基础的内存管理、变量、函数、运行机制方面,PHP本身并不会存在明显的性能差异,但由于PHP的动态运行特性,决定了PHP和 其他的编译型语言相比,所有的变量查找、函数运行等等都会多一些hash查找的CPU开销和额外的内存开销,至于这种开销具体有多大,可以通过后续的基准 性能和对比分析得出。
因此,也可以大体看出PHP不太适合的一些场景:大量计算性任务、大数据量的运算、内存要求很严格的应用场景。如果要实现这些功能,也建议通过扩展的方式实现,然后再提供钩子函数给PHP调用。这样可以减低内部计算的变量、函数等系列开销。
3.基准性能
对于PHP基准性能,目前缺少标准的数据。大多数同学都存在感性的认识,有人认为800QPS就是PHP的极限了。此外,对于框架的性能和框架对性能的影响很没有响应的权威数字。
本章节的目的是给出一个基准的参考性能指标,通过数据给大家一个直观的了解。
具体的基准性能有以下几个方面:
1.裸PHP性能。完成基本的功能。
2.裸框架的性能。只做最简单的路由分发,只走通核心功能。
3.标准模块的基准性能。所谓标准模块的基准性能,是指一个具有完整服务模块功能的基准性能。
3.1环境说明
测试环境:
Uname -a
Linux db-forum-test17.db01.baidu.com 2.6.9_5-7-0-0 #1 SMP Wed Aug 12 17:35:51 CST 2009 x86_64 x86_64 x86_64 GNU/Linux
Red Hat Enterprise Linux AS release 4 (Nahant Update 3)
8 Intel(R) Xeon(R) CPU E5520 @ 2.27GHz
软件相关:
Nginx:
nginx version: nginx/0.8.54 built by gcc 3.4.5 20051201 (Red Hat 3.4.5-2)
Php5:(采用php-fpm)
PHP 5.2.8 (cli) (built: Mar 6 2011 17:16:18)
Copyright (c) 1997-2008 The PHP Group
Zend Engine v2.2.0, Copyright (c) 1998-2008 Zend Technologies
with eAccelerator v0.9.5.3, Copyright (c) 2004-2006 eAccelerator, by eAccelerator
bingo2:
PHP框架。
其他说明:
目标机器的部署方式:脚本。
测试压力机器和目标机器独立部署。
3.2裸PHP性能
最简单的PHP脚本。
<?php
通过压力工具测试结果如下:
3.3裸PHP框架性能
为了和3.2的对比,基于bingo2框架实现了类似的功能。代码如下
<?php
压力测试结果如下:
从该测试结果可以看出:框架虽然有一定的消耗,但对整体的性能来说影响是非常小的。
3.4标准PHP模块的基准性能
所谓标准PHP模块,是指一个PHP模块所必须要具体的基本功能:
路由分发。
自动加载。
LOG初始化&Notice日志打印。所以的UI请求都一条标准的日志。
采用bingo2的代码自动生成工具产生标准的测试PHP模块:test。
测试结果如下:
3.5结论
从测试数据的结论来看,PHP本身的性能还是可以的。基准性能完全能够达到几千甚至上W的QPS。至于为什么在大多数的PHP模块中表现不佳,其实 这个时候更应该去找出系统的瓶颈点,而是简单的说OK,PHP不行,那我们换C来搞吧。(下一个章节,会通过一些例子来对比,采用C来处理不见得有特别的 优势)
通过基准数据,可以得出以下几个具体的结论:
1.PHP本身性能也很不错。简单功能下能够达到5000QPS,极限也能过W。
2.PHP框架本身对性能影响非常有限。尤其是在有一定业务逻辑和数据交互的情况下,几乎可以忽略。
3.一个标准的PHP模块,基准性能能够达到2000QPS(80 cpu idle)。
4.对比分析
很多时候,大家发现PHP模块性能不行的时候,就来一句“ok,我们采用C重写吧”。在公司内,采用C/C++来写业务逻辑模块的现象到处都有,在前几年甚至几乎全部都是采用C来写。那时候大家写的真是一个痛苦:调试难、敏捷不要谈。
文章出自:baidu-tech.com