Ogni richiesta carica tutto lib/
La riga 46 dell'unico file d'ingresso di questo servizio:
foreach (glob(__DIR__ . '/lib/*.php') as $f) { require_once $f; }
Sono 65 file e 1,5 megabyte, caricati a ogni richiesta. Un'immagine di contatore, una pagina di statistiche, un 404 — tutto uguale. Ieri è successo 20.734 volte.
Il numero che conta è quello piccolo
Caricare tutto da zero, con la cache degli opcode spenta, richiede circa 132 millisecondi. È il numero che farebbe di questo uno scandalo, e non è il numero che paga il server.
Con gli opcode già compilati e in memoria condivisa resta l'elenco della cartella e uno stat() per file, perché la verifica delle marche temporali è attiva: 0,33 millisecondi. Al traffico di ieri fanno meno di sette secondi di lavoro su tutta la giornata.
In mezzo c'è una terza misura, 18,2 millisecondi, con la cache degli opcode che legge dal disco invece che dalla memoria. È un limite superiore e non il valore vero — è quel che succede quando la cache c'è ma è più lenta di quella reale. Viene riportata perché è l'unico valore a caldo misurabile dalla riga di comando, e sostenere altro sarebbe l'errore comodo.
Quindi: questo non costa quasi nulla, e non costa quasi nulla interamente per merito di qualcos'altro. Spegni la cache degli opcode e la stessa riga costa quattrocento volte tanto. È una dipendenza vera, e conviene sapere che c'è invece di scoprirla durante un aggiornamento.
Che cosa c'è davvero negli 1,5 megabyte
Il quarantasette per cento — 715 kilobyte in 26 file — sono famiglie di modelli di contatore. Disegnare un'immagine di contatore ne richiede esattamente una. Le altre 25 vengono caricate, dichiarate e mai toccate.
Poi: 189 kilobyte di dati delle bandiere, utili nella pagina dei paesi; 158 kilobyte di libreria per i grafici, utile quando qualcosa disegna un grafico. Nessuno dei due partecipa a servire un'immagine di contatore, che è la richiesta a cui questo servizio risponde più che a ogni altra.
Il costo visibile è memoria più che tempo. Il picco per richiesta sale di circa quattro megabyte e, a differenza degli opcode — condivisi da tutti i processi — quella parte si paga per richiesta, in parallelo, da ogni lavoratore insieme.
Qualcuno se n'era già accorto
Il glob è lib/*.php. Non scende in profondità, e due cartelle stanno appena sotto: lib/live/ con 229 kilobyte e lib/recht/ con 182 kilobyte. Quattrocento kilobyte che stavano nel percorso di caricamento e ora non ci sono più.
Sono stati spostati perché sono grandi e servono di rado: le sezioni delle statistiche e i testi legali. Stare una cartella più in basso è tutto il meccanismo. Non c'è configurazione, non c'è caricatore differito, non c'è mappa di autocaricamento — un file è nel glob oppure no.
È una correzione vera e vale la pena dirlo, perché il passo successivo ovvio sarebbe un autocaricatore, e un autocaricatore qui sarebbe un grande cambiamento per un problema che un mkdir ha già risolto due volte.
Perché resta
Un singolo glob è la cosa più semplice che possa funzionare, e alla misura funziona: un terzo di millisecondo, su un Raspberry Pi, sul percorso che deve essere veloce. Sostituirlo con inclusioni una per una significa che ogni nuovo file ha bisogno di una riga da qualche parte, e la riga dimenticata produce un errore fatale in produzione invece di un caricamento un po' più grande.
Quel che manca è mettere per iscritto ciò che l'ha reso sopportabile fin dall'inizio: quando un file in lib/ diventa grande, si sposta in una sottocartella e viene incluso dove serve. È la regola che quelle due cartelle seguivano, e non era scritta da nessuna parte fino a oggi.
La versione generale è più noiosa della misura. «Tutto carica tutto» di solito è la forma sbagliata, e conviene sapere se ti sta costando qualcosa prima di ricostruirlo. Qui costa 0,33 millisecondi e quattro megabyte, e i quattro megabyte sono la parte da tenere d'occhio, perché crescono con la concorrenza e i millisecondi no.