OPcache i JIT

OPcache działa z OxPHP od razu po instalacji. Wszystkie wątki worker PHP współdzielą jeden segment pamięci OPcache. Skrypty są kompilowane raz przy pierwszym wykonaniu, a następnie serwowane z cache'u przez każdy worker. Włączenie tego współdzielenia nie wymaga żadnej dodatkowej konfiguracji.

Jak OPcache współpracuje z OxPHP

OxPHP rejestruje się jako nazwany SAPI, a OPcache traktuje go identycznie jak inne serwerowe SAPI. Kluczowe cechy to:

  • Współdzielony cache między workerami: wszystkie wątki worker PHP korzystają z tego samego cache'u skompilowanych opcode'ów. Jeden worker kompiluje plik, a korzystają na tym wszystkie workery.
  • Brak kompilacji per żądanie: po pierwszym żądaniu dla danego skryptu kolejne żądania całkowicie pomijają krok parsowania i kompilacji.
  • opcache.enable_cli nie ma wpływu na OxPHP. To ustawienie dotyczy wyłącznie SAPI o nazwach cli oraz phpdbg. OxPHP rejestruje się pod nazwą SAPI cli-server, więc OPcache jest sterowany wyłącznie przez opcache.enable. Ustawienie opcache.enable_cli jest przydatne, jeśli w tym samym kontenerze uruchamiasz PHP CLI (np. do migracji lub poleceń Artisan). Oficjalny obraz OxPHP dostarcza PHP CLI obok binarki serwera, więc możesz ustawić opcache.enable_cli=1, jeśli Twoje skrypty CLI zyskują na cache'owaniu.

Aby włączyć OPcache, wystarczy co najmniej:

ini
[opcache] opcache.enable=1
Note

Oficjalny obraz Docker OxPHP bazuje na php:*-zts-alpine, który kompiluje OPcache statycznie do binarki PHP. NIE dodawaj zend_extension=opcache do swojego pliku INI. Rozszerzenie jest już załadowane, a dodanie tej linii spowoduje ostrzeżenie przy każdym starcie PHP. Potrzebna jest wyłącznie sekcja konfiguracji [opcache].

Zalecane ustawienia produkcyjne

Te ustawienia są zoptymalizowane pod produkcyjne wdrożenia kontenerowe, w których pliki PHP nie zmieniają się w czasie działania. Wyłącz walidację znaczników czasu i wykonaj preloading skompilowanych plików przy starcie, aby uzyskać maksymalną przepustowość.

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
Ustawienie Zalecana wartość Opis
memory_consumption 128 Pamięć współdzielona w MB na skompilowane skrypty. Zwiększ, jeśli opcache_get_status() pokazuje mało wolnej pamięci.
interned_strings_buffer 16 Pamięć w MB na interned strings współdzielone przez wszystkie workery.
max_accelerated_files 10000 Maksymalna liczba cache'owanych skryptów. Ustaw wyżej niż łączna liczba Twoich plików .php.
validate_timestamps 0 Gdy 0, OPcache nigdy nie sprawdza systemu plików pod kątem zmian. Aby uwzględnić zmiany w kodzie, zrestartuj kontener lub wywołaj opcache_reset().
revalidate_freq 0 Liczba sekund między sprawdzeniami systemu plików. Nie ma efektu, gdy validate_timestamps=0.
file_update_protection 0 Liczba sekund po modyfikacji pliku, zanim plik kwalifikuje się do cache'owania. Ustaw na 0, aby cache'ować natychmiast przy starcie.

Ustawienia deweloperskie

Na potrzeby developmentu włącz walidację znaczników czasu, aby zmiany w kodzie były uwzględniane bez restartu kontenera. Wyłącz JIT, aby uzyskać czytelniejsze ślady stosu podczas debugowania.

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

Przy validate_timestamps=1 OPcache co revalidate_freq sekund sprawdza czas modyfikacji plików. Dodaje to niewielki narzut na każde żądanie, ale pozwala edytować pliki PHP i widzieć zmiany przy następnym żądaniu.

