ホームページ  >  記事  >  バックエンド開発  >  PHP オペコード – コードを変更せずにアプリケーションのパフォーマンスを向上させる

PHP オペコード – コードを変更せずにアプリケーションのパフォーマンスを向上させる

Linda Hamilton
Linda Hamiltonオリジナル
2024-11-14 10:13:01127ブラウズ

PHP エンジンによって生成される PHP オペコードは、コードの記述方法に強く影響されます。タスクを達成するためのステートメントの数だけではありません。それが非常に重要であることは明らかであり、あなたにとっても明らかだと思います。

あまり明らかではないかもしれませんが、コードの構文によっても生成されるオペコードが完全に変更され、マシンの CPU がまったく同じコードを実行する際に多大なオーバーヘッドが発生する可能性があるということです。

ここ数年で、私の SaaS 製品は大幅に成長し、ワークロードを可能な限り効率的に実行するための最適化手法をさらに深く掘り下げる機会が与えられました。

私が見た結果は印象的で、SaaS の開発を継続するためのフリー キャッシュ フローを解放するのに大いに役立ちました。

現時点で、私の SaaS 製品内の PHP プロセスは、2vCPU と 8GB のメモリを搭載したマシンで毎日 12 億 (B の場合) 以上のデータ パケットを処理しています。 予期せぬスパイクが発生した場合の柔軟性を高めるために AWS 自動スケーリング グループを使用していますが、2 台目のマシンが追加されることはめったにありません (週に 1 ~ 2 回)。

さらに技術的な記事については、Linkedin または X で私をフォローしてください。

最近、Inspector サーバーの ARM インスタンスへの移行についても書きました: https://inspector.dev/inspector-adoption-of-graviton-arm-instances-and-what-results-weve-seen/

記事の本題に入りましょう。とても興味深い内容だと思います。

PHP オペコードとは何ですか

PHP オペコードはオペレーション コードの略で、作成した PHP ソース コードがコンパイルされた後に PHP エンジンによって実行される低レベルの命令を指します。

PHP では、コードのコンパイルは実行時に行われます。基本的に、コードが初めて PHP エンジンに取り込まれるときに、コードはこのマシンに適したコードにコンパイルされ、キャッシュされるため、エンジンは同じコードを再度コンパイルしません。その後実行されます。

これはプロセスを簡単に表したものです:

PHP opcode – Improve application performance without changing your code

PHP オペコードのキャッシュ

PHP オペコードをキャッシュすると、コード実行プロセスの 3 つのステップ (生の PHP コードの解析、トークン化、コンパイル) を節約できます。

オペコードが初めて生成されると、後続のリクエストで再利用できるようにメモリに保存されます。これにより、PHP エンジンが同じ PHP コードを実行するたびに再コンパイルする必要がなくなり、CPU とメモリの消費量が大幅に節約されます。

PHP で最も一般的に使用されるオペコード キャッシュは OPCache で、PHP 5.5 から最近のバージョンまではデフォルトで含まれています。非常に効率的で、広くサポートされています。

プリコンパイルされたスクリプトのバイトコードをキャッシュするには、展開のたびにキャッシュを無効にする必要があります。変更されたファイルのバイトコード バージョンがキャッシュにある場合、PHP は古いバージョンのコードを実行し続けるためです。オペコード キャッシュをパージするまでは、新しいコードが再度コンパイルされて新しいキャッシュ アイテムが生成されます。

PHP オペコードを調査する方法

さまざまな構文がスクリプトのオペコードにどのような影響を与えるかを理解するには、PHP エンジンによって生成されたコンパイル済みコードを取得する方法が必要です。

オペコードを取得するには 2 つの方法があります。

OPCache ネイティブ関数

マシン上で OPCache 拡張機能を有効にしている場合は、そのネイティブ関数を使用して特定の PHP ファイルのオペコードを取得できます。

// Force compilation of a script
opcache_compile_file(__DIR__.'/yourscript.php');

// Get OPcache status
$status = opcache_get_status();

// Inspect the script's entry in the cache
print_r($status['scripts'][__DIR__.'/yourscript.php']);

VLD (Vulcan Logic 逆アセンブラー) PHP 拡張機能

VLD は、コンパイルされた PHP コードを逆アセンブルし、オペコードを出力する一般的な PHP 拡張機能です。これは、PHP がコードをどのように解釈して実行するかを理解するための強力なツールです。インストールしたら、-d オプションを指定した php コマンドを使用して、VLD を有効にして PHP スクリプトを実行できます。

php -d vld.active=1 -d vld.execute=0 yourscript.php

出力には、各操作、それに関連するコード行など、コンパイルされたオペコードに関する詳細情報が含まれます。

