La forgia e la ricerca che meritano la prova
Il vecchio problema del trucco
Ogni sistema software serio ha alcuni pezzi di codice che contano molto più di quanto la loro dimensione suggerisca. Un ciclo che viene eseguito milioni di volte. Un'operazione sui bit in un percorso di compressione. Una piccola routine sulle matrici. Un kernel di aritmetica modulare. Il genere di cose che in una revisione del codice sembrano innocue e poi decidono silenziosamente la bolletta energetica, il budget di latenza o il numero di macchine che devi comprare. Il software è molto democratico. Una piccola funzione può rovinare la riunione a tutti.
Storicamente, quei kernel vengono migliorati dalle persone. Un ingegnere senior ricorda un trucco da un articolo. Qualcuno scava in un vecchio post di un forum. Viene scritta una suite di benchmark. Si provano alcuni candidati. Il più veloce vince se sembra ancora corretto. Poi l'organizzazione lo congela, perché toccarlo di nuovo sembra come punzecchiare un trasformatore addormentato con una forchetta.
Forge è una ricerca su una versione migliore di quel processo. È un motore di sintesi di programmi per implementazioni piccole e critiche: dagli una specifica tipizzata e delle proprietà, lascia che cerchi programmi candidati, misura e confronta i compromessi, verifica l'equivalenza, poi riduci l'implementazione scoperta verso i target che contano. La parola importante non è ricerca. La parola importante è ancora. Deve ancora essere corretto.
È per questo che Forge vive nella ricerca. Non è un pulsante di prodotto pubblico in cui qualcuno digita rendi più veloce e riceve un miracolo. È un banco di lavoro di sintesi per esperimenti con i partner, scoperta di kernel e ricerca su quanto lontano può arrivare la ricerca automatizzata quando è legata alla verifica invece che al teatro dei benchmark.
Una specifica è la linea di partenza
Ottimizzare senza una specifica è solo giocare d'azzardo con nomi di variabili più carini. Nel momento in cui appare un candidato intelligente, il team deve sapere cosa deve preservare. Gestisce ogni input o solo quelli amichevoli del benchmark? Rispetta il comportamento di overflow? L'identità algebrica è valida sotto la rappresentazione effettivamente usata? Mantiene la stessa semantica quando viene ridotta a un backend diverso?
Forge parte da espressioni tipizzate e proprietà perché la ricerca ha bisogno di un confine. Il confine dice cosa conta come equivalente. Senza di esso, il motore può trovare qualcosa di sorprendentemente veloce cancellando metà del lavoro. I computer sono eccellenti nella conformità malevola quando il contratto è vago.
Il lato della ricerca è deliberatamente plurale. La ricerca enumerativa è utile quando lo spazio è abbastanza piccolo da coprire. CEGIS è utile quando i controesempi possono guidare il raffinamento. La programmazione genetica e MCTS esplorano in modo diverso. La ricerca guidata dall'apprendimento automatico può imparare modelli di costo e dare priorità alle regioni promettenti. Nessuno di questi è universalmente il migliore. Non è una debolezza. È così che si comporta la ricerca nel mondo reale. Se un solo martello risolvesse ogni kernel, le cassette degli attrezzi sarebbero molto noiose e i produttori di hardware sarebbero disoccupati.
La domanda di ricerca è come combinare quei motori con abbastanza pressione di prova affinché il risultato non sia solo intelligente. Un kernel sintetizzato deve sopravvivere sia al benchmark del percorso felice sia al verificatore del percorso infelice. Altrimenti il miglioramento non è ingegneria. È un trucco di magia con un costo di manutenzione.
Il verificatore è l'adulto nella stanza
Forge usa uno stack di verifica perché nessun singolo controllo è sufficiente per ogni dominio. Gli esempi rapidi sono economici e utili. I property test trovano ampie classi di errori e riducono i controesempi a qualcosa di leggibile da un essere umano. I risolutori SMT come Z3 e CVC5 possono dimostrare l'equivalenza quando l'encoding è trattabile. Il controllo esaustivo è pratico per domini piccoli. La saturazione di uguaglianza con e-graph offre un'altra via attraverso l'equivalenza algebrica.
Lo stack conta perché i kernel falliscono in modi fastidiosi. Un candidato può superare ogni benchmark ordinario ed essere comunque sbagliato su un caso limite. Può essere corretto per input senza segno e sbagliato per quelli con segno. Può essere corretto in un campo matematico e sbagliato dopo che la rappresentazione scelta va in overflow. Può essere corretto prima del lowering e sottilmente sbagliato dopo una decisione di selezione delle istruzioni. Il verificatore esiste perché l'ottimismo non è una strategia di test. Abbiamo controllato. Ripetutamente. Rimane vero.
C'è anche una ragione pratica per mantenere più percorsi di dimostrazione. I metodi formali sono potenti, ma non sono gratuiti. Alcuni encoding vanno in timeout. Alcuni domini sono troppo grandi per il controllo esaustivo. Alcune proprietà sono più facili da testare probabilisticamente prima e dimostrare dopo. Forge tratta la verifica come un imbuto, non come un rituale di purezza. I controlli economici scartano sciocchezze evidenti. I controlli più forti proteggono il candidato finale.
Veloce non è un solo numero
Il lavoro sulle prestazioni diventa assurdo quando a una singola metrica è permesso dominare ogni conversazione. La latenza conta. Il numero di operazioni conta. L'uso della memoria conta. La pressione sui registri conta. Il tempo di compilazione a volte conta. La portabilità conta quando lo stesso kernel deve vivere su più di un backend. Un candidato che vince sulla latenza bruciando registri come un piccolo falò può essere sbagliato per il target reale. Un candidato minuscolo ma lento può essere utile altrove. Il contesto rimane imbattuto.
Forge quindi inquadra l'ottimizzazione come un problema di Pareto. Il motore può cercare tra più obiettivi invece di fingere che esista un punteggio universale consegnato da un foglio di calcolo molto sicuro di sé. L'output utile non è sempre il singolo candidato più veloce. A volte è una famiglia di candidati con compromessi visibili, così un ingegnere può scegliere quello che si adatta al vincolo di deployment.
Questo è anche il motivo per cui non mi piacciono le affermazioni nude di speedup nei post del blog. La pagina di ricerca può descrivere aspettative interne e obiettivi sperimentali, ma le affermazioni pubbliche richiedono esecuzioni fresche, hardware attuale, flag del compilatore attuali e un contesto di workload esatto. Altrimenti il numero diventa un souvenir. I souvenir sono carini. Non sono architettura.
L'affermazione onesta è comunque più forte: Forge riguarda il rendere la ricerca riproducibile, comparabile e verificabile. Quando un candidato vince, dovremmo sapere su quale obiettivo ha vinto, quali candidati ha battuto, quale verificatore lo ha accettato e quale backend prende di mira. Questo è molto più utile di un numero che fluttua in una presentazione sembrando costoso.
Il lowering è dove le dimostrazioni vengono messe alla prova
Un'implementazione scoperta è utile solo se sopravvive al viaggio verso i target reali. La ricerca di Forge copre la discesa verso backend come x86-64, RISC-V, WASM, percorsi GPU Vulkan, C e Verilog. Quell'elenco di target non è decorazione. Ogni backend ha i propri vincoli, forme di istruzioni, comportamento della memoria e modalità di errore. La stessa specifica deve mantenere il suo significato mentre l'implementazione diventa qualcosa che il target può effettivamente eseguire.
È qui che la sintesi si collega al resto dello stack Dweve. Core vuole loop interni efficienti. Numerus si occupa di kernel numerici deterministici. BitWeave vuole operazioni binarie e su matrici che non sprechino la CPU. Kera si occupa di abbassare i grafi di calcolo verso hardware reale. Forge può alimentare questi livelli solo se l'implementazione generata è più che veloce. Deve essere equivalente, abbastanza portabile per il target scelto e ispezionabile quando qualcosa cambia.
Cosa cambia per i team
Per un team, il cambiamento interessante non è che una macchina possa scoprire un kernel più veloce. È che il lavoro sui kernel diventa meno dipendente dal folklore. Invece di un esperto che ricorda il trucco giusto, il processo diventa: dichiarare il contratto, esplorare lo spazio, misurare i candidati, provare l'equivalenza, registrare il compromesso e generare il codice per il target. Gli esseri umani decidono ancora. Semplicemente smettono di fare tutta la scoperta a mano.
Questo conta per le operazioni perché il debito di prestazioni è costoso in un modo che le organizzazioni spesso nascondono. Un kernel lento diventa più server. Più server diventano più costi, più energia, più complessità di distribuzione e più rumore nella pianificazione. Un'ottimizzazione sbagliata diventa incidenti. Un trucco corretto ma non documentato diventa un rischio di migrazione futuro. Forge è ricerca per ridurre quel mucchio di sciocchezze evitabili.
C'è anche un cambiamento culturale. Il lavoro manuale sulle prestazioni spesso premia l'eroismo. Qualcuno sparisce nella caverna e torna con un hack di bit intelligente. Tutti applaudono, nessuno lo capisce davvero e l'azienda ha acquisito un piccolo oggetto sacro. Forge spinge il processo verso l'evidenza: ecco la specifica, ecco il percorso di ricerca, ecco i candidati scartati, ecco il verificatore, ecco il backend selezionato. Meno mitologia. Più ricevute.
Dove il lavoro è ancora difficile
Niente di tutto ciò rende la sintesi facile. Le specifiche sono difficili. Se la specifica è sbagliata, il motore può scoprire fedelmente la cosa sbagliata. Gli spazi di ricerca possono esplodere. I solver possono andare in timeout. I modelli di costo possono trarre in inganno. I backend possono esporre dettagli di cui l'espressione astratta non si curava. La verifica può essere forte in un dominio e scomoda in un altro. Chiunque venda la sintesi di programmi come un distributore automatico di codice ottimale o sta saltando le parti difficili o fa pagare un extra per la delusione.
Forge è interessante proprio perché affronta direttamente quelle parti difficili. Combina diverse strategie di ricerca. Tiene la verifica vicina. Tratta gli obiettivi come compromessi. Punta a backend reali. Rimane un programma di ricerca perché stiamo ancora imparando dove si trova il confine tra scoperta automatizzata, giudizio umano, limiti dei solver e realtà della distribuzione.
Vale la pena esplorare quel confine. L'industria del software ha troppi piccoli loop critici, troppo folklore sulle prestazioni duplicato e troppe ottimizzazioni che nessuno vuole toccare di nuovo. Se Forge può trasformare anche solo parte di quel lavoro in un processo di evidenza ripetibile, il risultato non è solo codice più veloce. È codice più calmo. Il codice calmo è sottovalutato, soprattutto da chi non è mai stato chiamato alle 02:17.
Di cosa ha bisogno una buona esecuzione di Forge
Un esperimento serio con Forge inizia prima che il motore venga eseguito. Il team deve portare un kernel reale, non una vaga lamentela sulle prestazioni. Servono input rappresentativi, casi limite noti, hardware di destinazione, benchmark attuali e il motivo aziendale per cui questo kernel è importante. Altrimenti il motore di sintesi può passare molto tempo a risolvere un problema che in realtà nessuno ha. Gli strumenti di ricerca non sono immuni agli input spazzatura. Rendono semplicemente la spazzatura più costosa da esaminare.
L'input più utile è un contratto piccolo e preciso. Cosa dovrebbe calcolare la funzione? Quali leggi algebriche contano? Quale comportamento di overflow è intenzionale? Quali intervalli sono impossibili per costruzione e quali semplicemente non si sono verificati nell'ultima esecuzione di test? Quali output possono tollerare un'approssimazione e quali no? Un team che non sa rispondere a queste domande probabilmente non ha ancora un problema di ottimizzazione. Ha un problema di chiarimento del prodotto che indossa un cappello da compilatore.
Una buona esecuzione richiede anche una postura di destinazione. x86-64 e RISC-V non sono la stessa cosa. WASM ha vincoli diversi. I percorsi GPU Vulkan si preoccupano di forme e movimento della memoria. Verilog solleva domande hardware che i normali team applicativi raramente apprezzano prima del caffè. Forge può esplorare l'abbassamento di destinazione, ma non può decidere le priorità organizzative. Se la portabilità conta più della velocità su una singola destinazione, dillo. Se la latenza batte la memoria, dillo. Se la pressione dei registri è il limite pratico, dillo anche quello. Il motore è potente, non psichico.
L'output dovrebbe essere trattato come un pacchetto di prove. Candidato, obiettivo, percorso di dimostrazione, controesempi scartati, backend, contesto di benchmark e avvertenze aperte. Quel pacchetto è ciò che consente agli esseri umani di prendere una decisione sensata. A volte la mossa vincente è adottare il candidato. A volte è mantenere il vecchio kernel perché il compromesso sulla portabilità non vale la pena. A volte la scoperta è che la specifica era troppo vaga. Tutti e tre i risultati sono utili. Solo uno di essi sembra entusiasmante in una demo, motivo per cui le demo sono un povero sostituto dell'ingegneria.
La lezione
La lezione di Forge è semplice: le prestazioni non dovrebbero superare la dimostrazione. La ricerca è potente, ma un motore di ricerca senza verifica è solo un modo molto energico di creare bug. La verifica è potente, ma senza ricerca aspetta che gli esseri umani portino candidati. Forge mette insieme le due cose e chiede quali kernel possiamo scoprire quando alla macchina è permesso esplorare, ma non è permesso mentire.
Questa è la ricerca che vale la pena fare. Specifiche tipizzate, ricerca di candidati, imbuti di dimostrazione, obiettivi Pareto e abbassamento del backend. Non magia. Non una scorciatoia di prodotto. Un modo per creare codice piccolo e migliore con prove allegate.