DIEGO CECATO / NOTE DAL CAMPO

Linux e Claude Code: il collo di bottiglia era un contatore

diego cecato//
Linux e Claude Code: il collo di bottiglia era un contatore

Quando si legge che Linus Torvalds ha usato Claude Code per lavorare al kernel Linux, la discussione prende quasi sempre la direzione meno interessante: l’intelligenza artificiale può scrivere codice di sistema oppure è un pericolo da tenere lontano? Nel frattempo, sotto quel titolo, c’è una questione molto più concreta. Un’operazione apparentemente innocua, ripetuta da molti thread, può trasformarsi in un limite alla scalabilità. E per capirlo non serve prendere posizione in una guerra di religione sull’AI: serve guardare il percorso dei dati.

Nel repository ufficiale di Torvalds è comparso il branch sperimentale noref-filemap-read-with-llm. Il riferimento è verificabile nel messaggio del commit e nella ricostruzione di Phoronix. La proposta riguarda letture concorrenti dalla page cache senza prendere, nel percorso interessato, il consueto riferimento al folio. Non è una funzionalità già entrata nel kernel principale, e i numeri pubblicati non sono una certificazione universale. È proprio questa distinzione a rendere il caso istruttivo.

Un contatore condiviso può costare più del dato letto

La page cache permette al kernel di servire dati di file già presenti in memoria, evitando un nuovo accesso al dispositivo di archiviazione. Un folio è una delle unità con cui Linux organizza quelle pagine. Prima di usare un folio, il codice deve assicurarsi che l’oggetto resti valido per tutta l’operazione: normalmente entra in gioco un conteggio dei riferimenti, incrementato e poi decrementato. Il meccanismo tutela la correttezza della memoria, non è un accessorio che si possa togliere perché il profiler mostra qualche ciclo di troppo.

Il problema emerge quando molti thread leggono ripetutamente pochi byte dello stesso file. Se tutti aggiornano lo stesso contatore, le CPU devono coordinarsi sulla linea di cache che lo contiene. Il dato richiesto può essere già caldo in memoria, ma la sincronizzazione necessaria a proteggere il folio continua a muoversi fra i core. In questo scenario il tempo non viene speso a leggere i sedici byte: viene speso a garantire che l’oggetto da cui provengono quei byte non scompaia mentre lo si usa.

È una lezione che vale ben oltre Linux. Aumentare il numero di thread non significa aumentare il lavoro utile se ogni thread deve continuamente toccare lo stesso stato condiviso. La contesa non è sempre un lock visibile nel codice: può nascondersi in un contatore atomico, in una struttura dati apparentemente innocua o in una cache line che passa da un core all’altro. La domanda tecnica giusta non è soltanto quanto costa una funzione, ma quanto cambia il suo costo quando la eseguono tutti insieme.

Togliere il riferimento non significa togliere il problema

La serie sperimentale cerca di spostare la garanzia di sicurezza su un altro meccanismo: letture protette da RCU, cioè read-copy-update, e rilascio differito dei folio dopo il periodo di grazia necessario. In termini intuitivi, invece di chiedere a ogni lettore di modificare un contatore condiviso, si impedisce che la memoria venga liberata troppo presto mentre i lettori stanno ancora attraversando quel percorso.

Questo può ridurre drasticamente la contesa nel caso mirato, ma introduce un conto diverso. La memoria in attesa di rilascio deve essere gestita; i percorsi di eviction, migrazione, fault e pressione di memoria devono continuare a rispettare le invarianti. La serie include accorgimenti per evitare che le code di liberazione differita crescano senza controllo, ma il messaggio del lavoro riconosce che la gestione della memoria differita resta una delle parti meno risolte. Il guadagno nel percorso caldo non cancella i casi limite: li sposta nel perimetro da dimostrare corretto.

Chi ha affrontato ottimizzazioni concorrenti conosce il rischio: si elimina un costo misurabile e si introduce una dipendenza meno evidente, che magari compare soltanto sotto pressione, su un’altra architettura o in un carico misto. Per questo un cambiamento del genere non si giudica dalla bellezza della patch né dalla quantità di codice eliminato. Si giudica dalle proprietà che conserva e da quelle che deve ancora dimostrare.

Il benchmark racconta una storia vera, ma molto stretta

Secondo le misure riportate nel messaggio del branch, sul portatile di Torvalds un test con otto thread che effettuano piccole letture dello stesso file passa da circa 9,9-10,4 milioni a 30-33 milioni di letture al secondo. Nello stesso scenario, i cicli kernel per lettura scendono da circa 2.950 a circa 725. Sono numeri importanti, ma vanno letti con il loro contesto: una macchina, un file, una dimensione di lettura, una configurazione di test scelta proprio per far emergere la contesa. Non sono misure indipendenti su server eterogenei.

