ホームページ  >  記事  >  バックエンド開発  >  PHP_PHP チュートリアルの関数操作メカニズムを調べる

PHP_PHP チュートリアルの関数操作メカニズムを調べる

WBOY
WBOYオリジナル
2016-07-13 10:33:47908ブラウズ

どの言語でも、関数は最も基本的な構成要素です。 PHPの関数にはどのような特徴があるのでしょうか?関数呼び出しはどのように実装されますか? PHP 関数のパフォーマンスはどうですか? 使用上の提案はありますか?この記事では、原則に基づいて分析し、実際のパフォーマンス テストと組み合わせてこれらの質問に答え、実装を理解しながら PHP プログラムをより適切に作成できるようにします。同時に、いくつかの一般的な PHP 関数が紹介されます。

PHP関数の分類

PHPでは、関数を横に分けるとユーザー関数(組み込み関数)と内部関数(組み込み関数)の2つに分類されます。前者はプログラム内でユーザーがカスタマイズした一部の関数やメソッドであり、後者はPHP自体が提供する各種ライブラリ関数(sprintfやarray_pushなど)です。ユーザーは、後で紹介する拡張メソッドを使用してライブラリ関数を作成することもできます。ユーザー関数は、関数 (関数) とメソッド (クラス メソッド) に分類できます。この記事では、これら 3 つの関数をそれぞれ分析してテストします。

php関数の実装

PHP 関数は最終的にどのように実行されるのですか?

この質問に答えるために、まず PHP コードを実行するプロセスを見てみましょう。

PHP_PHP チュートリアルの関数操作メカニズムを調べる

上の図からわかるように、PHP は典型的な動的言語の実行プロセスを実装しています。コードの一部を取得した後、字句解析、構文解析、その他の段階を経て、ソース プログラムが命令 (オペコード) に変換され、 ZEND 仮想マシンはこれらの命令を順番に実行して操作を完了します。 Php 自体は C で実装されているため、最終的に呼び出される関数はすべて C の関数です。実際には、PHP は C で開発されたソフトウェアと考えることができます。

上記の説明から、PHP での関数の実行も呼び出し用のオペコードに変換されることがわかります。実際には、各関数呼び出しが 1 つ以上の命令を実行します。

各関数について、zend は次のデータ構造で記述されます。

リーリー

Type は関数の種類を示します: ユーザー関数、組み込み関数、オーバーロード関数。 Common には、関数名、パラメータ情報、関数フラグ (通常関数、静的メソッド、抽象メソッド) などの関数の基本情報が含まれます。

組み込み関数

組み込み関数は、基本的に実際の 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 組み込み関数がある場合は、その関数を自分で書き直して実装しないようにしてください。

クラスメソッド

クラスメソッドの実行原理はユーザー関数と同じで、オペコードにも変換されて順番に呼び出されます。クラスの実装は、データ構造 zend_class_entry を使用して zend によって実装されます。この構造には、クラスに関連するいくつかの基本情報が格納されます。このエントリは、PHP のコンパイル時に処理されます。

zend_functionのcommonには、現在のメソッドに対応するクラスのzend_class_entryを指すscopeというメンバーがあります。 PHP におけるオブジェクト指向の実装については、ここでは詳しく説明しません。今後、PHP におけるオブジェクト指向の実装原理について詳しく説明する記事を書きます。関数に関する限り、メソッドの実装原理は関数の実装原理とまったく同じであり、そのパフォーマンスは理論的には同様です。詳細なパフォーマンスの比較は後で行います。

