Qwen3.8 27B (FP8, vLLM)
27B'lik bir modeli tek MIG dilimine FP8 ile sığdırın, vLLM ile sunun ve gerçek gecikme/verim sayılarını ölçün.
27 milyar parametreli bir modeli tek bir MIG dilimine sığdırıp OpenAI uyumlu bir uçtan sunmak,
sonra da hızını tahmin etmek yerine ölçmek. Bu sayfadaki bütün sayılar bir H200 NVL kartının
4g.71gb dilimi üzerinde, vLLM'in kendi ölçüm aracıyla alınmıştır.
Neden FP8
Aynı model, üç ayrı biçimde:
| Biçim | Ağırlıklar | 71 GB dilimde | Kalite |
|---|---|---|---|
| bf16 | ~54 GB | sığar, ama KV cache'e neredeyse yer kalmaz | referans |
| FP8 | ~30 GB | rahat — 32,5 GiB KV cache kaldı | bf16'ya çok yakın |
| GGUF Q4 | ~16 GB | en küçük | kayıp en fazla |
H200 (Hopper) FP8'i donanımda destekler, yani küçültme hızdan çalmaz. Resmi
Qwen/Qwen3.8-27B-FP8 deposu ince taneli (blok 128) FP8 kullanır.
MIG'de --tensor-parallel-size 1 olmak zorundadır. MIG, dilimler arasında P2P/NVLink/NCCL'i
kapatır; tek bir CUDA süreci yalnızca bir dilim kullanabilir, model iki dilime yayılamaz.
FP8'in asıl değeri burada: bf16 tek dilime sıkışırken FP8 rahatça sığıyor.
Pod'u aç
Pod Dağıtımı sayfasından:
- İmaj:
vllm/vllm-openai:latest - GPU: MIG, profil
4g.71gb, adet 1 - Kaynak: 6 vCPU / 28 GB RAM
- Port: 8000 (dış erişim açık)
- Kalıcı disk: 80 GB,
/data— ağırlıklar buraya insin - Ortam değişkeni:
HF_HOME=/data/hf - İnternet erişimi: açık
Komut:
vllm serve Qwen/Qwen3.8-27B-FP8 \
--tensor-parallel-size 1 \
--kv-cache-dtype fp8 \
--enable-prefix-caching \
--prefix-match-unit 16 \
--reasoning-parser qwen3 \
--host 0.0.0.0 --port 8000 \
--max-model-len 65536HF_HOME'u kalıcı diske verin. Ağırlıklar 30 GB ve indirmesi ~45 dakika sürüyor. Konteyner
yeniden başlarsa yazılabilir katman imajdan yeniden kurulur; önbellek /data üzerinde değilse
her şey baştan iner.
Dilimi doğrula
nvidia-smi -LGPU 0: NVIDIA H200 NVL (UUID: GPU-ffa8158e-...)
MIG 4g.71gb Device 0: (UUID: MIG-a1b2be34-...)Açılışı izle
vLLM önce çekirdeklerini seçer:
Selected FlashInferFp8DeepGEMMDynamicBlockScaledKernel for Fp8LinearMethod
DeepGEMM PDL enabled · DeepGEMM E8M0 enabled
Using FlashAttention version 3Sonra ağırlıkları yükler ve en önemli satırı yazar:
Available KV cache memory: 32.5 GiB
GPU KV cache size: 988,865 tokens
Maximum concurrency for 65,536 tokens per request: 15.09xBu üç satır size dilimin gerçekte ne verdiğini söyler: ağırlıklardan sonra 32,5 GiB KV cache kalmış, bu da 65K'lık bağlamda 15 eşzamanlı istek demek. Tahmin etmeye gerek yok, vLLM hesabı kendisi yapıyor.
İlk açılış: indirme 43 dk 45 sn, ağırlıkların yüklenmesi 5 dk 24 sn, toplam ~53 dakika.
İkinci açılışta indirme yok — ağırlıklar /data'da.
İlk istek
OpenAI uyumlu, yani mevcut istemcileriniz çalışır:
curl -s localhost:8000/v1/chat/completions -H 'Content-Type: application/json' -d '{
"model": "Qwen/Qwen3.8-27B-FP8",
"messages": [{"role":"user","content":"Explain what MIG (Multi-Instance GPU) is, in one paragraph."}]
}'Modelin gerçek cevabı:
MIG (Multi-Instance GPU) is an NVIDIA technology, available on certain data-center GPUs such as A100 and H100, that lets a single physical GPU be partitioned into multiple isolated GPU instances, each with its own dedicated allocation of resources such as memory, memory bandwidth, compute cores, caches, and I/O. (…) making it useful for multi-tenant data centers, cloud environments, and mixed AI/ML workloads.
--reasoning-parser qwen3 verdiğimiz için model düşünme bloklarını ayrı bir reasoning_content
alanına koyar. Yukarıdaki cevapta o alan boştu — model doğrudan yanıtladı.
Ham /v1/completions (sohbet değil) ucunu kullanırsanız sohbet şablonu uygulanmaz: model kendi
cevabını bitirip istemi tekrar üretebilir. Akıl yürüten modellerde bu beklenen davranıştır;
sohbet ucunu kullanın.
Ölç — tahmin etme
vLLM kendi kıyaslama aracıyla gelir, ayrıca bir şey kurmanız gerekmez:
vllm bench serve --model Qwen/Qwen3.8-27B-FP8 \
--base-url http://localhost:8000 --dataset-name random \
--num-prompts 40 --random-input-len 512 --random-output-len 256 \
--percentile-metrics ttft,tpot,itl,e2el --metric-percentiles 25,50,75,99--max-concurrency ile aynı ölçümü farklı yük altında tekrarlayın. 40 istek, 512 girdi /
256 çıktı token, hepsi başarılı:
| mc=1 | mc=4 | mc=16 | sınırsız | |
|---|---|---|---|---|
| İstek/sn | 0,24 | 0,85 | 2,34 | 4,61 |
| Çıktı tok/sn | 60,3 | 218,5 | 600,3 | 1179,7 |
| Toplam tok/sn | 180,8 | 655,6 | 1800,8 | 3539,1 |
| TTFT ortalama | 65,6 ms | 226,5 ms | 529,3 ms | 1355,3 ms |
| TTFT P99 | 67,5 ms | 257,2 ms | 856,5 ms | 2128,7 ms |
| TPOT ortalama | 16,4 ms | 17,5 ms | 20,8 ms | 28,4 ms |
| TPOT P99 | 16,4 ms | 17,7 ms | 22,4 ms | 31,9 ms |
Tek kullanıcı, akışlı istek: TTFT 35–47 ms, çözme 60,5–61 tok/sn.
Sayıları okumak
Üç ayrı şey ölçülüyor ve karıştırılmamalı:
- TTFT (time to first token) — "cevap vermeye başladı" hissi. İnsanın algıladığı gecikme.
- TPOT (time per output token) — sonraki tokenların akış temposu. Metnin okunma hızı.
- Çıktı tok/sn — sistemin toplam üretimi. Kaç kişiye hizmet verebildiğiniz.
Tablodaki asıl bulgu: eşzamanlılık 1'den 16'ya çıkınca çıktı 10 kat artıyor (60 → 600 tok/sn) ama token temposu yalnızca 16,4 → 20,8 ms bozuluyor. Yani dilimi paylaştırmak neredeyse bedava.
Bedel TTFT'de ödeniyor: 65 ms'den 529 ms'ye çıkıyor. Sohbet arayüzü sunuyorsanız eşzamanlılığı sınırlamak, toplu işlem yapıyorsanız serbest bırakmak doğru olanı.
İşiniz bitince pod'u silin
GPU pod'ları saatlik ücretlendirilir ve dilim siz silene kadar başkasına açılmaz. Ağırlıklar kalıcı diskte kalır; volume'u saklarsanız bir dahaki açılışta indirme olmaz.
"Bu dilim kaç kişiye yeter?"
Bu soru tek sayıyla cevaplanmaz, çünkü aslında üç ayrı soru ve üçü birbirinin aleyhine çalışır. Ölçülen değerlerle:
| Eşzamanlı istek | 1 | 4 | 16 | sınırsız |
|---|---|---|---|---|
| İlk kelime (TTFT) | 66 ms | 227 ms | 529 ms | 1.355 ms |
| Token temposu (TPOT) | 16,4 ms | 17,5 ms | 20,8 ms | 28,4 ms |
| Tam cevap (E2EL, 256 token) | 4,25 sn | 4,69 sn | 5,84 sn | 8,59 sn |
| Sistem çıktısı | 60 tok/sn | 219 tok/sn | 600 tok/sn | 1.180 tok/sn |
| İstek/sn | 0,24 | 0,85 | 2,34 | 4,61 |
Tek cümlelik cevap şöyle kurulur:
Tek
4g.71gbdiliminde bir kullanıcı ilk kelimeyi 66 ms'de görür ve 256 tokenlık cevabın tamamını 4,2 saniyede alır. Aynı dilim 16 eşzamanlı üretimi kaldırır; o zaman tam cevap 5,8 saniyeye, ilk kelime 0,5 saniyeye çıkar ve sistem toplam 600 token/saniye üretir.
Tablodaki asıl ders: tam cevap süresi 1'den 16 kullanıcıya çıkarken neredeyse sabit kalıyor (4,25 → 5,84 sn) ama sistem çıktısı 10 kat artıyor. Paylaştırmak neredeyse bedava. Bozulan tek şey TTFT — sekiz kat. Kararınızı bu belirler:
- Sohbet arayüzü: algıyı TTFT belirler → eşzamanlılığı sınırlayın.
mc=4iyi bir denge: 227 ms'de ilk kelime, yine de 219 tok/sn. - Toplu iş (özetleme, sınıflandırma, veri işleme): TTFT önemsiz → sınırı kaldırın, 1.180 tok/sn.
Bu sayılar 512 token girdi / 256 token çıktı içindir. İkisi de tabloyu değiştirir:
- Girdi uzunluğu TTFT'yi doğrudan büyütür — ilk token üretilmeden önce istemin tamamı işlenir (prefill). Ölçtük: TTFT ≈ girdi token sayısı ÷ 7.700, yani 32K'lık bir istemde 66 ms değil ~4,25 saniye. Ayrıntı için aşağıdaki “Uzun istem” bölümü. Tek sayı vermenin en tehlikeli yeri burasıdır.
- Çıktı uzunluğu tam cevap süresini belirler — 1.000 tokenlık bir cevap tek kullanıcıda yaklaşık 16 saniye (66 ms + 999 × 16,4 ms).
- "Kaç kullanıcı" ile "kaç eşzamanlı istek" aynı şey değildir. Sohbette kullanıcı ortalama 30 saniyede bir mesaj yazıyorsa, 2,34 istek/sn kabaca 70 eşzamanlı sohbet eder — bu bir hesaptır, ölçüm değil; kendi kullanım deseninizle doğrulayın.
İki dilim: ölçek doğrusal mı?
Bir dilim yetmiyorsa modeli iki dilime bölemezsiniz (MIG'de tensor-parallel-size 1 zorunlu),
ama aynı modelin iki bağımsız kopyasını iki dilimde çalıştırıp önlerine bir yük dengeleyici
koyabilirsiniz. Soru şu: iki kopya gerçekten iki katı iş yapar mı, yoksa aynı kartı paylaştıkları
için birbirlerini yavaşlatırlar mı?
Ölçtük. Aynı kartın iki 4g.71gb dilimi, birbirinin aynı iki kopya (4 CPU / 15 GiB), aynı yük
(--max-concurrency 16, 40 istek):
| Çıktı tok/sn | |
|---|---|
| Tek kopya (r1, diğeri boşta) | 597,4 |
| Paralel — r1 | 600,0 |
| Paralel — r2 | 597,1 |
| Toplam | 1.197,1 |
Ölçek katsayısı 2,0039 — doğrusal. Gecikmede de kayıp yok: paralel yükte TTFT %0,04 daha düşük, TPOT değişmemiş, uçtan uca süre %0,01 farklı. İki dilim birbirini pratikte hiç etkilemiyor.
İki koşunun %93'ü gerçekten çakıştı (başlangıçlar arasında 0,11 sn fark), 40/40 istek başarılı, iki pod da yeniden başlamadı.
Kopya eklemek ucuz. İkinci kopya, birincinin indirdiği modeli aynı kalıcı diskten okudu — üstelik biraz daha hızlı (306 sn ve 311 sn, birincinin 324 sn'sine karşı). Yani ölçeklerken modeli tekrar indirmeniz gerekmiyor: aynı diski ikinci pod'a bağlamanız yeterli.
Bir de: ikinci kopya ağırlıklarını yüklerken birincinin performansı bozulmadı (yeniden ölçüldü, fark %0,5). Üretim trafiği alırken yeni kopya eklemek güvenli.
Host belleğini yeterli verin. Ağırlıklar VRAM'e gider ama vLLM'in yükleme sırasında host
belleğine de ihtiyacı vardır. 4 GiB'lık bir konteyner her denemede aynı yerde
(Encoder cache will be initialized... satırından hemen sonra) exitCode 137 ile öldü ve
sonsuz yeniden başlama döngüsüne girdi. 15 GiB ile sorunsuz çalıştı.
Teşhis ipucu: pod "running" görünüp hizmet vermiyorsa restart_count'a bakın. Yükleme
çubuğunun ilerlemesi aldatıcıdır — her yeniden başlamada baştan başlar.
Tek adres: iki kopyanın önüne yük dengeleyici
İki kopya iki ayrı adres demek; müşteriye tek uç vermek istiyorsanız araya bir yük dengeleyici koyun. Kiracı ağınızda pod'lar birbirini görür (izolasyon yalnızca interneti kapatır, küme içi trafiği değil), yani üçüncü bir pod olarak HAProxy yeterlidir. Her pod'un kendi IP'si olduğu için ikisinin de 8000 dinlemesi sorun değildir.
frontend llm
bind *:8080
default_backend vllm_pool
backend vllm_pool
balance leastconn
option httpchk GET /health
http-check expect status 200
default-server check inter 5s rise 2 fall 3 maxconn 64
server r1 qwen-r1-svc:8000
server r2 qwen-r2-svc:8000Varsayılan ayarlarla kurarsanız üçü de yanlış olur:
balance leastconn, round-robin değil. LLM istekleri çok eşitsizdir — 32K'lık bir istem 512'liğin ~64 katı prefill demektir. Round-robin uzun isteği alan kopyaya beslemeye devam eder.timeout server 600s. HAProxy varsayılanı 50 saniyedir; uzun bağlamda tek bir istek 76 saniye sürebiliyor ve üretim ortasında kesilir.option http-no-delay. Token akışı tamponda beklerse TTFT olduğundan kötü görünür.
Ölçüm (aynı kıyaslama, 512 girdi / 256 çıktı):
| Doğrudan tek kopya | LB üzerinden, aynı yük | LB üzerinden, çift yük | |
|---|---|---|---|
| Çıktı tok/sn | 598,7 | 685,0 | 1.248,6 |
| TTFT ortalama | 543,5 ms | 358,6 ms | 512,0 ms |
| TTFT P99 | 873,8 ms | 471,1 ms | 875,3 ms |
| TPOT | 20,8 ms | 18,4 ms | 21,4 ms |
Yük dengeleyicinin bedeli ölçülemedi, çünkü negatif çıktı: aynı yükte LB yolu her metrikte daha hızlı. Trafiğin yarısı ikinci kopyaya düşünce kuyruk azalıyor ve bu kazanç proxy'nin kendi gecikmesini gölgeliyor. Dolayısıyla bu ölçüm hop'un maliyetini ölçmez, üst sınırını koyar: 185 ms'den ucuz.
Çift yükte havuz iki kopyanın toplamını veriyor: 1.248,6 tok/sn, eşleştirilmiş doğrudan tabana
(659,1) göre 1,894× — mükemmel 2×'in %94,7'si. leastconn dağılımı da dengeli: 200 isteklik
koşuda 99'a 106.
Ölçerken tuzak: ilk denemede 40 istek mc=32 ile yalnızca 10,7 saniye sürdü ve 960 tok/sn
çıktı — havuz sınırı sanılabilirdi. Değildi: o kadar kısa bir koşuda rampa ve boşalma baskın olur
(anlık tepe 1.606 tok/sn ölçüldü). 200 istekle kararlı duruma çıkınca gerçek sayı göründü.
Kısa kıyaslamalar yanıltır.
Yük dengeleyici yetmiyor, kullanıcı başına anahtar, token tavanı ve harcama kaydı da gerekiyorsa aradaki katman HAProxy değil bir LLM kapısıdır. Ölçülmüş kurulumu ve bedeli: LiteLLM: çok kullanıcılı LLM kapısı.
Bundan sonrası: önbellek-farkında yönlendirme
Düz bir yük dengeleyici LLM'in durumlu olduğunu bilmez. Kullanıcılarınız ortak bir sistem
istemi paylaşıyorsa, leastconn onları farklı kopyalara dağıtır ve her kopya aynı ön eki sıfırdan
hesaplar. Bu sayfadaki kurala göre 32K'lık ortak bir istem her seferinde ~4,25 saniye prefill
demektir; önbellekten gelse neredeyse sıfır. (Tek kopya içinde ön ek önbelleğini açmayı
aşağıdaki bölüm anlatıyor — ama o yalnızca aynı kopyaya düşen istekleri kurtarır.)
Çözüm, aynı ön eke sahip istekleri aynı kopyaya yönlendiren önbellek-farkında yönlendirmedir
(vLLM Router'ın tutarlı hash'lemesi, Ray Serve'in PrefixCacheAffinityRouter'ı, llm-d'nin
inference scheduler'ı bunu yapar). vLLM'in kendi belgesi de sınırı açıkça çiziyor: iki basit
kopya için standart bir yük dengeleyici yeterlidir; router'ın değeri çok pod'lu mimaride ve
durumlu sohbet desenlerinde ortaya çıkar.
Bir sonraki adım olan prefill/decode ayrımı ise MIG'de yapılamaz — dilimler arası P2P kapalı olduğu için ayrı GPU havuzları gerekir.
Uzun istem: TTFT'nin fiyatı
Yukarıdaki bütün sayılar 512 token girdi içindir. İstemi büyütünce ne olduğunu tahmin etmeye gerek yok, ölçtük — aynı araç, aynı çıktı uzunluğu, tek değişken girdi:
| 512 girdi | 32.768 girdi | |
|---|---|---|
| TTFT (tek kullanıcı) | 65,59 ms | 4.250,39 ms |
| Prefill hızı | 7.806 tok/sn | 7.709 tok/sn |
Girdi 64 kat arttı, TTFT 64,8 kat arttı — prefill hızı ise ikisinde de aynı (fark %1,3). Yani prefill sabit hızlı bir borudur ve şu kural çıkar:
TTFT ≈ girdi token sayısı ÷ 7.700
32K'lık bir istemde ilk kelimeden önce yaklaşık 4,25 saniye sessizlik olur. 8K'da ~1 saniye, 64K'da ~8,5 saniye.
Uzun istemin bedeli peşin ödenir. Tek kullanıcıda token temposu neredeyse değişmiyor (16,40 → 17,78 ms). Yani istem uzun diye metin yavaş akmaz, sadece geç başlar.
Eşzamanlılıkla birlikte ise tablo bozulur:
| 32K girdi | mc=1 | mc=4 | sınırsız |
|---|---|---|---|
| TTFT | 4.250 ms | 7.104 ms | 37.563 ms |
| TPOT | 17,8 ms | 59,4 ms | 150,6 ms |
| Tam cevap (E2EL) | 8,8 sn | 22,3 sn | 76,0 sn |
| Çıktı tok/sn | 29,1 | 45,8 | 51,7 |
512 tokenlık istemlerde 16 eşzamanlı istek 600 tok/sn veriyordu; 32K'da sınırsız yük 51,7 tok/sn'de
kalıyor. mc=4'te P99 ITL 326,7 ms — birinin metni akarken başkasının 32K prefill'i araya
girip duraklatıyor.
Darboğaz KV cache değil. Sezgi burada yanıltır: 988.865 tokenlık cache 32K'lık isteklerden 29
tanesini tutar ve 16 istek yerleşikken tepe kullanım %56,3'te kaldı. Ölçümde sıfır preemption
görüldü (num_preemptions_total her örneklemede 0,0; logda tek bir tahliye satırı yok). İstekler
kuyruğa girdi ama sebep capacity — yani zamanlama kapasitesi, bellek değil.
Pratik sonucu: uzun istem patlamasında daha fazla KV cache almak işe yaramaz. Sınır prefill zamanlamasındadır. Çare, eşzamanlılığı sınırlamak ya da istemi kısaltmaktır.
Bu bölümdeki koşularda 16 istem kullanıldı (temel ölçümde 40'tı) — her istem 64 kat fazla girdi taşıdığı için. İstek başına gecikmeler (TTFT, TPOT, E2EL) iki tabloda doğrudan karşılaştırılabilir; toplam throughput ve "sınırsız" satırı karşılaştırılamaz.
Ön ek önbelleği: bu modelde varsayılan olarak kapalı
Aynı sistem istemini ya da aynı belgeyi tekrar tekrar gönderiyorsanız vLLM prefill'i yeniden
hesaplamak zorunda değildir — ortak ön eki önbellekten okuyabilir. Ama bu modelde bunu kendiliğinden
yapmaz. Çalışan sunucunun /metrics çıktısı açıkça söylüyordu:
enable_prefix_caching = "False"
vllm:prefix_cache_queries_total = 0.0--enable-prefix-caching bayrağını açıkça eklemek gerekiyor. Eklediğinizde vLLM Mamba
önbelleğini align moduna alır ve bunun deneysel olduğunu söyler.
Bayrakla birlikte ölçüm — aynı ~5.000 tokenlık istem üç kez, ardından ön eki değiştirilmiş bir kontrol isteği:
| Süre | Önbellek isabeti | |
|---|---|---|
| Koşu 1 (soğuk) | 939,6 ms | 0 / 5.063 |
| Koşu 2 | 306,4 ms | 4.704 / 5.063 |
| Koşu 3 | 304,9 ms | 4.704 / 5.063 |
| Aynı gövde, farklı ön ek | 849,0 ms | yeni isabet yok |
3,08 kat hızlanma, istek başına 635 ms. Son satır kontrol deneyidir: ön ek değişince isabet sayacı yerinde çakılı kalıyor, yani kazanç "ikinci istek ısınmış olur" etkisi değil, gerçekten ön ek eşleşmesi.
İsabet oranındaki %7'lik boşluk da gürültü değil: bu kopyanın block_size'ı 784 ve 4.704 = 6 × 784.
Varsayılan ayarda yalnızca tam bloklar yeniden kullanılır; sondaki 359 tokenlık yarım blok her
seferinde yeniden hesaplanır. Aşağıdaki --prefix-match-unit bölümü bu israfı kaldırıyor.
Sebebi FP8 değil — kontrol ettik. Önce "--kv-cache-dtype fp8 kapatıyor olabilir" diye
düşündük. Tek değişkeni çevirip vLLM'in yapılandırma katmanını iki kez çalıştırınca hipotez çöktü:
fp8 KV YOK -> enable_prefix_caching=False
fp8 KV VAR -> enable_prefix_caching=FalseSebep modelin kendisi: vLLM bu mimari (GDN doğrusal dikkat + Mamba önbelleği) için ön ek önbelleğini model bazlı varsayılanla kapatıyor. FP8'den vazgeçmenize gerek yok — bayrağı eklemeniz yeterli.
Deneysel olduğunu vLLM'in kendisi söylüyor: açılışta "Mamba katmanları için desteği deneysel" uyarısı düşer, kaynakta ise elle açmanın hatalı çıktı üretebileceği yazar. Bunu ölçtük: aynı istem soğuk, iki kez sıcak ve ön ek önbelleği hiç olmayan ikinci bir kopyada çalıştırıldı — dört cevabın da SHA-256'sı aynı çıktı. Yani bu testte bozulma görülmedi.
Yine de tek bir test genel doğruluk kanıtı değildir. Üretimde açacaksanız kritik çıktıları önbelleksiz bir kopyayla karşılaştırarak doğrulayın.
Kazanç çıktı uzunluğuna göre değişir. Yukarıdaki 3,08 kat, 16 tokenlık cevaplarda ölçüldü — orada süreyi prefill belirler. 180 tokenlık cevaplarda aynı önbellek isabeti 1,67 kata düşer, çünkü zamanın çoğu zaten üretimde geçer. Ön ek önbelleği TTFT'yi iyileştirir, üretim hızını değil.
Hangi bayrak işe yarar, hangisi yaramaz
Ön ek önbelleğini açtıktan sonra akla iki ayar geliyor. Biri boşa kürek, diğeri asıl kazanç.
--prefix-caching-hash-algo: dokunmayın. Varsayılanı zaten sha256, yani en güvenli seçenek;
onu tekrar yazmak hiçbir şey değiştirmez. Tek gerçek hamle xxhash'e düşmek olur ve bunun iki
sorunu var: vLLM'in kendi belgesi kriptografik olmayan hash'in çok kiracılı ortamda çakışma yoluyla
başka kullanıcının verisini sızdırabileceğini yazıyor, üstelik vllm/vllm-openai imajında
xxhash paketi kurulu bile değil — pod açılışta ölür. Kazanç tarafı da yok: 784 tokenlık bir
bloğun sha256'sı 17,3 µs, aynı bloğun prefill'i 203.636 µs. Yani toplam sürenin on binde
0,85'i.
--prefix-match-unit: asıl kazanç burada. Ön ek isabeti varsayılan olarak yalnızca tam blok
sınırında oluşur; blok boyutuna göre her istekte 783 tokene kadar yeniden hesaplanır. Bu bayrak
eşleşme granülerliğini düşürür.
Tek değişken farkla iki kopya, aynı ~7.000 tokenlık istem, beşer tekrar:
| İsabet | Yeniden hesaplanan | Sıcak istek (medyan) | |
|---|---|---|---|
--prefix-match-unit 16 | %99,8 (6.976/6.988) | 12 token | 184,2 ms |
| Varsayılan | %89,8 (6.272/6.988) | 716 token | 251,5 ms |
%26,8 kazanç, 67 ms. Sapma yok (183,9–184,3 ms'ye karşı 251,2–251,7 ms) ve iki kopyanın cevabının SHA-256'sı aynı.
Sayılar tesadüf değil: 6.272 = 8 × 784 ve 6.976 = 436 × 16.
Değeri blok boyutunuza göre seçin, yoksa pod açılmaz. Kural şu: her KV grubunun block_size'ı
bu sayıya tam bölünmeli; bölünmezse motor Invalid prefix_match_unit diyip durur.
Tuzak, blok boyutunun sabit olmaması: --kv-cache-dtype fp8 onu ikiye katlıyor (token başına
yarı bayt, aynı sayfaya iki kat token). Aynı model, aynı dilim:
block_size | |
|---|---|
--kv-cache-dtype fp8 ile | 1.568 |
| fp8 olmadan | 784 |
784 = 16 × 49'dur, yani 32'ye bölünmez — "32 koyalım" derseniz fp8'siz kurulumda pod açılmaz. 16 ikisini de böler, üstelik daha incedir. Kendi değerinizi doğrulamak için:
curl -s localhost:8000/metrics | tr ',' '\n' | grep -E 'block_size|prefix_match_unit'Asıl faydası çok turlu sohbette. Tek seferlik istemde 67 ms kulağa mütevazı gelir. Ama sohbet her turda birkaç yüz token büyür ve ön ek (sistem istemi + geçmiş) hep aynı kalır. Varsayılan granülerlikte her turda artan kısım yeniden hesaplanır ve yeni bir blok dolana kadar bu israf büyüyerek tekrarlar. 16 ile kayıp hiçbir zaman 15 tokeni geçmez.
MIG'de GPU okumaları — hangisi çalışır
Bu, MIG'e özgü ve şaşırtıcı: sorgu biçimi çalışmaz, tablo çalışır.
nvidia-smi --query-gpu=utilization.gpu,memory.used,memory.total --format=csv[N/A], [Insufficient Permissions], [Insufficient Permissions]Ama düz nvidia-smi çıktısındaki MIG tablosu dilimin belleğini verir:
30241MiB / 71424MiB (ağırlıklar yüklendi)
65799MiB / 71424MiB (KV cache dolarken)Yani "MIG'de bellek görünmez" doğru değil — --query-gpu görmez, MIG tablosu görür. İzleme
kurarken buna dikkat edin.
Sıcaklık ve güç okunur ve — sezginin aksine — kartın geneline değil, sizin kendi GPU
örneğinize aittir. İki dilimden aynı saniyede örnekleme yaptık: boştaki konteyner 129,2 W /
55 °C okurken çalışan 401,3 W / 68 °C okuyordu. Yani bu değerlerle kartın toplam yükünü
çıkaramazsınız; board toplamı için nvidia-smi'yi host üzerinde çalışormalısınız.
Ölçülen dilim değerleri: boşta 129,2 W / 55 °C, tek kopya yük altında 401,3 W / 68 °C.
Ölçülen süreler
| Adım | Süre |
|---|---|
vllm/vllm-openai imajı (düğümde önbellekliyse) | 21 sn |
| FP8 ağırlıklarının inmesi (30,89 GB) | 43 dk 45 sn |
| Ağırlıkların GPU'ya yüklenmesi | 5 dk 24 sn |
| Toplam: pod açılışından ilk isteğe | ~53 dk |
Sık karşılaşılan hatalar
| Belirti | Sebep |
|---|---|
| Pod açılırken öldürülüyor | İmaj düğümde yok ve çekimi 30 dakikayı aşıyor; imajı önceden dağıtın |
| Yeniden başlayınca model tekrar iniyor | HF_HOME kalıcı diskte değil |
tensor-parallel-size 2 çalışmıyor | MIG'de mümkün değil — dilimler arası P2P kapalı |
| TTFT 3 ms görünüyor | curl -w time_starttransfer akışta başlık flush'ını ölçer, ilk tokenı değil |
| bf16 sürüm sığmıyor | 27B bf16 ≈ 54 GB; 71 GB dilimde KV cache'e yer kalmaz, FP8 kullanın |
| CPU değiştirdim ama pod yeniden başladı | İstekte memory_limit de gönderilmiş — değeri aynı olsa bile bellek boyutlandırması sayılır ve konteyneri yeniden başlatır. Yalnız cpu_limit gönderin |
| Uzun istemde ilk kelime çok geç geliyor | Normal — prefill sabit hızlı: TTFT ≈ girdi ÷ 7.700 tok/sn |
Invalid prefix_match_unit ile açılmıyor | Değer block_size'a bölünmüyor; fp8 bloğu 1.568, fp8'siz 784 yapar — 16 ikisini de böler |
| Aynı istemi tekrar gönderince hiç hızlanmıyor | Ön ek önbelleği bu modelde varsayılan kapalı; --enable-prefix-caching ekleyin |
| Uzun istem yükünde her şey yavaşladı | Darboğaz KV cache değil, prefill zamanlaması; eşzamanlılığı sınırlayın |

Gao Kaptan Docs
Qwen3.8 27B (FP8, vLLM)