3v4l (EVAL の頭字語) を使用します。

3v4l は、エディターに入力した PHP コードによって生成されたオペコードを表示できる非常に便利なオンライン ツールです。基本的には、VLD がインストールされた PHP サーバーであるため、VLD 出力を取得し、オペコードをブラウザーに表示できます。

無料で配布されているため、次の分析にはこのオンライン ツールを使用します。

効率的な PHP オペコードを生成する方法

3v4l は、使用するコード構文が結果の PHP オペコードに良い意味でも悪い意味でもどのように影響するかを理解するのに最適です。以下のコードを 3v4l に貼り付け始めましょう。設定を「サポートされているすべてのバージョン」のままにして、「評価」をクリックします。

<?php

namespace App;

strlen('ciao');

コードを実行すると、下部にタブ メニューが表示されます。 [VLD] タブに移動して、対応する OPcode を視覚化します。

line      #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
    5     0  E >   INIT_NS_FCALL_BY_NAME                                    'App%5CSpace%5Cstrlen'
          1        SEND_VAL_EX                                              'ciao'
          2        DO_FCALL                                      0          
          3      > RETURN                                                   1

最初の操作は INIT_NS_FCALL_BY_NAME であることに注意してください。インタプリタは、現在のファイルの名前空間を使用して関数の名前を構築します。しかし、AppExample 名前空間には存在しないので、どのように機能するのでしょうか?

インタプリタは、関数が現在の名前空間に存在するかどうかを確認します。そうでない場合は、対応するコア関数を呼び出そうとします。

ここでは、この二重チェックを回避し、コア関数を直接実行するようにインタープリタに指示する機会があります。

strlen の前にバックスラッシュ () を追加して、「eval」をクリックしてみてください:

<?php

namespace App;

\strlen('ciao');

[VLD] タブで、ステートメントを 1 つだけ含むオペコードを確認できるようになりました。

line      #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
    5     0  E > > RETURN                                                   1

これは、関数の正確な場所を伝えたため、フォールバックを考慮する必要がありません。

If don't like to use the the backslash, you can import the function like any other class from the root namespace:

<?php

namespace App;

use function strlen;

strlen('ciao');

Leverage Automatic Opcode Optimizations

There are also a lot of internal automatisms of the PHP engine to generate an optimized opcode evaluating static expressions in advance. This was one of the most important reasons of the great performance improvement of PHP since the version 7.x

Being aware of these dynamics can really help you reduce resource consumption and cut costs. Once I made this research, I started using these tricks throughout the code.

Let me show you an example using PHP constants. Run this script into 3v4l:

<?php

namespace App;

if (PHP_OS === 'Linux') {
    echo "Linux";
}

Take a look at the first two lines of the PHP opcode:

line      #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
    5     0  E >   FETCH_CONSTANT                                   ~0      'App%5CPHP_OS'
          1        IS_IDENTICAL                                             ~0, 'Linux'
          2      > JMPZ                                                     ~1, ->4
    6     3    >   ECHO                                                     'Linux'
    7     4    > > RETURN                                                   1

FETCH_CONSTANT try to get the value of PHP_OS from the current namespace and it will look into the global namespace as it doesn’t exist here. Then the IS_IDENTICAL instruction executes the IF statement.

Now try adding the backslash to constant:

<?php

namespace App;

if (\PHP_OS === 'Linux') {
    echo "Linux";
}

As you can see in the opcode the engine doesn't need to try to fetch the constant because now it's clear where it is, and since it's a static value it already has it in memory.

Also the IF statement disappeared because the other side of the IS_IDENTITCAL statement is a static string ('Linux') so the IF can be marked as "true" without the overhead of interpreting it on every execution.

This is why you have a lot of power to influence the ultimate performance of your PHP code.

I hope it was an interesting topic, as I mentioned at the beginning of the article I'm getting a lot of benefits from using this tactic and in fact they are also used in our packages.

You can see here an example of how I used this tips in our PHP package to optimize its performance: https://github.com/inspector-apm/inspector-php/blob/master/src/Inspector.php#L302

For more technical articles you can follow me on Linkedin or X.

Monitor your PHP application for free

Inspector is a Code Execution Monitoring tool specifically designed for software developers. You don't need to install anything at the server level, just install the Laravel or Symfony package and you are ready to go.

If you are looking for HTTP monitoring, database query insights, and the ability to forward alerts and notifications into your preferred messaging environment, try Inspector for free. Register your account.

Or learn more on the website: https://inspector.dev

PHP opcode – Improve application performance without changing your code

以上がPHP オペコード – コードを変更せずにアプリケーションのパフォーマンスを向上させるの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。