Hoppa till huvudinnehållet

Hur du snabbar upp lokala LLM:er med DFlash spekulativ avkodning

Lär dig hur du accelererar lokal inferens för Gemma 4 31B på en enskild RTX 4090 med DFlash spekulativ avkodning och Flash Attention jämfört med en baslinjeuppsättning.
Uppdaterad 17 juni 2026

Under de senaste veckorna har jag sett mycket entusiasm i r/LocalLLaMA-communityn kring spekulativ avkodning, DFlash, bättre hantering av KV-cache och optimerade byggnader av llama.cpp. Det intressanta är att folk får stora hastighetsökningar utan att uppgradera sin hårdvara.

I den här guiden kör vi Gemma 4 31B IT lokalt på en RTX 4090 24GB med BeeLlama.cpp, en fork av llama.cpp som stödjer DFlash spekulativ avkodning.

Vi kommer att testa modellen på två sätt. Först kör vi den utan DFlash för att skapa en baslinje. Därefter kör vi den med DFlash för att jämföra hastighetsförbättringen.

Vad är DFlash?

Enkelt uttryckt använder DFlash en utkastmodell för att förutsäga flera token i förväg, medan huvudmodellen verifierar dessa token i stället för att generera allt en token i taget. När många utkasttoken godkänns blir genereringen mycket snabbare samtidigt som utdata ligger nära originalmodellen.

I mitt experiment gav DFlash nästan en 3,7x hastighetsökning i vissa uppgifter, med utdata som var mycket lik baslinjen. Målet med den här guiden är att visa upplägget, köra båda versionerna och jämföra resultaten tydligt.

Hur DFlash fungerar

Standardgenerering i LLM:er är långsam eftersom de flesta modeller genererar text en token i taget. Varje token beror på den föregående, så modellen måste ta sig steg för steg genom svaret.

DFlash snabbar upp det med spekulativ avkodning.

I stället för att låta huvudmodellen generera varje token direkt använder DFlash en separat utkastmodell för att gissa flera kommande token först. Huvudmodellen verifierar sedan dessa utkasttoken i ett större steg. Om utkasttokenen är bra accepterar huvudmodellen dem. Om någon av dem är felaktig korrigerar huvudmodellen den och fortsätter.

Ett enkelt sätt att tänka på det:

  • Utan DFlash: huvudmodellen skriver en token i taget.
  • Med DFlash: utkastmodellen föreslår ett block av token, och huvudmodellen kontrollerar snabbt vilka som kan accepteras.

Diagram över arbetsflödet för DFlash spekulativ avkodning.

Diagram över arbetsflödet för DFlash spekulativ avkodning.

Detta är särskilt användbart för strukturerade uppgifter som programmering. Kod följer ofta förutsägbara mönster såsom imports, funktionsdefinitioner, indentering, loopar och vanlig syntax. Tack vare detta kan utkastmodellen ofta gissa nästa token korrekt, vilket gör att huvudmodellen kan acceptera fler token i varje steg.

DFlash vs MTP: Vad är skillnaden?

DFlash och Multi-Token Prediction (MTP) syftar båda till att lösa samma problem: de hjälper modellen att generera mer än en token per dyrt avkodningssteg.

Skillnaden är hur de skapar utkasttoken.

Metod

Hur det fungerar

Behövs extra modell?

Styrka

MTP

Använder inbyggda multi-token-prediktionshuvuden för att förutsäga framtida token

Vanligtvis ingen separat utkastmodell

Enklare setup när modellen redan stöder MTP

DFlash

Använder en separat DFlash-utkastmodell för att föreslå större block av token

Ja

Kan ge starka hastighetsökningar på strukturerade utdata som kod

Enkelt uttryckt är MTP vanligen inbyggt i själva modellen. Det förutspår flera framtida token med interna prediktionshuvuden, så när det stöds kan det vara enklare att konfigurera och mer minneseffektivt.

DFlash använder däremot en separat utkastmodell. Det kan göra uppsättningen något tyngre, men tillåter också mer aggressiv utkastning. Därför kan DFlash ge stora hastighetsökningar i strukturerade uppgifter där nästa token är lättare att förutsäga.

1. Konfigurera miljön

Jag rekommenderar starkt att du kör detta lokalt om du har ett RTX 3090- eller RTX 4090-GPU. Annars kan du hyra ett GPU från RunPod, Vast.ai eller någon annan GPU-leverantör.

