Skip to content

Inferenza distribuita: condividi il carico tra più macchine

Durante il prefill (elaborazione iniziale del prompt), i worker possono lavorare in pipeline: mentre il coordinator sta processando il chunk N+1, il worker 1 può processare il chunk N, e così via. Questo accelera il prefill, specialmente per prompt lunghi.

Esempio numerico (due Mac M5 Max, Thunderbolt 5):

Prompt lengthSingle machine (Q2)Two machines (Q4)Speedup
9,421 token421 t/s582 t/s1.38x
28,684 token405 t/s674 t/s1.66x
63,819 token353 t/s654 t/s1.85x

Nota: i numeri “single machine” sono con Q2 (più leggero), mentre “two machines” con Q4 (più pesante). A parità di modello, lo speedup sarebbe ancora più marcato.

La generazione è strettamente autoregressiva: il token N+1 non può iniziare finché il token N non ha completato l’intero percorso attraverso tutti i worker e il coordinator ha effettuato il sampling. Quindi ogni token paga la latenza di rete andata/ritorno tra i worker.

Nello stesso setup Thunderbolt 5 (latenza ~0.45 ms tra le macchine), la generazione passa da 30.59 t/s (single machine, Q2) a 24.67 t/s (distribuita, Q4). Una perdita del 19%. Su reti più lente (WiFi, Internet) il degrado è molto peggiore.

Configurazione pratica: DeepSeek V4 PRO Q4 su due Mac Studio

Section titled “Configurazione pratica: DeepSeek V4 PRO Q4 su due Mac Studio”

Questo è l’uso più significativo della distribuzione. Avrai bisogno di:

  • Due Mac Studio con almeno 256 GB di RAM ciascuno (meglio 512 GB)
  • Una connessione di rete veloce: Thunderbolt 5 (consigliato), Ethernet 10 GbE, o almeno Gigabit
  • I due file GGUF specifici per il PRO Q4 diviso

Passo 1: Scaricare i due file GGUF su entrambe le macchine

Section titled “Passo 1: Scaricare i due file GGUF su entrambe le macchine”

Su entrambi i Mac, esegui:

Terminal window
cd ds4
./download_model.sh pro-q4-layers00-30
./download_model.sh pro-q4-layers31-output

Questo scaricherà due file nella cartella gguf/:

  • DeepSeek-V4-Pro-Q4K-Layers00-30.gguf (prima metà)
  • DeepSeek-V4-Pro-Q4K-Layers-31-output.gguf (seconda metà + output head)

Passo 2: Decidere chi è coordinator e chi worker

Section titled “Passo 2: Decidere chi è coordinator e chi worker”

Scegli una macchina come coordinator (quella da cui darai i comandi). L’altra sarà worker. L’importante è che il coordinator possieda i layer iniziali (0:30) e il worker quelli finali (31:output).

Sulla macchina worker, esegui:

Terminal window
./ds4 \
-m gguf/DeepSeek-V4-Pro-Q4K-Layers-31-output.gguf \
--role worker \
--layers 31:output \
--coordinator <IP_del_coordinator> 1234
  • --role worker: specifica il ruolo
  • --layers 31:output: intervallo di layer (dal layer 31 fino all’output head)
  • --coordinator <IP> 1234: indirizzo e porta del coordinator (aspetterà connessioni su quella porta)

Sulla macchina coordinator, esegui:

Terminal window
./ds4 \
-m gguf/DeepSeek-V4-Pro-Q4K-Layers00-30.gguf \
--role coordinator \
--layers 0:30 \
--listen 0.0.0.0 1234
  • --role coordinator: ruolo principale
  • --layers 0:30: intervallo di layer (dal layer 0 al 30)
  • --listen 0.0.0.0 1234: aspetta connessioni su tutte le interfacce, porta 1234

Il coordinator, dopo aver ricevuto la registrazione del worker, stamperà un messaggio tipo:

Distributed route: coordinator layers 0-30, worker 192.168.1.100 layers 31-output

Passo 5: Usare il coordinator come se fosse un ds4 normale

Section titled “Passo 5: Usare il coordinator come se fosse un ds4 normale”

Ora sulla macchina coordinator puoi usare il CLI normalmente:

Terminal window
./ds4 # (già avviato con i comandi sopra, oppure avvia in modalità interattiva)

Puoi anche usare ds4-server o ds4-agent in modalità distribuita (passando gli stessi flag). Tutta l’interazione avviene attraverso il coordinator.

Configurazione per accelerare il prefill (Flash su due Mac)

Section titled “Configurazione per accelerare il prefill (Flash su due Mac)”

Se non hai bisogno di far stare un modello enorme, ma vuoi solo accelerare il prefill di prompt lunghissimi (es. 100k token) sul modello Flash, puoi distribuire i layer in modo più equilibrato.

Esempio su due Mac M5 Max con 128 GB, modello Flash Q4:

Worker (possiede la seconda metà):

Terminal window
./ds4 \
-m gguf/DeepSeek-V4-Flash-Q4K...gguf \
--role worker \
--layers 20:output \
--coordinator 169.254.43.68 1234

Coordinator (possiede la prima metà + output head? No, l’output head ce l’ha l’ultimo worker):

Terminal window
./ds4 \
-m gguf/DeepSeek-V4-Flash-Q4K...gguf \
--role coordinator \
--layers 0:19 \
--listen 0.0.0.0 1234

In questo caso, il worker possiede l’output head (perché 20:output include l’ultimo layer e la testa). I logits vengono calcolati dal worker e restituiti al coordinator.

