BaiduのエンジニアはPHP関数の実現原理と性能分析(一)を話しています。
前言
どの言語においても、関数は最も基本的な構成要素である。phpの関数については、どのような特徴がありますか?関数コールはどうやって実現されますか?php関数の性能はどうですか?本論文では原理から分析し、実際の性能テストと組み合わせてこれらの問題を答えてみます。いくつかの一般的なphp関数を紹介します。
php関数の分類
phpでは、横に分けると、関数は大きく2つに分類されます。user function(内蔵関数)とinternal function(内蔵関数)です。前者はユーザーがプログラムでカスタマイズした関数と方法であり、後者はphp自身が提供する各種ライブラリ関数(例えばsprintf、array_)である。プッシュなど)。ユーザーも拡張法によりライブラリ関数を作成することができます。これは後述します。user functionについては、function(関数)とmethod(クラス方法)に細分化することができます。ここではこれらの3つの関数についてそれぞれ分析とテストを行います。
php関数の実装
一つのphp関数は最終的にどのように実行されますか?
この質問に答えるには、まずphpコードの実行過程を見てみます。
図1から見ると、phpは典型的な動的言語実行プロセスを実現しています。一部のコードを取得した後、語法解析、文法解析などの段階を経て、ソースプログラムは一つ一つの命令(opcodes)に翻訳され、ZEND仮想マシンは順次これらの命令を実行します。Php自体はcで実現されていますので、最終的に呼び出したのもcの関数です。実際には、phpをcで開発されたソフトウェアとみなすことができます。上記の説明により、phpにおける関数の実行も、opcodesに翻訳されて呼び出され、各関数呼び出しは、実際には1つ以上の命令を実行していることが分かります。
各関数について、zedは以下のデータ構造で記述されている。
関数を内蔵
内蔵関数は、本質的には本当のc関数であり、各内蔵関数は、phpが最終的にコンパイルされた後に展開されます。zif_という名前です。xxxxのfunction、例えば私達のよくあるsprintf、対応は一体層はzif_です。スプリング。Zendは実行する時、内蔵関数が発見されたら、簡単に転送操作をするだけです。
Zendは、パラメータの取得、配列操作、メモリの割り当てなどを含む一連のアプリを提供しています。内蔵関数のパラメータは、zend_で取得します。パーシ_配列、文字列などのパラメータに対して、zedは浅いコピーを実現するので、この効率は高いです。このように、php内蔵関数に対しては、その効率は、対応するc関数とほぼ同じであり、一回だけ転送呼び出しが多くなっている。
内蔵関数はphpではso方式で動的にロードされています。ユーザーも必要に応じて自分でそのsoを作成できます。つまり、私たちがよく話している拡張機能です。ZENDは拡張のための一連のアプリを提供しています。
ユーザー関数
内蔵関数と比較して、ユーザがphpで実現するカスタム関数は、実行プロセスと実装原理が全く異なる。前に述べたように、phpコードは翻訳されて一つのopcodeになって実行されることを知っています。ユーザー関数も例外ではありません。実際には各関数は一つのopcodeに対応しています。このコマンドはzend_に保存されています。function中です。したがって、ユーザ関数の呼び出しは、最終的には、一連のopcodesの実行に対応する。
」局所変数の保存と再帰的な実現
関数再帰はスタックによって達成されることを知っている。phpにおいても同様の方法で実現される。Zendは各php関数に活動符号表を割り当てました。sym_テーブル)は、現在の関数のすべての局所変数の状態を記録します。すべての記号表はスタック形式で維持され、関数が呼び出されるたびに、新しい記号表を割り当ててスタックに組み込まれる。呼び出しが完了すると、現在の記号がスタックから出ます。これにより、状態の保存と再帰性が実現される。
スタックのメンテナンスについて、zedはここで最適化しました。あらかじめNの長さの静的配列を割り当ててスタックをシミュレートします。このような静的配列を通して動的データ構造をシミュレートする手法は、私たち自身のプログラムでもよく使われています。この方法は、呼び出しごとにメモリの割り当てや廃棄を回避します。ZEENDは、関数呼び出しが終了した時点で、現在スタックトップの符号表データをクリアすれば良いです。静的配列長はNであるため、一旦関数がNを超えると、プログラムがスタックオーバーフローしなくなり、この場合、zendは符号表の割り当て、破壊を行うので、性能が低下することが多い。zendでは、Nは現在32値を取っています。したがって、私たちはphpプログラムを作成する時、関数の呼び出しレベルは32を超えないほうがいいです。もちろん、webアプリケーションであれば、レベルの深さを関数で呼び出すことができます。
』パラメータの伝達と内蔵関数の呼び出しパーシ_バラmsによるパラメータの取得は、ユーザ関数におけるパラメータの取得は命令により行われます。関数にはいくつかのパラメータがあります。いくつかの命令に対応します。具体的に実現すると、普通の変数の割り当てです。上の分析から、内蔵関数と比較して、自分でスタック表を維持しているため、各コマンドの実行もc関数であり、ユーザー関数の性能は相対的に悪くなり、後には具体的な比較分析があることが分かります。したがって、一つの機能がある場合は、php内蔵関数に対応して、できるだけ自分で関数を書き換えないでください。
どの言語においても、関数は最も基本的な構成要素である。phpの関数については、どのような特徴がありますか?関数コールはどうやって実現されますか?php関数の性能はどうですか?本論文では原理から分析し、実際の性能テストと組み合わせてこれらの問題を答えてみます。いくつかの一般的なphp関数を紹介します。
php関数の分類
phpでは、横に分けると、関数は大きく2つに分類されます。user function(内蔵関数)とinternal function(内蔵関数)です。前者はユーザーがプログラムでカスタマイズした関数と方法であり、後者はphp自身が提供する各種ライブラリ関数(例えばsprintf、array_)である。プッシュなど)。ユーザーも拡張法によりライブラリ関数を作成することができます。これは後述します。user functionについては、function(関数)とmethod(クラス方法)に細分化することができます。ここではこれらの3つの関数についてそれぞれ分析とテストを行います。
php関数の実装
一つのphp関数は最終的にどのように実行されますか?
この質問に答えるには、まずphpコードの実行過程を見てみます。
図1から見ると、phpは典型的な動的言語実行プロセスを実現しています。一部のコードを取得した後、語法解析、文法解析などの段階を経て、ソースプログラムは一つ一つの命令(opcodes)に翻訳され、ZEND仮想マシンは順次これらの命令を実行します。Php自体はcで実現されていますので、最終的に呼び出したのもcの関数です。実際には、phpをcで開発されたソフトウェアとみなすことができます。上記の説明により、phpにおける関数の実行も、opcodesに翻訳されて呼び出され、各関数呼び出しは、実際には1つ以上の命令を実行していることが分かります。
各関数について、zedは以下のデータ構造で記述されている。
typedef union _zend_function {
zend_uchar type; /* MUST be the first element of this struct! */
struct {
zend_uchar type; /* never used */
char *function_name;
zend_class_entry *scope;
zend_uint fn_flags;
union _zend_function *prototype;
zend_uint num_args;
zend_uint required_num_args;
zend_arg_info *arg_info;
zend_bool pass_rest_by_reference;
unsigned char return_reference;
} common;
zend_op_array op_array;
zend_internal_function internal_function;
} zend_function;
typedef struct _zend_function_state {
HashTable *function_symbol_table;
zend_function *function;
void *reserved[ZEND_MAX_RESERVED_RESOURCES];
} zend_function_state;
typeには関数の種類が表記されています。ユーザ関数、内蔵関数、重負荷関数です。Commonには関数の基本情報が含まれています。関数名、パラメータ情報、関数フラグ(一般関数、静的方法、抽象的方法)などがあります。また、ユーザー関数については、関数記号表があり、内部変数などが記録されています。これは後述します。Zendは全体のfunction_を維持しました。テーブル、これは大きいハスの時計です。関数の呼び出しは、まず関数名に基づいてテーブルから対応するzend(u)を見つけます。機能。関数呼び出しを行う場合、仮想機会は、typeによって呼び出し方法を決定し、異なるタイプの関数の実行原理は異なります。関数を内蔵
内蔵関数は、本質的には本当のc関数であり、各内蔵関数は、phpが最終的にコンパイルされた後に展開されます。zif_という名前です。xxxxのfunction、例えば私達のよくあるsprintf、対応は一体層はzif_です。スプリング。Zendは実行する時、内蔵関数が発見されたら、簡単に転送操作をするだけです。
Zendは、パラメータの取得、配列操作、メモリの割り当てなどを含む一連のアプリを提供しています。内蔵関数のパラメータは、zend_で取得します。パーシ_配列、文字列などのパラメータに対して、zedは浅いコピーを実現するので、この効率は高いです。このように、php内蔵関数に対しては、その効率は、対応するc関数とほぼ同じであり、一回だけ転送呼び出しが多くなっている。
内蔵関数はphpではso方式で動的にロードされています。ユーザーも必要に応じて自分でそのsoを作成できます。つまり、私たちがよく話している拡張機能です。ZENDは拡張のための一連のアプリを提供しています。
ユーザー関数
内蔵関数と比較して、ユーザがphpで実現するカスタム関数は、実行プロセスと実装原理が全く異なる。前に述べたように、phpコードは翻訳されて一つのopcodeになって実行されることを知っています。ユーザー関数も例外ではありません。実際には各関数は一つのopcodeに対応しています。このコマンドはzend_に保存されています。function中です。したがって、ユーザ関数の呼び出しは、最終的には、一連のopcodesの実行に対応する。
」局所変数の保存と再帰的な実現
関数再帰はスタックによって達成されることを知っている。phpにおいても同様の方法で実現される。Zendは各php関数に活動符号表を割り当てました。sym_テーブル)は、現在の関数のすべての局所変数の状態を記録します。すべての記号表はスタック形式で維持され、関数が呼び出されるたびに、新しい記号表を割り当ててスタックに組み込まれる。呼び出しが完了すると、現在の記号がスタックから出ます。これにより、状態の保存と再帰性が実現される。
スタックのメンテナンスについて、zedはここで最適化しました。あらかじめNの長さの静的配列を割り当ててスタックをシミュレートします。このような静的配列を通して動的データ構造をシミュレートする手法は、私たち自身のプログラムでもよく使われています。この方法は、呼び出しごとにメモリの割り当てや廃棄を回避します。ZEENDは、関数呼び出しが終了した時点で、現在スタックトップの符号表データをクリアすれば良いです。静的配列長はNであるため、一旦関数がNを超えると、プログラムがスタックオーバーフローしなくなり、この場合、zendは符号表の割り当て、破壊を行うので、性能が低下することが多い。zendでは、Nは現在32値を取っています。したがって、私たちはphpプログラムを作成する時、関数の呼び出しレベルは32を超えないほうがいいです。もちろん、webアプリケーションであれば、レベルの深さを関数で呼び出すことができます。
』パラメータの伝達と内蔵関数の呼び出しパーシ_バラmsによるパラメータの取得は、ユーザ関数におけるパラメータの取得は命令により行われます。関数にはいくつかのパラメータがあります。いくつかの命令に対応します。具体的に実現すると、普通の変数の割り当てです。上の分析から、内蔵関数と比較して、自分でスタック表を維持しているため、各コマンドの実行もc関数であり、ユーザー関数の性能は相対的に悪くなり、後には具体的な比較分析があることが分かります。したがって、一つの機能がある場合は、php内蔵関数に対応して、できるだけ自分で関数を書き換えないでください。