La fondazione dell'inferenza nel browser: kernel WebGPU ottimizzati

Uno degli obiettivi più ambiziosi del team WebAI di Hugging Face è rendere l'inferenza nel browser il più veloce e intuitivo possibile. Per raggiungere questo traguardo, è necessario un approccio su più livelli: i modelli devono avere rappresentazioni compatibili con il browser, i runtime devono costruire piani di esecuzione efficienti, e le singole operazioni GPU alla base dello stack devono sfruttare al meglio numerosi dispositivi e implementazioni di browser diversi.

Oggi Hugging Face rilascia il primo strato di questo sforzo: @huggingface/kernels, una libreria minimale per caricare ed eseguire kernel WebGPU ottimizzati dall'Hub di Hugging Face, insieme a una collezione iniziale di 207 kernel disponibili su huggingface.co/webgpu-kernels. Questi kernel rappresentano un progresso significativo nell'ecosistema di intelligenza artificiale locale e nell'elaborazione GPU nel browser.

La collezione copre operazioni utilizzate in un'ampia varietà di architetture di machine learning e carichi di lavoro. Ancora più importante, ogni kernel è pubblicato come un pacchetto completo e versionato: la sua interfaccia, i template di shader, i casi di correttezza, i casi di benchmark e le istruzioni di utilizzo vivono insieme sull'Hub. Questo approccio democratizza lo sviluppo dell'IA locale, consentendo ai sviluppatori di accedere a ottimizzazioni di livello enterprise direttamente dai loro browser.

Fleet: benchmarking e testing nel browser

Parallel al lancio dei kernel, Hugging Face introduce anche Fleet, una suite di benchmarking e testing GPU in-browser che esegue e valuta i kernel sul vostro hardware. Oltre ai risultati per la vostra macchina specifica, Fleet offre alla comunità un modo per contribuire con prove di performance e correttezza da dispositivi che non potrebbero mai essere coperti in un laboratorio di test convenzionale.

Con il consenso degli utenti, ogni esecuzione aggiunge prove private che possono aiutare il team a trovare malfunzionamenti (risultati non corretti, casi patologicamente lenti, ecc.), migliorare le varianti dei kernel e prendere decisioni di ottimizzazione migliori su hardware reale. Questo approccio collaborativo trasforma ogni utente in un contribuente a un progetto di ricerca distribuito, creando un feedback loop che accelera il miglioramento continuo dell'intera piattaforma.

La catena di elaborazione: dalle operazioni GPU all'inferenza

Un modello eseguito nel browser si trasforma infine in una sequenza di operazioni GPU: moltiplicazioni di matrici, normalizzazioni, convolazioni, primitive di attenzione, operazioni di quantizzazione, trasformazioni di layout dei dati e molte altre. WebGPU mette queste operazioni a disposizione nei browser moderni attraverso un'API portabile, mentre WGSL fornisce un linguaggio comune per gli shader che le eseguono.

La portabilità, tuttavia, non garantisce automaticamente le prestazioni. Due shader possono implementare la stessa operazione e produrre lo stesso output mentre si comportano completamente diversamente su acceleratori diversi. La dimensione dei workgroup, i pattern di accesso alla memoria, la vettorizzazione, i tipi di dati e le strategie di fusione possono tutti influire sulle prestazioni. La scelta migliore può anche cambiare con la forma dell'input, il dispositivo, il browser e le funzionalità WebGPU disponibili.

Questo è il motivo per cui i kernel formano uno strato fondazionale dell'inferenza veloce nel browser. I runtime di livello superiore possono essere efficienti solo quanto le operazioni che dispatchano. Rendendo quelle operazioni individualmente scopribili, testabili, benchmarkabili e versionate, il team può migliorare la fondazione indipendentemente mantenendo un contratto stabile per gli strati sopra di esso.

Struttura e documentazione dei kernel

Ogni kernel nella collezione ha il suo repository e una kernel card. La card documenta la semantica dell'operazione, gli input, gli output, gli attributi, i tipi di dati supportati, i file sorgente e un esempio pronto all'uso di @huggingface/kernels.

Ad esempio, ai.onnx.Add implementa l'addizione element-wise con broadcasting multidirezionale. È una delle operazioni più semplici in una rete neurale, utilizzata ovunque dalle connessioni residue all'aggiunta di bias. La sua card documenta i due input, la forma dell'output trasmessa, i tipi di dati supportati e le varianti disponibili per forme e dispositivi diversi.