To jest zalecana strategia przeładowywania w trybie deweloperskim dla OxPHP. OPcache wykonuje sprawdzenie w linii przy każdym include, więc edycje kodu są uwzględniane przy następnym żądaniu, bez restartu kontenera i bez zewnętrznego demona nasłuchującego zmian plików. Użyj revalidate_freq=0, aby stat wykonywał się przy każdym include (najwyższa dokładność, nieco więcej operacji I/O), lub revalidate_freq=2, aby zamortyzować koszt operacji stat. Domyślna wartość pokazana powyżej to dobry kompromis, zwłaszcza gdy Twój DOCUMENT_ROOT znajduje się na wolnym bind-mount (Docker na macOS/Windows).

Czego validate_timestamps NIE przeładowuje

Kilka rodzajów zmian nadal wymaga restartu kontenera (lub recyklingu workera) nawet przy validate_timestamps=1:

  • Pliki objęte preloadingiem (opcache.preload) są linkowane do serwera przy starcie i nigdy nie są ponownie walidowane. Edytujesz plik objęty preloadingiem — restartuj kontener.
  • Stan bootstrapu w trybie Worker — w trybie Worker autoloader, kontener DI oraz wszelkie obiekty zbudowane w zewnętrznym zakresie żyją w pamięci workera. OPcache przekompiluje zmienione pliki klas, ale worker nie uruchomi ponownie swojego bootstrapu. W deweloperskich pętlach worker wywołaj Worker::scheduleExit() na końcu każdego żądania (np. za flagą środowiskową OXPHP_DEV), aby zrecyklingować worker, co ponownie wykona zewnętrzny zakres i uwzględni wszystkie zmiany.
  • Cache na poziomie frameworka — skompilowany kontener Symfony, cache tras/konfiguracji/widoków Laravela, zoptymalizowana classmapa Composera. To są pliki .php, które OPcache rewaliduje, ale wartości w nich zawarte odwołują się do nieaktualnych ścieżek klas lub identyfikatorów kontenera. Uruchom polecenie cache:clear frameworka; sam OPcache nie wystarczy.
  • Pliki nie-PHP.env, composer.json, konfiguracja YAML/JSON, pliki szablonów kompilowane poza OPcache. OPcache śledzi tylko pliki, które sam skompilował; wszystko inne wymaga restartu.

Kompilacja JIT

Kompilator JIT w OPcache tłumaczy opcode'y PHP na natywny kod maszynowy w czasie działania. Użyj trybu tracing, aby uzyskać najlepszą optymalizację:

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

JIT daje największe korzyści dla kodu PHP ograniczonego przez CPU: pętli intensywnie liczących, przetwarzania łańcuchów znaków, manipulacji obrazami i renderowania szablonów. W przypadku aplikacji ograniczonych przez I/O, które większość czasu spędzają, czekając na zapytania do bazy danych lub wywołania zewnętrznych API, poprawa jest minimalna.

Aby wyłączyć JIT:

ini
opcache.jit=disable opcache.jit_buffer_size=0

Preloading

Preloading w OPcache kompiluje i cache'uje pliki PHP przy starcie serwera, zanim obsłużone zostaną jakiekolwiek żądania. Całkowicie eliminuje to koszt kompilacji przy pierwszym żądaniu i udostępnia klasy oraz funkcje globalnie, bez żadnego narzutu związanego z require czy autoloadingiem.

Skonfiguruj preloading w swoim pliku INI:

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

Utwórz skrypt preload.php, który ładuje Twoje najczęściej używane pliki:

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

Klasy i funkcje objęte preloadingiem są trwale dostępne dla wszystkich żądań. Nie można ich zmienić bez restartu serwera.

Tryb Worker i preloading

Jeśli używasz trybu Worker, Twoja aplikacja jest już zainicjalizowana raz: autoloader, konfiguracja i połączenia z bazą danych utrzymują się między żądaniami. Preloading w OPcache uzupełnia to, eliminując narzut kompilacji opcode'ów, ale nie zastępuje inicjalizacji aplikacji. Oba mechanizmy działają niezależnie i można ich używać razem.

Stosowanie konfiguracji PHP

OxPHP odczytuje konfigurację PHP ze standardowego katalogu conf.d. Użyj wolumenu Dockera lub instrukcji COPY, aby dostarczyć swój własny plik 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

Monitorowanie stanu cache'u

Sprawdź na żywo stan OPcache z poziomu PHP, aby zweryfikować, że działa:

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";

Jeśli free_memory jest stale niskie, zwiększ opcache.memory_consumption.

Zobacz też