Variante per link lenti: Su reti molto lente o con bande limitate, puoi usare --layers 20:42 (senza output). In questo caso il coordinator mantiene l’output head e calcola i logits localmente, scambiando meno dati per token ma aggiungendo lavoro al coordinator.

Tipo di connessioneLatenza tipicaPrefill (speedup)GenerazioneConsigliata
Thunderbolt 5 (bridge)0.45 ms1.6-1.8x-19%✅ Ideale
Ethernet 10 GbE0.3-0.5 mssimilesimile✅ Ottima
Gigabit Ethernet0.3-0.5 mssimileleggermente peggio👍 Accettabile
WiFi 6 (buon segnale)2-5 ms~1.3x-60%⚠️ Solo prefill
Internet/VPN50-150 ms~1.1x-90%🔴 Non raccomandato

2. Ridurre la dimensione delle attivazioni in transito

Section titled “2. Ridurre la dimensione delle attivazioni in transito”

Le attivazioni scambiate tra worker sono tensori di hidden state (di solito float32). Puoi comprimerle a 16 o 8 bit per ridurre il traffico di rete:

Terminal window
# Sul coordinator (e si propaga ai worker)
./ds4 ... --dist-activation-bits 16

--dist-activation-bits 16 dimezza il traffico, con perdita di qualità trascurabile. --dist-activation-bits 8 è più aggressivo, ma antirez segnala che il miglioramento non è enorme, quindi questa opzione potrebbe essere rimossa in futuro.

3. Regolare la profondità della pipeline (--dist-prefill-window)

Section titled “3. Regolare la profondità della pipeline (--dist-prefill-window)”

--dist-prefill-window N controlla quanti chunk di prefill possono essere in volo end-to-end tra coordinator e worker. Il default è conservativo e limitato. Aumentarlo può migliorare il throughput su prompt molto lunghi, ma consuma più memoria.

4. Regolare la dimensione dei chunk di prefill

Section titled “4. Regolare la dimensione dei chunk di prefill”

Il parametro --dist-prefill-chunk determina quanti token vengono elaborati in pipeline prima di sincronizzarsi. Il default (4096 token) è ottimale. Modificalo solo per esperimenti.

Terminal window
./ds4 ... --dist-prefill-chunk 2048

Aggiungi --debug al coordinator per vedere la telemetria della distribuzione:

Terminal window
./ds4 --role coordinator --layers 0:19 --listen 0.0.0.0 1234 --debug

Vedrai messaggi come:

[dist] route built: coordinator(0-19) worker(20-output)
[dist] prefill chunk 0: local 45.2ms, worker 48.1ms, total 50.3ms
[dist] generate token 42: local 39.8ms, worker 44.2ms, total 84.0ms

Questi numeri ti aiutano a capire se il bilanciamento dei layer è equilibrato (i tempi local e worker dovrebbero essere simili). Se un worker è molto più lento, sposta più layer sull’altro.

Se un worker si disconnette (perché il processo viene ucciso o la rete salta), il coordinator:

  • Rimuove quel worker dal route attivo.
  • Se c’è un altro worker compatibile (con gli stessi layer), lo reinserisce.
  • Se il route diventa incompleto, le richieste successive falliranno finché non si riconnette un worker adeguato.

Quando un worker si riconnette, il coordinator replay i token mancanti dalla cronologia, ricostruendo lo stato KV del worker. Questo è possibile perché il coordinator mantiene una copia di tutti i token della conversazione.

  • Nessuna crittografia o autenticazione – La comunicazione tra coordinator e worker è in chiaro. Usala solo su reti fidate (LAN privata, Thunderbolt, VPN sicura).
  • Compatibilità dei binari – Coordinator e worker devono essere compilati dalla stessa versione di ds4. Commit diversi potrebbero avere protocolli incompatibili.
  • Latenza di rete imprevedibile – Su WiFi o Internet, la generazione diventa molto lenta. La distribuzione è pensata per laboratori con connessioni ad alta velocità e bassa latenza.
  • Memory mapping – Ogni worker mappa in memoria solo il proprio sottoinsieme di layer. Il modello totale non deve essere caricato interamente su nessuna macchina, ma ogni worker deve avere abbastanza RAM per la propria porzione + KV cache.
  • KV cache distribuita – Ogni worker mantiene la propria parte di KV cache. Quando viene salvata una sessione (es. con /save nell’agente), il coordinator raccoglie i tensori da tutti i worker e li unisce in un unico file. Al caricamento, li redistribuisce.
  • Il README ufficiale, sezione Distributed Inference
  • ds4_distributed.c – implementazione del protocollo
  • ds4_distributed.h – strutture dati

📘 Perché la generazione distribuita è più lenta?
In una singola macchina, dopo che il layer N ha finito, il layer N+1 può iniziare immediatamente (sono nello stesso processo). Nella distribuzione, dopo che il worker A ha finito, deve serializzare le attivazioni, inviarle via TCP, e il worker B deve deserializzarle prima di iniziare. Anche con una latenza di 0.5 ms, su 200 layer si perdono 100 ms per token – un’enormità. Inoltre, il coordinator deve aspettare il logits finale prima di campionare il prossimo token.

💡 Se devi solo accelerare il prefill, usa la distribuzione anche su reti più lente


Il prefill beneficia della pipeline anche con latenza più alta (es. WiFi), perché i chunk grandi (4096 token) sono elaborati in parallelo. La generazione sarà lenta, ma se hai solo bisogno di una risposta lunga (es. riassunto di un documento), puoi accettare il compromesso.