Mejoras de Rendimiento en PHP 8.x
PHP 8.x trae mejoras significativas de rendimiento gracias al compilador JIT, optimizaciones internas del motor y nuevas caracteristicas como preloading. Conoce como aprovecharlas.
El compilador JIT
JIT (Just-In-Time) es la mejora mas importante de PHP 8.0. Compila el codigo PHP a instrucciones de maquina en tiempo de ejecucion, eliminando la interpretacion para codigo que se ejecuta frecuentemente.
; Habilitar JIT en php.ini
; Requiere OPcache habilitado
opcache.enable=1
opcache.jit_buffer_size=100M
opcache.jit=1255
; Modos JIT disponibles:
; 0 = deshabilitado
; 1205 = modo tracing (recomendado para web)
; 1255 = modo tracing agresivo
; 1235 = modo function
Cuando beneficia el JIT
El JIT mejora significativamente operaciones CPU-intensivas:
<?php
declare(strict_types=1);
// Operaciones matematicas intensivas: GRAN mejora con JIT
function calcularFibonacci(int $n): int
{
if ($n <= 1) {
return $n;
}
return calcularFibonacci($n - 1) + calcularFibonacci($n - 2);
}
// Procesamiento de imagenes, criptografia, ML: GRAN mejora
// Aplicaciones web tipicas: mejora MODERADA
// La mayoria del tiempo se va en I/O (base de datos, red)
// Verificar si JIT esta habilitado
if (function_exists('opcache_get_status')) {
$status = opcache_get_status();
if (isset($status['jit']['enabled'])) {
echo 'JIT habilitado: ' . ($status['jit']['enabled'] ? 'Si' : 'No');
}
}
Para aplicaciones web tipicas, el JIT ofrece mejoras del 5-10%. El beneficio real esta en tareas CPU-intensivas donde puede ser 2-3x mas rapido.
OPcache: la base del rendimiento
OPcache almacena el bytecode compilado de PHP en memoria compartida, evitando recompilar en cada peticion. Es fundamental para produccion:
; Configuracion recomendada para produccion
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
; En produccion: no revalidar archivos
opcache.validate_timestamps=0
; En desarrollo: revalidar cada 2 segundos
; opcache.validate_timestamps=1
; opcache.revalidate_freq=2
<?php
declare(strict_types=1);
// Verificar estado de OPcache
$status = opcache_get_status();
echo 'Memoria usada: ' . $status['memory_usage']['used_memory'];
echo 'Scripts cacheados: ' . $status['opcache_statistics']['num_cached_scripts'];
echo 'Hit rate: ' . $status['opcache_statistics']['opcache_hit_rate'] . '%';
// Limpiar cache (necesario despues de deploy)
opcache_reset();
Preloading (PHP 7.4+)
Preloading carga clases y funciones en memoria al iniciar PHP-FPM, haciendo que esten disponibles para todas las peticiones sin necesidad de cargarlas desde disco:
; En php.ini
opcache.preload=/var/www/app/preload.php
opcache.preload_user=www-data
<?php
// preload.php - Se ejecuta una vez al iniciar PHP-FPM
// Precargar clases frecuentes
$archivos = [
__DIR__ . '/src/Core/App.php',
__DIR__ . '/src/Core/Router.php',
__DIR__ . '/src/Core/Request.php',
__DIR__ . '/src/Core/Response.php',
];
foreach ($archivos as $archivo) {
if (file_exists($archivo)) {
opcache_compile_file($archivo);
}
}
// O usar el autoloader de Composer
require __DIR__ . '/vendor/autoload.php';
// Precargar clases especificas
$clases = [
App\Core\App::class,
App\Core\Router::class,
];
foreach ($clases as $clase) {
if (class_exists($clase)) {
// La clase ya esta cargada
}
}
Optimizaciones internas de PHP 8.x
PHP 8.x incluye numerosas optimizaciones automaticas:
<?php
declare(strict_types=1);
// 1. Match es mas rapido que switch
// PHP optimiza match a tabla de saltos
$estado = match ($codigo) {
200 => 'OK',
404 => 'Not Found',
500 => 'Server Error',
default => 'Unknown',
};
// 2. Named arguments: sin penalizacion
// El compilador optimiza a llamadas posicionales
function crearUsuario(string $nombre, string $email, bool $activo = true): array
{
return compact('nombre', 'email', 'activo');
}
// Esto se optimiza internamente
$user = crearUsuario(nombre: 'Ana', email: 'ana@example.com');
// 3. Nullsafe operator: optimizado
// Evita multiples comprobaciones
$ciudad = $usuario?->direccion?->ciudad;
// 4. Constructor promotion: mismo rendimiento
// Solo azucar sintactico, sin overhead
class Producto
{
public function __construct(
public readonly string $nombre,
public readonly float $precio,
) {}
}
Mejoras en tipos y validacion
Los tipos estrictos mejoran el rendimiento al evitar conversiones implicitas:
<?php
declare(strict_types=1);
// Con tipos: PHP sabe exactamente que esperar
function calcular(int $a, int $b): int
{
return $a + $b;
}
// Sin tipos: PHP debe inferir y posiblemente convertir
function calcularSinTipos($a, $b)
{
return $a + $b; // ¿Son numeros? ¿Strings? PHP debe verificar
}
// Union types (PHP 8.0): eficientes
function procesar(int|float $valor): float
{
return $valor * 1.5;
}
// Intersection types (PHP 8.1): validacion en compilacion
interface Serializable {}
interface Jsonable {}
function exportar(Serializable&Jsonable $objeto): string
{
// PHP verifica tipos en compilacion, no en runtime
return '';
}
Mejoras en arrays
<?php
declare(strict_types=1);
// Spread operator: optimizado para arrays grandes
$base = [1, 2, 3];
$extendido = [...$base, 4, 5, 6];
// array_is_list: O(1) en muchos casos
// PHP trackea internamente si un array es lista
$lista = [1, 2, 3];
$esLista = array_is_list($lista); // Muy rapido
// Unpacking con claves string (PHP 8.1)
$defaults = ['color' => 'azul', 'size' => 'M'];
$custom = ['color' => 'rojo'];
$merged = [...$defaults, ...$custom]; // ['color' => 'rojo', 'size' => 'M']
Buenas prácticas de rendimiento
<?php
declare(strict_types=1);
// 1. Usa readonly para inmutabilidad
// PHP puede optimizar mejor datos que no cambian
readonly class Config
{
public function __construct(
public string $appName,
public string $environment,
) {}
}
// 2. Prefiere funciones nativas sobre regex simple
$texto = 'Hola mundo';
// Lento
if (preg_match('/mundo/', $texto)) {}
// Rapido
if (str_contains($texto, 'mundo')) {}
// 3. Evita crear objetos innecesarios en bucles
// Malo
foreach ($items as $item) {
$formatter = new Formatter(); // Objeto nuevo cada iteracion
echo $formatter->format($item);
}
// Mejor
$formatter = new Formatter();
foreach ($items as $item) {
echo $formatter->format($item);
}
// 4. Usa generators para grandes conjuntos de datos
function leerArchivo(string $path): Generator
{
$handle = fopen($path, 'r');
while (($linea = fgets($handle)) !== false) {
yield trim($linea);
}
fclose($handle);
}
// Procesa linea por linea, sin cargar todo en memoria
foreach (leerArchivo('datos.txt') as $linea) {
procesar($linea);
}
Medir el rendimiento
<?php
declare(strict_types=1);
// Medir tiempo de ejecucion
$inicio = hrtime(true);
// ... codigo a medir ...
$fin = hrtime(true);
$nanosegundos = $fin - $inicio;
$milisegundos = $nanosegundos / 1e6;
echo "Tiempo: {$milisegundos}ms";
// Medir memoria
$memoriaInicial = memory_get_usage();
// ... codigo a medir ...
$memoriaFinal = memory_get_usage();
$diferencia = $memoriaFinal - $memoriaInicial;
echo "Memoria usada: " . ($diferencia / 1024) . " KB";
// Pico de memoria
echo "Pico: " . (memory_get_peak_usage() / 1024 / 1024) . " MB";
// Reiniciar pico (PHP 8.2)
memory_reset_peak_usage();
// Funcion simple de benchmark
function benchmark(callable $fn, int $iteraciones = 1000): float
{
$inicio = hrtime(true);
for ($i = 0; $i < $iteraciones; $i++) {
$fn();
}
$fin = hrtime(true);
return ($fin - $inicio) / 1e6 / $iteraciones;
}
$tiempoPromedio = benchmark(fn () => str_contains('texto', 'ext'));
echo "Promedio: {$tiempoPromedio}ms";
Ejercicios
Ejercicio 1: Comparar rendimiento
Crea un script que compare el tiempo de ejecucion de
strpos() !== false vs str_contains()
para 100,000 iteraciones.
Ver solucion
<?php
declare(strict_types=1);
$texto = 'Este es un texto de prueba para buscar palabras';
$buscar = 'prueba';
$iteraciones = 100000;
// Metodo antiguo: strpos
$inicio = hrtime(true);
for ($i = 0; $i < $iteraciones; $i++) {
$encontrado = strpos($texto, $buscar) !== false;
}
$tiempoStrpos = (hrtime(true) - $inicio) / 1e6;
// Metodo nuevo: str_contains
$inicio = hrtime(true);
for ($i = 0; $i < $iteraciones; $i++) {
$encontrado = str_contains($texto, $buscar);
}
$tiempoContains = (hrtime(true) - $inicio) / 1e6;
echo "strpos: {$tiempoStrpos}ms\n";
echo "str_contains: {$tiempoContains}ms\n";
echo "Diferencia: " . ($tiempoStrpos - $tiempoContains) . "ms";
Ejercicio 2: Generador vs Array
Compara el uso de memoria entre cargar un millon de numeros en un array vs usar un generador.
Ver solucion
<?php
declare(strict_types=1);
$cantidad = 1000000;
// Con array: carga todo en memoria
function crearArray(int $n): array
{
$numeros = [];
for ($i = 0; $i < $n; $i++) {
$numeros[] = $i;
}
return $numeros;
}
// Con generador: usa memoria minima
function crearGenerador(int $n): Generator
{
for ($i = 0; $i < $n; $i++) {
yield $i;
}
}
// Medir array
$memoriaInicio = memory_get_usage();
$array = crearArray($cantidad);
$suma = array_sum($array);
$memoriaArray = memory_get_usage() - $memoriaInicio;
unset($array);
// Medir generador
memory_reset_peak_usage();
$memoriaInicio = memory_get_usage();
$suma = 0;
foreach (crearGenerador($cantidad) as $numero) {
$suma += $numero;
}
$memoriaGenerador = memory_get_usage() - $memoriaInicio;
echo "Array: " . ($memoriaArray / 1024 / 1024) . " MB\n";
echo "Generador: " . ($memoriaGenerador / 1024) . " KB";
Ejercicio 3: Verificar OPcache
Crea una funcion que muestre el estado de OPcache y JIT de forma legible.
Ver solucion
<?php
declare(strict_types=1);
function mostrarEstadoOpcache(): void
{
if (!function_exists('opcache_get_status')) {
echo "OPcache no esta disponible\n";
return;
}
$status = opcache_get_status(false);
if ($status === false) {
echo "OPcache esta deshabilitado\n";
return;
}
echo "=== OPcache ===\n";
echo "Habilitado: Si\n";
$memoria = $status['memory_usage'];
$usada = $memoria['used_memory'] / 1024 / 1024;
$libre = $memoria['free_memory'] / 1024 / 1024;
echo "Memoria usada: " . round($usada, 2) . " MB\n";
echo "Memoria libre: " . round($libre, 2) . " MB\n";
$stats = $status['opcache_statistics'];
echo "Scripts cacheados: {$stats['num_cached_scripts']}\n";
echo "Hit rate: " . round($stats['opcache_hit_rate'], 2) . "%\n";
if (isset($status['jit'])) {
echo "\n=== JIT ===\n";
$jit = $status['jit'];
echo "Habilitado: " . ($jit['enabled'] ? 'Si' : 'No') . "\n";
if ($jit['enabled']) {
echo "Buffer: " . ($jit['buffer_size'] / 1024 / 1024) . " MB\n";
}
}
}
mostrarEstadoOpcache();
¿Has encontrado un error o tienes una sugerencia?
EscribenosHas completado el modulo PHP 8.x
Continua con el manejo de errores y excepciones.
Descubrir cursos premium