Настройки производительности
OPcache -- кеширование байткода
OPcache -- встроенное расширение PHP, которое кеширует скомпилированный байткод скриптов в разделяемой памяти. Без OPcache PHP каждый запрос читает файл, парсит его, компилирует в опкоды и выполняет. С OPcache этапы чтения, парсинга и компиляции пропускаются.
Базовая настройка
[opcache]
; Enable OPcache (required)
opcache.enable = 1
; Enable for CLI scripts (useful for long-running workers, tests)
opcache.enable_cli = 0
; Shared memory size in MB for storing compiled scripts
; Default: 128. Increase for large projects (Symfony/Laravel: 256+)
opcache.memory_consumption = 256
; Memory for interned strings (class names, function names, comments)
; Default: 8. Increase for projects with many classes
opcache.interned_strings_buffer = 32
; Maximum number of files that can be cached
; Default: 10000. Symfony app typically has 15000-30000 files
; Use prime number close to your actual file count
opcache.max_accelerated_files = 30000
; How often to check if files changed (seconds)
; 0 = check every request, 2 = default
; Production: disable entirely with validate_timestamps=0
opcache.revalidate_freq = 2
Ключевая production-директива
; Production: NEVER check if source files changed
; Requires PHP-FPM restart/reload after deployment
opcache.validate_timestamps = 0
; Development: check on every request
opcache.validate_timestamps = 1
opcache.revalidate_freq = 0
Критически важно: При
validate_timestamps=0PHP никогда не перечитывает исходные файлы. После деплоя нуженphp-fpm reloadили вызовopcache_reset(). Это даёт значительный прирост производительности -- нетstat()системных вызовов.
JIT-компиляция (PHP 8.0+)
JIT (Just-In-Time) компилирует PHP-байткод в машинный код процессора. Наибольший эффект на CPU-интенсивных задачах (математика, обработка данных). Для типичных веб-приложений (I/O-bound) эффект минимальный.
Конфигурация JIT
; Enable JIT (requires opcache.enable=1)
; Available modes in PHP 8.4:
opcache.jit = tracing
; Memory buffer for JIT compiled code (in MB)
; Separate from opcache.memory_consumption!
opcache.jit_buffer_size = 128M
; JIT debug: 0 = disabled, useful for troubleshooting
opcache.jit_debug = 0
Режимы JIT в PHP 8.4
В PHP 8.4 конфигурация JIT упрощена по сравнению с 8.0-8.3:
; PHP 8.4 simplified JIT modes:
opcache.jit = disable ; JIT completely off
opcache.jit = tracing ; Best for web apps (traces hot paths)
opcache.jit = function ; Compiles entire functions
; PHP 8.0-8.3 legacy numeric format (still supported):
; opcache.jit = 1255 ; Tracing JIT
; opcache.jit = 1205 ; Function JIT
| Режим | Лучше для | Описание |
|---|---|---|
disable |
Отладка, совместимость | JIT полностью выключен |
tracing |
Веб-приложения | Отслеживает горячие пути, оптимизирует их |
function |
CLI, математика | Компилирует функции целиком |
Когда JIT помогает, а когда нет
<?php
// JIT helps: CPU-intensive computation
function fibonacci(int $n): int
{
if ($n <= 1) return $n;
return fibonacci($n - 1) + fibonacci($n - 2);
}
// With JIT: ~40-60% faster for pure computation
// JIT doesn't help much: I/O-bound web apps
function getUser(PDO $db, int $id): array
{
// Bottleneck is database query, not PHP code
$stmt = $db->prepare('SELECT * FROM users WHERE id = ?');
$stmt->execute([$id]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
PHP 8.4: Влияние property hooks на OPcache
<?php
// PHP 8.4 property hooks -- cached by OPcache like regular methods
class Product
{
public string $slug {
set(string $value) => strtolower(trim($value));
get => $this->slug;
}
public float $priceWithTax {
get => round($this->price * 1.2, 2);
}
public function __construct(
public readonly float $price,
) {}
}
// Property hooks generate additional opcodes
// Increase opcache.memory_consumption if using extensively
Мониторинг OPcache
<?php
// Get OPcache status
$status = opcache_get_status();
// Key metrics
$memory = $status['memory_usage'];
echo "Used: " . round($memory['used_memory'] / 1024 / 1024, 2) . " MB\n";
echo "Free: " . round($memory['free_memory'] / 1024 / 1024, 2) . " MB\n";
echo "Wasted: " . round($memory['wasted_percentage'], 2) . "%\n";
$stats = $status['opcache_statistics'];
echo "Cached files: " . $stats['num_cached_scripts'] . "\n";
echo "Cache hits: " . $stats['hits'] . "\n";
echo "Cache misses: " . $stats['misses'] . "\n";
echo "Hit rate: " . round($stats['opcache_hit_rate'], 2) . "%\n";
// JIT status (PHP 8.0+)
$jit = $status['jit'];
echo "JIT enabled: " . ($jit['enabled'] ? 'yes' : 'no') . "\n";
echo "JIT buffer size: " . round($jit['buffer_size'] / 1024 / 1024, 2) . " MB\n";
echo "JIT buffer used: " . round($jit['buffer_used'] / 1024 / 1024, 2) . " MB\n";
Сброс кеша OPcache
<?php
// Full reset -- clears ALL cached scripts
opcache_reset();
// Invalidate single file
opcache_invalidate('/path/to/file.php', true);
// In deployment script:
// 1. Deploy new code
// 2. Call opcache_reset() via HTTP or CLI
// 3. Optionally preload with opcache_compile_file()
Preloading (PHP 7.4+)
Preloading загружает файлы в OPcache при старте PHP-FPM и хранит их постоянно в памяти.
; Path to preload script (runs once at FPM start)
opcache.preload = /var/www/app/config/preload.php
; User for preloading (required on Linux)
opcache.preload_user = www-data
<?php
// config/preload.php
// Preload framework core classes
require_once __DIR__ . '/../vendor/autoload.php';
// Symfony preload example
if (file_exists(__DIR__ . '/../var/cache/prod/App_KernelProdContainer.preload.php')) {
require __DIR__ . '/../var/cache/prod/App_KernelProdContainer.preload.php';
}
Ограничение: Preloading работает только с PHP-FPM. Изменение предзагруженных файлов требует полного рестарта FPM (не reload).
realpath_cache -- кеш файловых путей
PHP кеширует результаты realpath() -- преобразования относительных путей в абсолютные и проверки существования файлов.
; Size of realpath cache per process
; Default: 4096K. Increase for projects with many files
realpath_cache_size = 4096K
; TTL in seconds
; Default: 120. Production: increase to 600+
realpath_cache_ttl = 600
<?php
// Check current realpath cache usage
$info = realpath_cache_size();
echo "Realpath cache used: " . round($info / 1024, 2) . " KB\n";
// View cached entries
$entries = realpath_cache_get();
echo "Cached paths: " . count($entries) . "\n";
Связь с OPcache: При
opcache.validate_timestamps=0realpath_cache менее критичен, так как OPcache не проверяет файлы. Но Composer autoload всё равно используетrealpath().
memory_limit и max_execution_time
Расчёт memory_limit
; Web applications: 128M-256M for most cases
memory_limit = 256M
; API endpoints: 64M-128M (simple operations)
; CLI workers/queues: 256M-512M (data processing)
; Import/export: 512M-1G (large files)
; NEVER use -1 in production!
<?php
// Formula for PHP-FPM:
// memory_limit * pm.max_children <= Available_RAM
// Example: 256M * 50 children = 12.8 GB needed
// Leave RAM for OS, database, Redis, etc.
// Monitor actual usage
echo "Current: " . round(memory_get_usage(true) / 1024 / 1024, 2) . " MB\n";
echo "Peak: " . round(memory_get_peak_usage(true) / 1024 / 1024, 2) . " MB\n";
max_execution_time
; Web: 30 seconds default (good for most apps)
max_execution_time = 30
; CLI: 0 (unlimited) -- this is default for CLI SAPI
; max_execution_time counts ONLY CPU time
; Does NOT count: sleep(), database queries, file I/O, network waits
<?php
// set_time_limit() RESETS the timer from current moment
set_time_limit(30); // 30 seconds from NOW
// ini_set() sets from script START (does not reset)
ini_set('max_execution_time', '30');
// TRAP: set_time_limit() in a loop = infinite execution
while ($item = $queue->next()) {
set_time_limit(30); // Timer resets each iteration!
process($item); // Script never times out
}
Полная production-конфигурация OPcache
[opcache]
; Core
opcache.enable = 1
opcache.enable_cli = 0
opcache.memory_consumption = 256
opcache.interned_strings_buffer = 32
opcache.max_accelerated_files = 30000
; Validation -- DISABLE for production
opcache.validate_timestamps = 0
opcache.revalidate_freq = 0
; Optimization
opcache.save_comments = 1
opcache.fast_shutdown = 1
opcache.optimization_level = 0x7FFEBFFF
opcache.huge_code_pages = 1
; JIT (PHP 8.4)
opcache.jit = tracing
opcache.jit_buffer_size = 128M
; Preloading
opcache.preload = /var/www/app/config/preload.php
opcache.preload_user = www-data
; Realpath cache
realpath_cache_size = 4096K
realpath_cache_ttl = 600