16 GB Ekran Kartında 27B Model: Qwen3.8'i 3 Kat Hızlandırmak
Qwen3.8-27B'yi RTX 4060 Ti 16GB üzerinde yerel olarak çalıştırmaya karar verdiğimde beklentim mütevazıydı: "çalışsın yeter". Ölçe ölçe ilerleyince ortaya çıkan şey, doğru ayarların modeli seçmekten daha belirleyici olduğuydu. Aşağıdakilerin hepsi kendi makinemde ölçülmüş gerçek rakamlar — kopyalanmış tablolar değil.
Donanım: RTX 4060 Ti 16 GB · Ryzen 7 5700X3D (8c/16t) · 128 GB RAM · Windows 11 Yazılım: llama.cpp b10826 (CUDA 13.3)
Neden FP8 değil?
İlk refleksim Qwen'in resmî FP8 sürümünü indirmekti. Sığmadı: 30.9 GB. Dahası, Ada mimarisinde FP8 tensor çekirdeği olmasına rağmen llama.cpp bu yolu kullanmıyor — yani sığsa bile bir kazanç sağlamayacaktı.
| Sürüm | Boyut | Durum |
|---|---|---|
| BF16 | 56 GB | sığmaz |
| FP8 | 30.9 GB | sığmaz |
| UD-IQ4_XS | 14.25 GB | sığar, en iyi 4-bit |
| UD-Q3_K_XL | 13.15 GB | sığar, en hızlı |
Unsloth'un Dynamic v3.0 kuantizasyonlarını seçtim: katman başına bit genişliği imatrix ile ayarlanıyor, aynı dosya boyutunda düz kuantlara göre belirgin şekilde daha doğru.
Üç adayı 8K bağlam derinliğinde ölçtüm:
| Kuant | Dosya | Üretim |
|---|---|---|
| UD-Q3_K_XL | 13.15 GB | 18.33 tok/s |
| UD-IQ4_XS | 14.25 GB | 17.08 tok/s |
| UD-Q4_K_S | 15.36 GB | 15.95 tok/s |
Hız neredeyse tam olarak dosya boyutuyla ters orantılı. Bu tesadüf değil — birazdan nedenine geleceğim.
Mimari: Neden bu model 16 GB'a bu kadar iyi oturuyor
Qwen3.8-27B klasik bir transformer değil. Katman düzeni şöyle:
16 × ( 3 × Gated DeltaNet → 1 × Gated Attention )
64 katmanın 48'i lineer dikkat (Gated DeltaNet), sadece 16'sı tam dikkat. Lineer dikkat katmanları bağlam uzunluğundan bağımsız, sabit boyutlu bir özyinelemeli durum tutuyor — toplam 144 MiB. KV önbelleği yalnızca 16 katmanda büyüyor.
Pratik sonucu şu: bağlam derinleştikçe hız neredeyse hiç düşmüyor.
| Bağlam derinliği | 0 | 4K | 16K | 30K |
|---|---|---|---|---|
| Üretim (tok/s) | 17.60 | 17.27 | 16.38 | 15.47 |
30 bin token derinlikte kayıp sadece %12. Aynı boyutta klasik bir transformer'da bu çok daha sert olurdu.
En büyük kazanç: spekülatif kod çözme
Toplam hızlanmanın neredeyse tamamı buradan geldi. Üç yöntem denedim, hepsi kayıpsız — üretilen metin spekülasyonsuz çalıştırmayla birebir aynı (greedy'de), örneklemede ise aynı dağılım.
MTP: dosyanın içinde saklı
Qwen3.8'in GGUF'u çok-token tahmin (MTP) başlığını blk.64 olarak zaten içeriyor.
Repoda ayrı bir mtp-*.gguf dosyası da var ve ben günümü onu yüklemeye çalışarak
harcadım — yüklenmiyor, çünkü gerekli değil:
# YANLIŞ
--spec-type draft-mtp -md mtp-Qwen3.8-27B-Q4_0.gguf # "invalid vector subscript"
# DOĞRU
--spec-type draft-mtp --spec-draft-n-max 4 # -md yok
n-max taraması (IQ4_XS, 16K bağlam):
n-max |
kapalı | 3 | 4 | 5 | 6 | 8 |
|---|---|---|---|---|---|---|
| tok/s | 17.22 | 34.46 | 36.75 | 36.44 | 35.93 | 32.07 |
2.13× hızlanma, sıfır ek VRAM.
DFlash2: harici taslak model, %25 daha iyi
z-lab/Qwen3.8-27B-DFlash2-GGUF — blok-difüzyon taslak modeli. 8 token'lık bir bloğu
tek geçişte tahmin ediyor, her pozisyonda en iyi adayları tutuyor. Sadece 1.14 GB.
--spec-draft-n-max |
2 | 3 | 4 | 5 | 7 |
|---|---|---|---|---|---|
| tok/s | 35.65 | 39.85 | 51.26 | 45.03 | 33.75 |
| kabul uzunluğu | 2.49 | 2.89 | 3.74 | 3.49 | 3.36 |
n-max 4'te belirgin bir tepe var. Model kartı Q8_0 ve BF16 taslak sürümlerini de
sunuyor ama Q4_K_M hem en küçüğü hem de en yüksek kabul oranlısı — diğerlerini
indirmeye gerek yok.
ngram: zahmete değmez
--spec-type ngram-simple: 17.75 tok/s (+%3), kabul oranı %25. Taslak modeli olmayan
mimarilerde işe yarayabilir, burada anlamsız.
Özet
| Yöntem | Q3_K_XL | IQ4_XS |
|---|---|---|
| Yok | ~18.3 | 17.2 |
| ngram | — | 17.8 |
| MTP n4 | 40.9 | 36.8 |
| DFlash2 n4 | 51.3 | 39.5 |
Bir uyarı: IQ4_XS + DFlash2 kombinasyonu 16 GB'a sığmıyor — 8K bağlamda bile 15.9 GB. O kuantla MTP'de kalmak gerekiyor.
Beni en çok yakan üç tuzak
Bunlar hiçbir kılavuzda yazmıyordu, ölçerek buldum.
1. KV önbelleğinde K ve V aynı tipte olmalı
-ctk f16 -ctv q8_0 # 17 → 6.1 tok/s
Karışık tip verince flash-attention sessizce devre dışı kalıyor ve hız 3 kat düşüyor. Hiçbir uyarı yok, sadece yavaşlıyor.
2. q5_1 KV'nin flash-attention çekirdeği yok
-ctk q5_1 -ctv q5_1 # 37 → 10.0 tok/s
Sadece f16, q8_0 ve q4_0 kullanılabilir. Aradaki değerler tuzak.
3. VRAM taşmasının imzası: prompt işleme çöküşü
-ub 2048 denediğimde prompt işleme 855 → 255 tok/s'ye düştü. Sebep VRAM taşıp
paylaşılan belleğe düşmek. Bunu şöyle teşhis ediyorum: üretim hızı normal görünürken
prompt işleme aniden 3-4 kat düşerse VRAM taşmıştır. -ub 512 (varsayılan) en iyisi;
-ub 256 ise VRAM kazandırmadan sadece yavaşlatıyor (38.3 → 35.6).
Bağlam ne kadar uzayabilir?
Q3_K_XL + DFlash2 n4 + q4_0 KV ile:
| Bağlam | Üretim | VRAM | Durum |
|---|---|---|---|
| 16K | 51.26 | 15016 MiB | |
| 32K | 51.29 | 15384 MiB | önerilen |
| 48K | 51.22 | 15752 MiB | çalışıyor, dar |
| 64K | 50.4 | ~16.1 GB | kararsız — 5 koşudan birinde sunucu çöktü |
64K gerekiyorsa DFlash2'yi bırakıp MTP'ye dönmek gerekiyor: 38.3 tok/s ama 14.9 GB ile rahat bir pay kalıyor.
İşe yaramayan iki fikir
Blog yazılarında genelde sadece başarılar anlatılır. İki büyük denemem başarısız oldu ve ikisi de öğreticiydi.
"Modelin bir kısmını RAM'de tutalım"
128 GB RAM'im var, neden kullanmayayım? Çünkü zarar veriyor. Yoğun (dense) bir modelde her token tüm ağırlıkları okuyor; CPU'ya taşınan her tensör 288 GB/s yerine ~50 GB/s'ye düşüyor. Ölçtüm: Q4_K_S'te 15.95 → 12.62 tok/s.
128 GB RAM'in buradaki en iyi kullanımı model dosyasını işletim sistemi disk önbelleğinde tutmak — sunucu bu sayede 4 saniyede açılıyor.
(Not: bu sonuç yoğun modellere özgü. MoE modellerde tam tersi geçerli — orada token başına sadece aktif uzmanlar okunduğu için RAM'e taşımak gerçekten işe yarıyor. Ama o ayrı bir yazının konusu.)
Kendi çalışma zamanımı yazmak
llama.cpp'yi es geçip Qwen3.8 mimarisini sıfırdan yazdım: kendi GGUF okuyucum, kendi 4-bit formatım, Triton ile yazılmış özel GPU çekirdekleri. Doğru çalıştı — llama.cpp ile aynı ağırlıklarda 5 sondanın 5'inde birebir aynı ilk token'ı üretti.
Ama 8.82 tok/s'de kaldı, llama.cpp'nin 18.3'üne karşı.
Sebebini ölçünce proje bitti: kartın gerçek okuma bant genişliğini ölçtüm, 277.5 GB/s (teorik 288'in %96'sı). llama.cpp 13.13 GB'lık modeli 18.33 tok/s'de okuyor — yani efektif 240 GB/s, tavanın %87'si. Çekirdek yazarak kazanılacak alan en fazla %13.
Kendi GEMV çekirdeğim 236-284 GB/s yapıyordu, yani çekirdek iyiydi; kaybettiğim yer Python orkestrasyonuydu (token başına ~1300 çekirdek başlatması = 41 ms).
Çıkarılan ders: kod çözme tamamen bellek bandı sınırlı. Hız kazanmak istiyorsan çekirdek değil, token başına okunan bayt sayısını düşürmelisin. Bu yüzden "hız ≈ 1/dosya boyutu" ilişkisi bu kadar temiz çıkıyor.
Nihai yapılandırma
llama-server ^
--model Qwen3.8-27B-UD-Q3_K_XL.gguf ^
--spec-draft-model Qwen3.8-27B-DFlash2-Q4_K_M.gguf ^
--spec-type draft-dflash --spec-draft-n-max 4 --spec-draft-ngl 99 ^
--n-gpu-layers 99 ^
--ctx-size 32768 ^
--parallel 1 ^
--flash-attn on ^
--cache-type-k q4_0 --cache-type-v q4_0 ^
--threads 8 ^
--load-mode none ^
--temp 1.0 --top-p 0.95 --top-k 20 --min-p 0.0 ^
--jinja
51.3 tok/s üretim, 801 tok/s prompt işleme, 15.4 GB VRAM.
Bonus: VS Code Copilot'a bağlarken
Yerel modeli VS Code'a özel uç nokta olarak tanıttım ve şu hatayı almaya başladım:
request (24713 tokens) exceeds the available context size (16384 tokens)
Sorun sunucuda değil, istemciye bildirdiğim limitte. llama.cpp'de --ctx-size
girdi + çıktı toplamıdır. Copilot'a maxInputTokens: 128000 yazmıştım; o da
"rahatım" diye konuşmayı hiç özetlemeden büyüttü.
Doğrusu: maxInputTokens + maxOutputTokens ≈ --ctx-size × 0.95.
--ctx-size |
maxInputTokens |
maxOutputTokens |
|---|---|---|
| 32.768 | 22000 | 8000 |
| 49.152 | 34000 | 12000 |
| 65.536 | 46000 | 16000 |
%5'lik payı bırakmak önemli: Copilot kendi tokenizer'ıyla sayıyor, Qwen'inki aynı metni (özellikle Türkçe ve kod için) biraz daha fazla token'a bölüyor.
Bir de düşünme (thinking) meselesi var. Qwen3.8 varsayılan olarak düşünüyor ve Copilot
chat_template_kwargs alanını hiç göndermiyor — yani her cevap 100-200 token'ı
düşünmeye harcıyor. Bunu istemci tarafında çözemezsin; ya sunucuya varsayılan olarak
enjekte etmen ya da araya küçük bir vekil koyman gerekiyor.
Özet
| Adım | Kazanç |
|---|---|
| Başlangıç (spekülasyonsuz) | 17.2 tok/s |
MTP n-max 4 |
36.8 tok/s (2.13×) |
DFlash2 n-max 4 + Q3_K_XL |
51.3 tok/s (2.98×) |
Üç cümlelik özet:
- Spekülatif kod çözme her şeyden önemli. Tek bir bayrak hızı 3 katına çıkarıyor ve kalite kaybı yok.
- KV önbelleği ayarları sessizce sabote ediyor. K ve V aynı tipte olmalı ve sadece f16/q8_0/q4_0 kullanılabilir.
- Kod çözme bellek bandı sınırlı. Daha hızlı istiyorsan daha az bayt oku; daha iyi çekirdek yazmak seni kurtarmaz.
Bu yazıdaki tüm rakamlar tek bir makinede, llama.cpp b10826 ile, 4516 token'lık sabit bir istem ve greedy örnekleme kullanılarak, yapılandırma başına 3-5 tekrarın medyanı olarak ölçüldü.