Lo stesso materiale descrive casi in cui il costo non migliora e test ancora mancanti. Non c’è contraddizione: un’ottimizzazione per molti lettori concorrenti può peggiorare un percorso meno conteso. Il punto non è trovare un numero abbastanza grande da diventare un titolo, ma capire quali variabili lo producono. Numero di thread, topologia dei core, stato della cache, mitigazioni, temperatura e pressione sulla memoria non sono note a piè di pagina: sono parte del risultato.

Una misurazione può essere eseguita correttamente e restare insufficiente per decidere un merge. Servono riproduzioni, workload diversi, architetture diverse, test di correttezza e regressione. L’ho già affrontato, da un’altra prospettiva, ragionando sul memory hotplug di Linux e sui colli di bottiglia nascosti nelle scansioni: prima di parlare di prestazioni bisogna identificare l’operazione che domina il costo e verificare che il miglioramento non abbia soltanto spostato il problema.

Claude Code ha prodotto codice. La responsabilità non è stata delegata

Torvalds ha dichiarato apertamente che Claude Code ha contribuito al codice, ai programmi di test, alla revisione riassunta nel messaggio e alla prima bozza del testo, lavorando sotto la sua direzione. Ha aggiunto che le misure sono state eseguite sulla sua macchina. È una descrizione del processo, non una garanzia che ogni riga sia corretta. E non autorizza a concludere che il kernel abbia già adottato quella soluzione.

La parte interessante dell’uso di un agente non è la velocità con cui può generare una patch. È la possibilità di esplorare una soluzione, produrre strumenti per misurarla, tornare sui risultati e sottoporla a una revisione tecnica. C’è però una trappola: se codice, test e prima revisione arrivano dallo stesso assistente, la loro apparente concordanza non equivale a una verifica indipendente. Un test che riproduce le ipotesi sbagliate dell’implementazione può passare perfettamente.

Qui torna utile distinguere la qualità dell’output dalla qualità del processo. Un benchmark per la code review assistita dall’AI misura qualcosa soltanto se è progettato per trovare errori, non per confermare la soluzione che abbiamo già deciso di adottare. Lo stesso vale per un test generato accanto alla patch: è un punto di partenza, non un certificato di correttezza. Chi propone il cambiamento resta responsabile del metodo, delle ipotesi e delle regressioni.

La lezione per chi costruisce sistemi

Questo caso non dimostra che gli agenti AI siano pronti a sostituire i maintainer del kernel. Dimostra una cosa più utile: un assistente può partecipare a un lavoro tecnico molto serio quando il problema è circoscritto, il comportamento è misurabile e qualcuno sa formulare le domande che contano. L’esperimento è interessante anche se la patch dovesse essere modificata profondamente o respinta. In quel caso avremmo imparato qualcosa sul costo del reference counting, sulle alternative basate su RCU e sui limiti del processo di verifica.

È il contrario della narrazione secondo cui basta chiedere a un modello di ottimizzare il codice e aspettare il miracolo. La parte difficile resta decidere che cosa misurare, isolare il collo di bottiglia, difendere le invarianti e capire se un risultato locale vale anche fuori dal laboratorio. Il branch di Torvalds è una proposta pubblica da sottoporre a quel lavoro, non la fine del lavoro. E la vera notizia, per chi sviluppa software, è che il metodo conta più della firma sul commit.

Domande frequenti

La patch di Torvalds è già nel kernel Linux?

No. Il lavoro è su un branch sperimentale chiamato noref-filemap-read-with-llm. Non è corretto descriverlo come una funzione già integrata nel ramo principale del kernel.

Perché il reference count dei folio può rallentare le letture?

Quando molti thread leggono piccoli blocchi dello stesso file, l’aggiornamento del contatore condiviso richiede coordinamento tra le CPU. La contesa sulla stessa linea di cache può pesare più della lettura dei dati già presenti in memoria.

Il miglioramento di circa tre volte vale per tutti i server?

No. Le misure riportate sono state eseguite da Torvalds su un solo portatile e su un test mirato con molti thread che leggono lo stesso file. Servono benchmark indipendenti, carichi diversi e verifiche di regressione.

Che cosa ha fatto Claude Code nel progetto?

Secondo la dichiarazione riportata nel messaggio del branch, l’assistente ha contribuito a codice, programmi di test, revisione e prima bozza del testo sotto la direzione di Torvalds. La responsabilità tecnica e la valutazione della patch restano umane.