OPcache と JIT

OPcache は OxPHP でそのまま動作します。すべての PHP ワーカースレッドは、単一の OPcache メモリセグメントを共有します。スクリプトは最初の実行時に一度だけコンパイルされ、それ以降はすべてのワーカーがキャッシュから提供します。この共有を有効にするための特別なセットアップは必要ありません。

OPcache が OxPHP でどのように動作するか

OxPHP は名前付き SAPI として自身を登録し、OPcache はそれを他のサーバー SAPI とまったく同じように扱います。主な特徴は次のとおりです。

  • ワーカー間で共有されるキャッシュ: すべての PHP ワーカースレッドが、同じコンパイル済みオペコードキャッシュを使用します。あるワーカーがファイルをコンパイルすれば、すべてのワーカーがその恩恵を受けます。
  • リクエストごとのコンパイルが不要: 各スクリプトへの最初のリクエスト以降、後続のリクエストはパースとコンパイルのステップを完全にスキップします。
  • opcache.enable_cli は OxPHP には影響しません。この設定は cli および phpdbg という名前の SAPI にのみ適用されます。OxPHP は cli-server という SAPI 名で登録するため、OPcache は opcache.enable のみで制御されます。opcache.enable_cli 設定は、同じコンテナ内で PHP CLI を実行する場合(マイグレーションや Artisan コマンドなど)に役立ちます。公式の OxPHP イメージはサーバーバイナリと一緒に PHP CLI を同梱しているため、CLI スクリプトがキャッシュの恩恵を受ける場合は opcache.enable_cli=1 を設定できます。

OPcache を有効にするには、最低限次のようにします。

ini
[opcache] opcache.enable=1
Note

公式の OxPHP Docker イメージは php:*-zts-alpine をベースにしており、OPcache を PHP バイナリに静的にコンパイルしています。INI ファイルに zend_extension=opcache を追加しないでください。この拡張はすでに読み込まれており、その行を追加すると PHP の起動ごとに警告が発生します。必要なのは [opcache] の設定セクションだけです。

推奨される本番環境向け設定

これらの設定は、PHP ファイルが実行時に変更されない本番環境のコンテナデプロイ向けに最適化されています。タイムスタンプ検証を無効化し、起動時にコンパイル済みファイルをプリロードすることで、スループットを最大化します。

ini
[opcache] opcache.enable=1 opcache.memory_consumption=128 opcache.interned_strings_buffer=16 opcache.max_accelerated_files=10000 opcache.validate_timestamps=0 opcache.revalidate_freq=0 opcache.file_update_protection=0 opcache.jit_buffer_size=64M opcache.jit=tracing
設定 推奨値 説明
memory_consumption 128 コンパイル済みスクリプト用の共有メモリ(MB 単位)。opcache_get_status() で空きメモリが少ないと表示される場合は増やします。
interned_strings_buffer 16 すべてのワーカーで共有される interned string 用のメモリ(MB 単位)。
max_accelerated_files 10000 キャッシュするスクリプトの最大数。合計の .php ファイル数よりも大きく設定します。
validate_timestamps 0 0 の場合、OPcache はファイルシステムの変更を一切確認しません。コード変更を反映するには、コンテナを再起動するか opcache_reset() を呼び出します。
revalidate_freq 0 ファイルシステムを確認する間隔(秒)。validate_timestamps=0 の場合は効果がありません。
file_update_protection 0 ファイル変更後、そのファイルがキャッシュ対象になるまでの秒数。起動時にすぐキャッシュするには 0 に設定します。

開発環境向け設定

開発環境では、タイムスタンプ検証を有効にして、コンテナを再起動せずにコード変更が反映されるようにします。デバッグ中により分かりやすいスタックトレースを得るために、JIT を無効にします。

ini
[opcache] opcache.enable=1 opcache.memory_consumption=128 opcache.interned_strings_buffer=16 opcache.max_accelerated_files=10000 opcache.validate_timestamps=1 opcache.revalidate_freq=2 opcache.jit_buffer_size=0 opcache.jit=disable

validate_timestamps=1 の場合、OPcache は revalidate_freq 秒ごとにファイルの更新時刻を確認します。これによりリクエストごとにわずかなオーバーヘッドが加わりますが、PHP ファイルを編集して次のリクエストで変更を確認できるようになります。

これは OxPHP で推奨される開発モードのリロード戦略です。 OPcache は include のたびにインラインで確認を行うため、コンテナの再起動も外部のファイル監視デーモンも不要で、コードの編集が次のリクエストで反映されます。include ごとに即座に stat を行う(精度は最も高いが I/O はやや増える)には revalidate_freq=0 を、stat のコストを分散するには revalidate_freq=2 を使用します。上記のデフォルト値は、特に DOCUMENT_ROOT が低速なバインドマウント(macOS/Windows 上の Docker)にある場合に、良いバランスとなります。