I den här guiden använder vi en RunPod RTX 4090-pod. Jag började med den senaste RunPod PyTorch-mallen och gjorde några små ändringar:

  • Exponerade port 8910 för llama.cpp-servern
  • Ökade beständig lagring till 100 GB
  • Lade till min Hugging Face-token för att snabba upp modellnedladdning

Redigera den senaste Pytorch-mallen i Runpod

Med detta upplägg kostar poden omkring $0,70 per timme, beroende på aktuell RunPod-prissättning och tillgänglighet.

Runpod Pytorch-docker körs på RTC 4090-maskinen.

När poden är distribuerad, öppna JupyterLab från RunPods instrumentpanel. Starta sedan en ny terminal och installera grundläggande beroenden:

apt update
apt install -y git cmake build-essential curl wget python3-pip

Installera Linux-paket i Jupyter-terminalen.

2. Klona BeeLlama.cpp

Nästa steg är att klona BeeLlama.cpp, den llama.cpp-fork vi ska använda i detta upplägg.

BeeLlama.cpp är utformat för snabbare lokal GGUF-inferens samtidigt som det bekanta arbetsflödet i llama.cpp behålls. Du får samma typ av verktyg, inklusive llama-server, men med extra prestandafokuserade funktioner som DFlash spekulativ avkodning, adaptiv utkastkontroll och TurboQuant/TCQ KV-cachekomprimering

Kör följande kommandon i din JupyterLab-terminal:

git clone https://github.com/Anbeeld/beellama.cpp.git
cd beellama.cpp

Detta hämtar BeeLlama.cpp-förrådet och flyttar dig till projektmappen. Alla byggkommandon i nästa steg ska köras inifrån denna katalog.

3. Bygg BeeLlama.cpp med CUDA

Nu ska vi bygga BeeLlama.cpp med CUDA-stöd så att den kan använda RTX 4090 på rätt sätt.

För detta upplägg aktiverar vi CUDA, Flash Attention, inbyggda CPU-optimeringar och kvantiserade Flash Attention-kärnor. Eftersom vi använder en RTX 4090 sätter vi också CUDA-arkitekturen till 89.

cmake -B build -DGGML_CUDA=ON -DGGML_NATIVE=ON \
 -DGGML_CUDA_FA=ON -DGGML_CUDA_FA_ALL_QUANTS=ON \
 -DCMAKE_CUDA_ARCHITECTURES=89 \
 -DCMAKE_BUILD_TYPE=Release

cmake --build build -j

Bygget kan ta 20 minuter. Under kompileringen kan du se varningar relaterade till TurboQuant, TCQ eller DFlash CUDA-deklarationer. I mitt fall var detta bara varningar och stoppade inte bygget.

BeeLlama.cpp med CUDA-bygge är klart

Avsluta med att kopiera serverbinären till huvudprojektmappen så att den blir enklare att köra senare: 

cp ./build/bin/llama-server ./llama-server

4. Installera Hugging Face CLI och ladda ner modellerna

Nu behöver vi ladda ner två GGUF-filer: huvudmodellen och DFlash-utkastmodellen.

Huvudmodellen är den som producerar slutlig utdata. DFlash-utkastmodellen är mycket mindre och används bara för att förutsäga token före huvudmodellen. Huvudmodellen verifierar fortfarande de genererade tokenen, så utkastmodellen finns där för att snabba upp avkodningen snarare än att ersätta huvudmodellen.

Börja med att installera Hugging Face CLI:

pip install -U huggingface_hub

Skapa sedan en mapp för att hålla modelfilerna organiserade:

mkdir -p models

Ladda ner huvudmodellen Gemma 4 31B IT GGUF:

hf download unsloth/gemma-4-31B-it-GGUF \
gemma-4-31B-it-Q4_K_S.gguf \
--local-dir models

Ladda sedan ner DFlash-utkastmodellen:

hf download Anbeeld/gemma-4-31B-it-DFlash-GGUF \
gemma4-31b-it-dflash-Q5_K_M.gguf \
--local-dir models

DFlash-utkastmodellen listas på Hugging Face som en dflash-draft-arkitekturmodell, där Q5_K_M-filen är runt 1,09 GB, så den är mycket mindre än huvudmodellen på 31B. Det gör den praktisk att ladda tillsammans med huvudmodellen för spekulativ avkodning. 