Dietro la card, il repository contiene gli artefatti necessari per comprendere e valutare l'implementazione:

  • WGSL source: il codice dello shader stesso, facile da leggere e da estendere
  • Manifest: il contratto JavaScript che descrive input, output, attributi e tipi supportati
  • Correctness tests: casi di test che verificano l'output con tolleranze appropriate per i tipi di dati
  • Benchmark cases: set di dati rappresentativi per misurare il throughput su diverse forme di input
  • README: spiegazioni sul design, on scelte di ottimizzazione e note su specifici acceleratori

Questa struttura trasforma uno shader in un artefatto software riutilizzabile. L'interfaccia è ispezionabile senza leggere WGSL, i casi di correttezza e performance viaggiano con l'implementazione, e le versioni pubblicate possono essere caricate esplicitamente piuttosto che dipendere da un URL di file non versionato. I kernel possono anche servire come implementazioni di riferimento per sviluppatori che costruiscono kernel WebGPU personalizzati o che integrano queste operazioni nei loro stessi runtime.

Compatibilità browser e requisiti WebGPU

L'esecuzione di questi kernel richiede un browser con supporto WebGPU. La disponibilità di WebGPU dipende dal browser, dal sistema operativo, dalla GPU e dal driver. È possibile verificarla in JavaScript con "gpu" in navigator. Questo controllo è essenziale per costruire esperienze web robuste che si degradino elegantemente su piattaforme senza supporto WebGPU.

@huggingface/kernels fornisce il ponte tra un repository di kernel e la vostra applicazione. Chiamate getKernel con un ID repository dell'Hub e una versione del contratto, quindi invocate la funzione restituita con dati di input tipizzati e forme di tensore. Ecco un piccolo esempio di bias-add:

Il secondo input viene trasmesso lungo la prima dimensione, producendo un output con forma [2, 3]. Il loader deriva quella forma di output e il tipo di dati logico dal contratto del manifest e dagli input, allocando poi automaticamente le uscite.

Semplicità d'uso e pattern di chiamata uniforme

L'addizione su sei float è deliberatamente la demo più piccola possibile. A questa scala, il round trip GPU costa molto più della matematica stessa. Il punto è lo schema di chiamata: rimane esattamente lo stesso per le operazioni pesanti dove i kernel ottimizzati pagano davvero, come la moltiplicazione di matrici (ai.onnx.MatMul). Solo l'ID del repository e gli input cambiano. Questo principio di uniformità significa che gli sviluppatori possono imparare un modello una volta e applicarlo all'intera libreria.

Anche questa operazione elementare illustra perché i kernel hanno bisogno di varianti. L'addizione di forma uguale può utilizzare un percorso vettorizzato diretto, mentre gli input trasmessi hanno bisogno di una logica di indicizzazione diversa. Il kernel Add pubblicato include varianti per forme uguali, broadcasting vettorizzato, elaborazione scalare e broadcasting generale. Il runtime può selezionare un'implementazione che si adatta alla chiamata corrente e al dispositivo senza modificare l'API esposta all'applicazione.

L'opzione version: 1 seleziona la versione 1 del contratto del kernel pubblicato. È separato da un opset ONNX, da un since_version di un operatore o da una revisione del modello. Mantenere questi concetti separati consente alle applicazioni di dipendere da un contratto stabile rivolto a JavaScript mentre le implementazioni dei kernel evolvono dietro le quinte. Questo approccio di versioning decoupled è cruciale per mantenere la stabilità API mentre si permettono miglioramenti continui alle prestazioni.

Benchmark e confronti di prestazioni: risultati significativi

Quanto vale davvero la differenza dei kernel ottimizzati? Il team di Hugging Face ha messo la sua collezione a confronto diretto con ORT WebGPU su una GPU Apple M4, utilizzando ONNX Runtime Web 1.30.0-dev.20260826-b1f76d586a. Hanno iniziato con 1.756 casi di test su tutti i 207 operazioni e hanno mantenuto gli 809 casi in cui entrambi i lati hanno prodotto output corrispondenti e timing affidabili.

