Baiduエンジニアが語るPHP機能の実装原理とパフォーマンス分析(1)
はじめに 参照元:
どの言語においても、関数は最も基本的な構成要素です。 PHPの関数にはどのような特徴があるのでしょうか?関数呼び出しはどのように実装されますか? php 関数のパフォーマンスはどうですか? 使用方法について何か提案はありますか?この記事では、実装を理解しながら PHP プログラムをより適切に作成できるように、原理を分析し、実際のパフォーマンス テストと組み合わせることで、これらの質問に答えていきます。同時に、いくつかの一般的な PHP 関数が紹介されます。
PHPの関数の分類
PHPでは関数を横に分けるとユーザー関数(組み込み関数)と内部関数(組み込み関数)の2つに分類されます。前者はプログラム内でユーザーがカスタマイズした一部の関数やメソッドであり、後者はPHP自体が提供する各種ライブラリ関数(sprintfやarray_pushなど)です。ユーザーは、後で紹介する拡張メソッドを使用してライブラリ関数を作成することもできます。ユーザー関数は、関数 (関数) とメソッド (クラス メソッド) に分類できます。この記事では、これら 3 つの関数をそれぞれ分析してテストします。
php関数の実装
php関数は最終的にどのように実行されるのでしょうか?
この質問に答えるために、まず PHP コードを実行するプロセスを見てみましょう。
図 1 からわかるように、PHP は典型的な動的言語実行プロセスを実装しています。コードの一部を取得した後、字句解析、構文解析、その他の段階を経て、ソース プログラムが命令 (オペコード) に変換されます。 ZEND 仮想マシンはこれらの命令を順番に実行して操作を完了します。 Php 自体は C で実装されているため、最終的に呼び出される関数はすべて C の関数です。実際には、PHP は C で開発されたソフトウェアと考えることができます。上記の説明から、PHP での関数の実行は呼び出し用のオペコードにも変換されることがわかります。各関数呼び出しは実際に 1 つ以上の命令を実行します。
各関数について、zend は次のデータ構造で記述されます
type は関数のタイプを示します: ユーザー関数、組み込み関数、オーバーロード関数。 Commonには、関数名、パラメータ情報、関数フラグ(通常関数、静的メソッド、抽象メソッド)などの関数の基本情報が含まれます。また、ユーザー関数については、内部変数などを記録した関数シンボルテーブルもありますが、詳細は後述します。 Zend は、大規模なハッシュ テーブルであるグローバル function_table を維持します。関数が呼び出されると、まず関数名に基づいてテーブルから対応する zend_function が検索されます。関数呼び出しを行うとき、仮想マシンは関数の種類に基づいて呼び出し方法を決定します。関数の種類が異なれば、実行原理も異なります。
組み込み関数
組み込み関数は基本的に実際の C 関数であり、PHP は最終コンパイル後に zif_xxxx という名前の関数に展開します。たとえば、一般的な sprintf は最下層の zif_sprintf に対応します。 Zend の実行中に組み込み関数が見つかった場合、転送操作が実行されます。
Zend は、パラメーターの取得、配列操作、メモリ割り当てなどを含む、呼び出し用の一連の API を提供します。組み込み関数のパラメータは、zend_parse_parameters メソッドを通じて取得されます。配列や文字列などのパラメータについては、zend は浅いコピーを実装しているため、この効率は非常に高くなります。 PHP 組み込み関数の場合、転送呼び出しが追加されるだけで、その効率は対応する C 関数の効率とほぼ同じであると言えます。
組み込み関数は、so を通じて PHP に動的にロードされます。ユーザーは、ニーズに応じて対応する so を記述することもできます。これは、私たちがよく拡張機能と呼ぶものです。 ZEND は拡張機能で使用するための一連の API を提供しています
ユーザー関数
組み込み関数と比較して、PHP で実装されるユーザー定義関数は、実行プロセスと実装原理がまったく異なります。上で述べたように、PHP コードは実行のためにオペコードに変換されることがわかっており、ユーザー関数も例外ではありません。実際、各関数は一連のオペコードに対応しており、この一連の命令は zend_function に保存されます。したがって、ユーザー関数の呼び出しは、最終的に一連のオペコードの実行に対応します。
》》ローカル変数の保存と再帰の実装
関数の再帰はスタックを通じて完了することが分かりました。 php では、これを実現するために同様の方法が使用されます。 Zend は、アクティブ シンボル テーブル (active_sym_table) を各 PHP 関数に割り当て、現在の関数内のすべてのローカル変数のステータスを記録します。すべてのシンボル テーブルはスタックの形式で維持され、関数が呼び出されるたびに、新しいシンボル テーブルが割り当てられ、スタックにプッシュされます。呼び出しが終了すると、現在のシンボル テーブルがスタックからポップされます。これにより、状態の保存と再帰が可能になります。
スタックのメンテナンスのために、zend はここで最適化を行いました。スタックをシミュレートするために長さ N の静的配列を事前に割り当てます。静的配列を使用して動的データ構造をシミュレートするこの方法は、呼び出しごとに発生するメモリ割り当てを回避します。 ZEND は、関数呼び出しの終了時に現在のスタックの最上位にあるシンボル テーブル データをクリーンアップするだけです。静的配列の長さは N であるため、関数呼び出しレベルが N を超えると、プログラムはスタック オーバーフローを引き起こすことはなく、この場合、zend はシンボル テーブルを割り当てて破棄し、大幅なパフォーマンスの低下を引き起こします。 zend では、N の現在の値は 32 です。したがって、PHP プログラムを作成するときは、関数呼び出しレベルが 32 を超えないようにすることが最善です。もちろんWebアプリケーションであれば関数呼び出しレベル自体は深くても構いません。
》》パラメータの転送は、zend_parse_params を呼び出してパラメータを取得する組み込み関数とは異なります。ユーザー関数でのパラメータの取得は命令によって完了します。関数が持つパラメータの数は命令の数に対応します。実装に特有の、通常の変数の代入です。上記の分析から、組み込み関数と比較して、スタック テーブルが独自に維持され、各命令が C 関数として実行されるため、ユーザー関数のパフォーマンスが相対的に大幅に低下することがわかります。具体的な比較分析は後ほど。したがって、関数に対応する PHP 組み込み関数がある場合は、その関数を自分で書き直して実装しないようにしてください。
Baiduエンジニアが語るPHP機能の実装原理と性能分析(1)
http://www.lai18.com/content/410091.html
以上、Baidu エンジニアが解説する PHP 機能の実装原理とパフォーマンス分析 (1) を内容も含めて紹介しましたが、PHP チュートリアルに興味のある友人の参考になれば幸いです。