よく使われるPHP関数の実装と紹介

  • 数える
  • count はよく使用する関数で、その機能は配列の長さを返すことです。

    count 関数の複雑さはどれくらいですか?よく言われるのは、count 関数は配列全体を走査して要素の数を見つけるため、複雑さは O(n) になるということです。では、これは実際にそうなのでしょうか?

    count の実装に戻りましょう。ソース コードを見ると、配列の count 操作の場合、関数の最終パスは zif_count->php_count_recursive->zend_hash_num_elements であり、zend_hash_num_elements の動作は return ht であることがわかります。 ->nNumOfElements。これは O(n) ではなく O(1) 操作であることがわかります。実際、配列は PHP の下部にある hash_table であり、ハッシュ テーブルの場合、現在の要素の数を記録する特別な要素 nNumOfElements が zend にあるため、一般的なカウントの場合、この値が実際に直接返されます。このことから、count の複雑さは O(1) であり、特定の配列のサイズとは関係がないという結論が得られます。

    非配列型変数の count の動作は何ですか?設定されていない変数の場合は 0 を返し、int、double、string などの場合は 1 を返します。

    ストレン
  • Strlen は文字列の長さを返すために使用されます。それでは、彼の実装原則は何でしょうか?

    strlen が C の O(n) 関数であることは誰もが知っています。この関数は、文字列に遭遇するまで順番に文字列を走査します。

    さらに、非文字列型変数に対して strlen を呼び出す場合、まず変数を文字列に強制してから長さを検出することに注意する必要があります。

    isset と array_key_exists

  • これら 2 つの関数の最も一般的な使用法は、配列にキーが存在するかどうかを確認することです。ただし、前者は、変数が設定されているかどうかを判断するために使用することもできます。前述したように、isset は実関数ではないため、効率は後者よりもはるかに高くなります。 array_key_exists の代わりにこれを使用することをお勧めします。
  • array_push と array[]

  • どちらも配列の末尾に要素を追加します。違いは、前者は一度に複数のものをプッシュできることです。それらの最大の違いは、一方が関数であり、もう一方が言語構造であるため、後者の方が効率的であることです。したがって、通常に要素を追加するだけの場合は、array[] を使用することをお勧めします。
  • ランドとmt_rand

  • どちらも乱数を生成する機能を提供します。前者は libc 標準 rand を使用します。後者は、乱数生成器としてメルセンヌ ツイスターの既知の機能を使用しており、libc が提供する rand() よりも平均して 4 倍高速に乱数値を生成できます。したがって、より高いパフォーマンス要件がある場合は、前者の代わりに mt_rand の使用を検討できます。
  • rand が疑似乱数を生成することは誰もが知っています。C では、指定されたシードを表示するには srand を使用する必要があります。ただし、php では、rand はデフォルトで srand を 1 回呼び出します。通常の状況では、自分で明示的に呼び出す必要はありません。

    特別な状況で srand を呼び出す必要がある場合は、それに応じて呼び出す必要があることに注意してください。つまり、srand は rand に対応し、mt_srand は srand に対応します。これらを混合してはなりません。混合しないと無効になります。

    ソートとソート

  • どちらもソートに使用されます。違いは、前者は C の qsort や C++ の sort と同様にソート戦略を指定できることです。
  • ソートに関しては、どちらも標準のクイックソートを使用して実装されています。ソート要件がある場合は、特別な事情がない限り、PHP が提供するこれらのメソッドを呼び出すだけで済み、効率が向上します。はるかに低いです。その理由は、ユーザー関数と組み込み関数の以前の分析と比較でわかります。

  • urlencode と rawurlencode
  • これらはどちらも URL エンコードに使用され、-_ を除く文字列内のすべての非英数字は、その後に続く 2 つの 16 進数に置き換えられます。 2 つの唯一の違いは、スペースの場合、urlencode は + としてエンコードされるのに対し、rawurlencode は %20 としてエンコードされることです。

    一般に、検索エンジンを除いて、私たちの戦略はスペースを %20 としてエンコードすることです。したがって、主に後者が使用されます。エンコード シリーズとデコード シリーズを一緒に使用する必要があることに注意してください。

  • strcmpシリーズの機能
  • この一連の関数には strcmp、strncmp、strcasecmp、strncasecmp が含まれており、その実装関数は C 関数と同じです。ただし、php 文字列が許可されているため、違いがあります。

    さらに、PHP は文字列の長さを直接取得できるため、この点を最初にチェックし、多くの場合効率が大幅に高くなります。

    is_int と is_numeric
  • これら 2 つの関数の機能は似ていますが、まったく同じではありません。使用する場合は、その違いに注意する必要があります。

    Is_int: 変数の型が整数型であるかどうかを判断します。PHP 変数には型を表す特別なフィールドがあるため、この型を直接判断できます。これは絶対的な O(1) 操作です。

    Is_numeric: 変数が整数であるか数値文字列であるかを判断します。つまり、true を返す整数変数に加えて、文字列変数が「1234」、「1e4」などの形式であるかどうかを判断します。 . も真であると判断されます。このとき、文字列をたどって判定します。