5. Kör Gemma 4 31B utan DFlash

Innan vi aktiverar DFlash behöver vi först köra Gemma 4 31B normalt. Det ger oss en baslinje för genereringshastighet, VRAM-användning och utdatakvalitet. Senare jämför vi denna baslinje med DFlash-körningen för att se den faktiska hastighetsökningen.

Kör följande kommando inifrån beellama.cpp-mappen:

./llama-server \
 -m "models/gemma-4-31B-it-Q4_K_S.gguf"  \
--host 0.0.0.0  \
 --port 8910 \
-np 1 \
-ngl all \
-b 2048 -ub 512 \
--ctx-size 32768  \
--cache-type-k q5_0 \
--cache-type-v q4_1 \
--flash-attn on \
--jinja \
--metrics \
--log-timestamps \
--log-prefix \
--reasoning off \
--temp 0.7 \
--top-k 64 \
--top-p 0.95 \
--min-p 0.0

Detta kommando startar modellservern på port 8910. Eftersom vi exponerade port 8910 när vi skapade RunPod-poden kan vi komma åt modellen direkt från webbläsaren.

När modellen har laddats in i GPU-minnet bör du se ett meddelande som visar att servern körs på: 0.0.0.0:8910.

Inferensservern för Gemma 4 31B utan DFlash körs lokalt.

Gå nu tillbaka till RunPods instrumentpanel och klicka på portlänken som är kopplad till 8910. 

Öppna port 8910 från Runpods instrumentpanel

Detta öppnar webbgränssnittet för llama.cpp, där du kan testa modellen i ett enkelt chattliknande UI. 

Testar Gemma 4 31B utan DFlash

Vid det här laget kan du prova att ställa några längre eller mer komplexa frågor så att du kan observera genomsnittlig tokenhastighet. I min baslinjekörning utan DFlash fick jag omkring 41 token per sekund i snitt. 

Testar Gemma 4 31B utan DFlash

6. Utvärdera baslinjemodellen

Nu när baslinjemodellen körs behöver vi ett enkelt sätt att mäta dess genereringshastighet. För detta använder vi tre kodningsprompter och skickar dem till den lokala llama.cpp-servern via OpenAI-kompatibla endpointen för chattkompletteringar.

Målet är inte att skapa en perfekt benchmarksvit. Vi vill bara ha en konsekvent baslinje så att vi kan jämföra samma prompter senare med DFlash aktiverat.

Öppna en ny Jupyter Terminal-flik och skapa ett testskript:

cat > test_llm_prompts.sh <<'EOF'
#!/usr/bin/env bash

PORT="${1:-8910}"
MODEL="${2:-local-gemma}"
PREFIX="${3:-run}"

URL="http://localhost:${PORT}/v1/chat/completions"

PROMPTS=(
"Write a complete Python task store module. Include a Task dataclass, TaskStatus enum, TaskStore class, add_task, update_task, delete_task, search_tasks, filter_by_status, export_to_json, get_all_tasks, and 5 tests. Return only one complete Python file."

"Write a complete Python key-value report module. Include a KeyValueStore class, set, get, delete, exists, list_keys, filter_by_prefix, export_to_json, load_from_json, and a generate_report function that returns total keys, empty values, prefix counts, and largest value length. Include 5 tests. Return only one complete Python file."

"Write a complete Python doubly linked list module. Include a Node dataclass, DoublyLinkedList class, append, prepend, delete, find, reverse, to_list, from_list, clear, and 5 tests. Return only one complete Python file."
)

echo "Testing server: $URL"
echo "Model: $MODEL"
echo "Output prefix: $PREFIX"

for i in "${!PROMPTS[@]}"; do
  NUM=$((i+1))
  OUT="${PREFIX}_prompt_${NUM}.json"

  echo ""
  echo "Running prompt ${NUM}..."
  echo "Saving to ${OUT}"
  echo "--------------------------------"

  jq -n \
    --arg model "$MODEL" \
    --arg prompt "${PROMPTS[$i]}" \
    '{
      model: $model,
      messages: [
        {
          role: "user",
          content: $prompt
        }
      ],
      max_tokens: 1200,
      temperature: 0.7
    }' | curl -s "$URL" \
      -H "Content-Type: application/json" \
      -d @- | tee "$OUT" | jq '.timings'

  echo "Saved full result to ${OUT}"
