Cada petición carga todo lib/
La línea 46 del único archivo de entrada de este servicio:
foreach (glob(__DIR__ . '/lib/*.php') as $f) { require_once $f; }
Son 65 archivos y 1,5 megabytes, cargados en cada petición. Una imagen de contador, una página de estadísticas, un 404 — todo igual. Ayer eso ocurrió 20.734 veces.
La cifra que importa es la pequeña
Cargarlo todo desde cero, con la caché de código apagada, lleva unos 132 milisegundos. Esa es la cifra que convertiría esto en un escándalo, y no es la cifra que paga el servidor.
Con el código ya compilado y en memoria compartida, lo que queda es listar el directorio y un stat() por archivo, porque la validación de marcas de tiempo está activada: 0,33 milisegundos. Con el tráfico de ayer eso son menos de siete segundos de trabajo en todo el día.
En medio hay una tercera medición, 18,2 milisegundos, con la caché de código leyendo desde disco en vez de memoria. Es una cota superior y no la cifra real — es lo que pasa cuando la caché existe pero es más lenta que la de verdad. Se informa porque es la única cifra en caliente que se puede medir desde la línea de órdenes, y fingir otra cosa sería el error cómodo.
Así que: esto no cuesta casi nada, y no cuesta casi nada enteramente por culpa de otra cosa. Apaga la caché de código y la misma línea cuesta cuatrocientas veces más. Es una dependencia real, y conviene saber que está ahí en vez de descubrirla durante una actualización.
Qué hay de verdad en los 1,5 megabytes
El cuarenta y siete por ciento — 715 kilobytes en 26 archivos — son familias de diseños de contador. Dibujar una imagen de contador necesita exactamente una. Las otras 25 se cargan, se declaran y no se tocan nunca.
Después: 189 kilobytes de datos de banderas, necesarios en la página de países; 158 kilobytes de biblioteca de gráficos, necesaria cuando algo dibuja un gráfico. Ninguno participa en servir una imagen de contador, que es la petición que este servicio responde más que ninguna otra.
El coste visible de eso es memoria más que tiempo. El pico por petición sube unos cuatro megabytes y, a diferencia del código compilado — que comparten todos los procesos — esa parte se paga por petición, en paralelo, por cada trabajador a la vez.
Alguien ya se dio cuenta
El glob es lib/*.php. No baja en profundidad, y justo debajo hay dos directorios: lib/live/ con 229 kilobytes y lib/recht/ con 182 kilobytes. Cuatrocientos kilobytes que antes estaban en la ruta de carga y ahora no.
Se movieron porque son grandes y rara vez necesarios: las secciones de estadísticas y los textos legales. Estar un directorio más abajo es todo el mecanismo. No hay configuración, ni cargador diferido, ni mapa de autocarga — un archivo está en el glob o no lo está.
Eso es un arreglo de verdad y merece decirse, porque el paso siguiente obvio sería un autocargador, y un autocargador aquí sería un cambio grande para un problema que un mkdir ya resolvió dos veces.
Por qué se queda
Un solo glob es lo más simple que puede funcionar, y según la medición funciona: un tercio de milisegundo, en una Raspberry Pi, en la ruta que tiene que ser rápida. Sustituirlo por inclusiones una a una significa que cada archivo nuevo necesita una línea en algún sitio, y la línea que se olvida produce un fallo fatal en producción en vez de una carga algo mayor.
Lo que falta es escribir aquello que lo hizo llevadero desde el principio: si un archivo de lib/ se hace grande, se muda a un subdirectorio y se incluye donde se usa. Esa es la regla que seguían esos dos directorios, y no estaba escrita en ningún sitio hasta hoy.
La versión general es más aburrida que la medición. «Todo carga todo» suele ser la forma equivocada, y conviene saber si te está costando algo antes de reconstruirlo. Aquí cuesta 0,33 milisegundos y cuatro megabytes, y los cuatro megabytes son la parte que hay que vigilar, porque crecen con la concurrencia y los milisegundos no.