Attraverso quei confronti, i kernel di Hugging Face erano 2,57x più veloci per media geometrica e 1,90x più veloci alla mediana, con 629 vincite, 176 perdite e 4 pareggi. Questo è un miglioramento sostanziale che suggerisce che le ottimizzazioni specifiche di WebGPU sono materie e pronte per l'uso in produzione.

Operazioni specifiche e guadagni di prestazioni

Alcuni guadagni individuali sono stati molto più grandi. Un caso Einsum bilineare particolarmente difficile (i,ij,j con dimensione 4096) è stato eseguito in 0,136 ms con il kernel di Hugging Face rispetto a 1.396 ms con ORT WebGPU: più di 10.000x più veloce. Un CumSum per riga su [256, 4096] era 301x più veloce, a 0,016 ms rispetto a 4,784 ms. Questi sono casi insoliti piuttosto che miglioramenti che dovreste aspettarvi ovunque, ma mostrano quanto un kernel specializzato possa aiutare quando un'implementazione generale colpisce un percorso lento.

Le operazioni comuni mostrano anche miglioramenti significativi, anche se meno drammatici. I kernel mostrano consistentemente vantaggi nelle seguenti categorie:

  • Operazioni di normalizzazione: LayerNorm, BatchNorm e affini beneficiano della fusione e dell'accesso efficiente alla memoria
  • Operazioni di attenzione: i primitivi di attenzione specializzati superano le implementazioni generiche
  • Moltiplicazioni di matrici: MatMul ottimizzati per diverse forme di input
  • Operazioni di quantizzazione: kernel specializzati per la conversione e la compressione dei dati

Metodologia di misurazione e considerazioni importanti

È importante notare che il team ha misurato il lavoro eseguito sulla GPU stessa, escludendo setup come il caricamento di kernel, la creazione di sessioni, l'upload di input, la compilazione di shader e la lettura dei risultati. I carichi di lavoro molto brevi sono naturalmente più difficili da misurare e i piccoli casi possono beneficiare della cache GPU, quindi questi numeri sono meglio interpretati come un utile confronto piuttosto che una promessa per ogni applicazione.

Sono anche risultati per operazioni individuali, non per modelli completi. Le prestazioni esatte cambieranno tra GPU e browser, ed è per questo che Fleet è così importante per costruire un quadro più ampio. La variabilità di WebGPU tra GPU, browser e driver è una realtà con cui i developer devono convivere.

Fleet: raccolta di dati distribuita e miglioramento continuo

WebGPU mostra prestazioni diverse su GPU, browser e driver diversi, quindi i risultati da una sola macchina raccontano solo parte della storia. Fleet consente a chiunque di eseguire verifiche di correttezza e prestazioni nel browser e vedere come i kernel si comportano sul loro hardware.

Con il consenso, ogni esecuzione contribuisce privatamente con prove che aiutano il team a individuare malfunzionamenti specifici del dispositivo, confrontare varianti e migliorare le regole di selezione. L'obiettivo è semplice: utilizzare una copertura ampia e reale per rendere i kernel più veloci e affidabili per tutti. Questo approccio distribuito trasforma i milioni di dispositivi nel mondo in una infrastruttura di test distribuita, raccogliendo dati su configurazioni hardware e driver che nessun laboratorio potrebbe mai testare completamente.

Espansione futura e roadmap

I 207 kernel iniziali sono un punto di partenza, non lo stato finale. Pubblicare kernel indipendentemente sull'Hub fornisce un luogo comune per ispezionare contratti, confrontare implementazioni, riprodurre verifiche di correttezza e migliorare le prestazioni senza incorporare ogni shader direttamente in ogni runtime.

La collezione è anche parte dell'ecosistema kernel più ampio dell'Hub: nella pagina Kernels, i kernel WebGPU si trovano insieme a kernel per CUDA, ROCm, Metal e altre piattaforme, e possono essere filtrati, ordinati ed esplorati come qualsiasi altro artefatto sull'Hub. Questo approccio unificato significa che gli sviluppatori possono gestire tutte le loro ottimizzazioni della GPU in un'unica posizione coerente.

Questo è il fondamento di basso livello per i prossimi passi nello stack di inferenza del browser. Il team è entusiasta di connettere questi kernel agli strumenti di modellamento di livello superiore, continuare ad espandere la copertura operativa e rendere l'inferenza veloce locale più facile da usare in tutto l'ecosistema WebAI.

Questioni