done

echo ""
echo "Summary"
echo "--------------------------------"

for f in ${PREFIX}_prompt_*.json; do
  echo "$f"
  jq '{
    model: .model,
    prompt_tokens: .usage.prompt_tokens,
     completion_tokens: .usage.completion_tokens,
    total_tokens: .usage.total_tokens,
    generation_speed_tok_s: .timings.predicted_per_second,
    generation_time_sec: (.timings.predicted_ms / 1000),
    draft_tokens: .timings.draft_n,
    accepted_draft_tokens: .timings.draft_n_accepted
  }' "$f"
done
EOF

På macOS eller Linux, kom ihåg att göra skriptet körbart:

chmod +x test_llm_prompts.sh

Kör det sedan mot baslinjemodellen: 

./test_llm_prompts.sh 8910 local-gemma-baseline baseline

Detta skript skickar tre Python-kodgenereringsprompter till modellen och sparar varje fullständigt svar som en JSON-fil. Det skriver också ut användbar tidsinformation, inklusive completion-token, genereringshastighet, genereringstid och fält för utkasttoken. 

Hela utskriften är ganska lång, så nedan följer en kort sammanfattning av baslinjeresultaten. Det ger en snabb överblick över hur modellen presterar innan vi aktiverar DFlash.

Prompt

Completion-token

Genereringshastighet

Genereringstid

Prompt 1: Task store-modul

1124

40,66 tok/s

27,64 s

Prompt 2: Key-value report-modul

1200

40,67 tok/s

29,51 s

Prompt 3: Doubly linked list-modul

1200

40,72 tok/s

29,47 s

Över alla tre prompter höll sig baslinjemodellen mycket konsekvent runt 40,68 token per sekund. Detta ger oss en tydlig referenspunkt innan vi testar samma prompter med DFlash aktiverat.

7. Kör Gemma 4 31B med DFlash

Nu när vi har baslinjeresultaten kan vi köra samma modell igen med DFlash aktiverat.

Gå tillbaka till terminalen där baslinjeservern körs och stoppa den med Ctrl + C

Starta sedan den optimerade DFlash-servern:

./llama-server \
-m "models/gemma-4-31B-it-Q4_K_S.gguf" \
--spec-draft-model "models/gemma4-31b-it-dflash-Q5_K_M.gguf" \
--spec-type dflash \
--spec-dflash-cross-ctx 1024 \
--host 0.0.0.0  \
 --port 8910 \
-np 1 \
--kv-unified \
-ngl all \
--spec-draft-ngl all \
-b 2048 -ub 512 \
--ctx-size 32768 \
--flash-attn on \
--cache-ram 0 \
--jinja \
--no-mmap \
--mlock \
--no-host \
--metrics \
--log-timestamps \
--log-prefix \
--reasoning off \
--temp 0.7 \
--top-k 64 \
--top-p 0.95 \
--min-p 0.0

Detta kommando laddar samma huvudmodell Gemma 4 31B, men nu laddas också DFlash-utkastmodellen via --spec-draft-model.

De viktiga DFlash-relaterade flaggorna:

Flagga

Syfte

--spec-draft-model

Laddar DFlash-utkastmodellen

--spec-type dflash

Aktiverar DFlash spekulativ avkodning

--spec-dflash-cross-ctx 1024

Anger cross-context-fönster som används av DFlash

--spec-draft-ngl all

Offloadar utkastmodellens lager till GPU:n

--kv-unified

Använder enhetlig KV-hantering för huvud- och utkastmodell

Det kan ta lite längre tid att starta den här gången eftersom både huvudmodellen och DFlash-utkastmodellen måste laddas in i minnet.

Laddar Gemma 4 31B med DFlash i GPU-minnet

När servern är helt laddad bör du återigen se inferensservern köra på: 0.0.0.0:8910.

Kör Gemma 4 31B med DFlash lokalt

8. Utvärdera DFlash-modellen

Gå nu tillbaka till Jupyter-terminalen där vi skapade benchmarkskriptet. Vi kan köra samma skript igen, men den här gången mot servern med DFlash aktiverat. 

./test_llm_prompts.sh 8910 local-gemma-dflash dflash

Detta använder samma tre kodningsprompter från baslinjetestet, vilket gör jämförelsen rättvis. Den enda stora skillnaden är att servern nu körs med DFlash-utkastmodellen aktiverad.