validate_timestamps でリロードされないもの

validate_timestamps=1 を設定していても、いくつかの種類の変更は依然としてコンテナの再起動(またはワーカーのリサイクル)が必要です。

  • プリロードされたファイルopcache.preload)は起動時にサーバーにリンクされ、再検証されることはありません。プリロードされたファイルを編集したら、コンテナを再起動してください。
  • ワーカーモードのブートストラップ状態ワーカーモードでは、オートローダー、DI コンテナ、および外側のスコープで構築されたあらゆるオブジェクトがワーカーのメモリ内に存在します。OPcache は変更されたクラスファイルを再コンパイルしますが、ワーカーはブートストラップを再実行しません。開発用のワーカーループでは、各リクエストの終わりに(例えば OXPHP_DEV 環境フラグの後ろで)Worker::scheduleExit() を呼び出してワーカーをリサイクルします。これにより外側のスコープが再実行され、すべての変更が反映されます。
  • フレームワークレベルのキャッシュ — コンパイル済みの Symfony コンテナ、Laravel のルート/設定/ビューキャッシュ、Composer の最適化されたクラスマップなど。これらは OPcache が再検証する .php ファイルですが、その中の値は古いクラスパスやコンテナ ID を参照しています。フレームワークの cache:clear コマンドを実行してください。OPcache だけでは不十分です。
  • PHP 以外のファイル.envcomposer.json、YAML/JSON の設定、OPcache の外部でコンパイルされるテンプレートファイルなど。OPcache が追跡するのは自身がコンパイルしたファイルのみで、それ以外はすべて再起動が必要です。

JIT コンパイル

OPcache の JIT コンパイラは、実行時に PHP のオペコードをネイティブなマシンコードに変換します。最良の最適化を得るには tracing モードを使用します。

ini
opcache.jit=tracing opcache.jit_buffer_size=64M

JIT が最も効果を発揮するのは、CPU バウンドな PHP コードです。数値計算の多いループ、文字列処理、画像操作、テンプレートのレンダリングなどです。ほとんどの時間をデータベースクエリや外部 API 呼び出しの待機に費やす I/O バウンドなアプリケーションでは、改善効果はわずかです。

JIT を無効にするには:

ini
opcache.jit=disable opcache.jit_buffer_size=0

プリロード

OPcache のプリロードは、リクエストが処理される前のサーバー起動時に PHP ファイルをコンパイルしてキャッシュします。これにより最初のリクエストでのコンパイルコストが完全になくなり、require やオートロードのオーバーヘッドなしで、クラスや関数をグローバルに利用できるようになります。

INI ファイルでプリロードを設定します。

ini
opcache.preload=/var/www/html/preload.php opcache.preload_user=www-data

最も頻繁に使用するファイルを読み込む preload.php スクリプトを作成します。

preload.php
<?php // preload.php — runs once at server startup require __DIR__ . '/vendor/autoload.php'; // Preload framework core files $files = glob(__DIR__ . '/vendor/symfony/http-kernel/**.php'); foreach ($files as $file) { opcache_compile_file($file); } // Preload hot application paths opcache_compile_file(__DIR__ . '/src/Controller/ApiController.php'); opcache_compile_file(__DIR__ . '/src/Service/UserService.php');
Note

プリロードされたクラスと関数は、すべてのリクエストから永続的に利用できます。サーバーを再起動しない限り変更できません。

ワーカーモードとプリロード

ワーカーモードを使用している場合、アプリケーションはすでに一度だけ初期化されています。オートローダー、設定、データベース接続はリクエスト間で保持されます。OPcache のプリロードは、オペコードのコンパイルオーバーヘッドをなくすことでこれを補完しますが、アプリケーションの初期化を置き換えるものではありません。この 2 つの仕組みは互いに独立して動作し、併用できます。

PHP 設定の適用

OxPHP は標準の conf.d ディレクトリから PHP 設定を読み込みます。Docker ボリュームまたは COPY 命令を使用して、カスタム INI ファイルを供給します。

bash
docker run -p 80:80 \ -v ./custom.ini:/usr/local/etc/php/conf.d/custom.ini:ro \ ghcr.io/oxphp/oxphp:0.10.0

キャッシュ状態の監視

OPcache のライブ状態を PHP から確認して、正しく動作していることを検証します。

php
<?php $status = opcache_get_status(); echo "Cached scripts: " . $status['opcache_statistics']['num_cached_scripts'] . "\n"; echo "Cache hits: " . $status['opcache_statistics']['hits'] . "\n"; echo "Cache misses: " . $status['opcache_statistics']['misses'] . "\n"; echo "Free memory: " . $status['memory_usage']['free_memory'] . " bytes\n";

free_memory が常に少ない場合は、opcache.memory_consumption を増やします。

関連項目