← Back
𝕏 f in
Yapay Zeka Eylül,18 · 8 dk okuma

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:

  1. Spekülatif kod çözme her şeyden önemli. Tek bir bayrak hızı 3 katına çıkarıyor ve kalite kaybı yok.
  2. KV önbelleği ayarları sessizce sabote ediyor. K ve V aynı tipte olmalı ve sadece f16/q8_0/q4_0 kullanılabilir.
  3. 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ü.

Yazan: Osman Özaydın Tüm yazılara dön