Jämföra inferenshastighet

Hela utskriften är lång, så här är en kort sammanfattning av baslinje- och DFlash-resultat:

Prompt

Baslinjehastighet

DFlash-hastighet

Hastighetsökning

Baslinjetid

DFlash-tid

Tidsbesparing

Task store-modul

40,66 tok/s

130,96 tok/s

3,22x

27,64 s

8,23 s

19,41 s

Key-value report-modul

40,67 tok/s

145,68 tok/s

3,58x

29,51 s

8,24 s

21,27 s

Doubly linked list-modul

40,72 tok/s

153,04 tok/s

3,76x

29,47 s

7,84 s

21,63 s

Över dessa tre kodningsuppgifter ökade DFlash genereringshastigheten från cirka 40 tok/s till 130–153 tok/s. Det ger ungefär en 3,2x till 3,8x hastighetsökning, samtidigt som genereringstiden minskade från nästan 30 sekunder till runt 8 sekunder per prompt. 

Du kan också öppna samma länk för port 8910 från RunPods instrumentpanel och testa modellen via webbgränssnittet.

Jämföra utdatakvalitet

Eftersom vi närmar oss en fyrfaldig hastighetsökning på kodningsprompter är nästa sak att kontrollera utdatakvaliteten. För det testade jag modellen på några olika uppgifter.

Först bad jag den att generera en enkel portföljsajt för ”Abid”. För en lokal 31B-modell som körs på en enda RTX 4090 var resultatet imponerande. Den producerade en ren struktur med användbar HTML och styling. 

Webbplats genererad av Gemma 4

Därefter bad jag den att generera ett diagram för en komplett MLOps-pipeline. Modellen returnerade Mermaid-kod med etiketter, färger och ett komplett arbetsflöde. Jag testade koden, och den fungerade direkt. 

Testar Gemma 4 31B med DFlash för diagramgenerering.

Sedan bad jag den skriva ett blogginlägg om Mixture of Experts i LLM:er. Kvaliteten var fortfarande stark, men hastigheten sjönk till runt 95 tok/s. Det är fortfarande mycket snabbare än baslinjen, men långsammare än kodningsprompterna. 

Testar Gemma 4 31B med DFlash för bloggs skrivande.

Detta är rimligt eftersom DFlash fungerar bäst när utdata är mer förutsägbara. Koduppgifter följer ofta tydliga mönster, så utkastmodellen kan gissa fler token korrekt. Kreativt skrivande eller forskningsliknande prompter är mindre förutsägbara, så modellen kan acceptera färre utkasttoken och hastighetsökningen kan bli lägre. 

Avslutande tankar

Efter att ha testat detta upplägg tycker jag att spekulativ avkodning i kombination med bättre KV-cachehantering är den verkliga vinnaren för lokal LLM-inferens.

Den största fördelen är inte bara hastighetsökningen på papper. Det är vad den hastigheten möjliggör. När en 31B-modell kan generera kod i 130–150 token per sekund på en enda RTX 4090 börjar det kännas praktiskt som en lokal kodningsagent. Du kan använda den för att bygga projekt från grunden, koppla den till MCP-servrar, köra bash-verktyg, använda egna färdigheter och skapa ett arbetsflöde som känns mycket närmare premium-kodningsagenter.

För den som redan har en RTX 3090 eller 4090 är detta ännu mer spännande. I stället för att betala för varje kodassistent eller förlita sig helt på molnverktyg kan du köra en kraftfull lokal lösning som är snabb, privat och flexibel. Det ersätter kanske inte alla hostade verktyg för alla, men för lokala AI-entusiaster, utvecklare och byggare börjar det bli väldigt nära.

Jag tror också att detta bara är början. Många testar redan liknande upplägg med nyare modeller som Qwen3.6-27B och rapporterar ännu bättre kvalitet. I takt med att modellerna förbättras, utkastmodeller blir bättre och inferensmotorer som BeeLlama.cpp blir mer optimerade, kommer lokal AI bara att bli mer användbar.

Det bästa är communityn runt detta. Många av dessa förbättringar kommer från lokala AI-entusiaster som experimenterar, benchmarkar, förbättrar verktygen och delar sina resultat öppet. Det gör det enklare för resten av oss att replikera upplägget och uppleva samma prestandavinster.

Ämnen