まとめと提案

機能実装の原理分析とパフォーマンステストを通じて、次の結論をまとめました:

    PHP の関数呼び出しのオーバーヘッドは比較的大きいです。
  1. 関数関連の情報は大きな hash_table に格納され、呼び出されるたびに関数名がハッシュ テーブル内で検索されるため、関数名の長さもパフォーマンスに一定の影響を与えます。
  2. 関数の戻り参照には実際的な意味はありません。
  3. 組み込み PHP 関数のパフォーマンスは、特に文字列操作の場合、ユーザー関数のパフォーマンスよりもはるかに高くなります。
  4. クラスメソッド、通常の関数、静的メソッドの効率はほぼ同じで、大きな違いはありません。
  5. 空の関数呼び出しの影響を除けば、組み込み関数と同じ関数の C 関数のパフォーマンスは基本的に同じです。
  6. すべてのパラメータ転送は参照カウントを使用した浅いコピーであり、コストは非常に小さいです。
  7. 関数の数がパフォーマンスに与える影響はほとんど無視できます。
したがって、php 関数の使用については、次のような提案があります:

    関数は組み込み関数で完成させることができます。php 関数を自分で記述する代わりに、組み込み関数を使用してみてください。
  1. 特定の機能に高いパフォーマンス要件がある場合は、拡張機能を使用して実装することを検討できます。
  2. PHP 関数呼び出しはコストがかかるため、過度にカプセル化しないでください。一部の関数を何度も呼び出す必要があり、1 行または 2 行のコードだけで実装できる場合は、呼び出しをカプセル化しないことをお勧めします。
  3. さまざまなデザインパターンにこだわりすぎないでください。前の記事で説明したように、過度のカプセル化はパフォーマンスの低下を引き起こします。両者の間には考慮する必要のあるトレードオフがあります。 PHP には独自の特性があり、Java モデルをあまり真似しないでください。
  4. 関数はあまり深く入れ子にしないでください。再帰は注意して使用する必要があります。
  5. 疑似関数は非常に高性能なので、同じ関数を実装する場合は優先されます。たとえば、array_key_exists の代わりに isset を使用します。
  6. 参照を返す関数はあまり意味がなく、実用的な効果はありません。考慮しないことをお勧めします。
  7. クラスメンバーメソッドは通常の関数と比べて効率が低いため、パフォーマンスの低下を心配する必要はありません。可読性とセキュリティが向上する、より静的なメソッドを検討することをお勧めします。
  8. 特別な必要がない限り、パラメータを渡すときは参照渡しではなく値渡しを使用することをお勧めします。もちろん、パラメーターが大きな配列であり、変更する必要がある場合は、参照渡しを検討できます。

www.bkjia.comtru​​ehttp://www.bkjia.com/PHPjc/752407.html技術記事どの言語でも、関数は最も基本的な構成要素です。 PHPの関数にはどのような特徴があるのでしょうか?関数呼び出しはどのように実装されますか? PHP関数のパフォーマンスはどうですか?使用方法の提案は何ですか...
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。