Seri · The Quiet Machine · Bölüm 10
RTX 3060 Üzerinde Yerel LLM Benchmark'ları: Saniyede 35 Token
Bu noktada box’ta SSH, Tailscale, self-hosted bir CI runner ve binlerce dosyayı indeksleyen bir memory framework var. Ama bunların hiçbiri günlük iş açısından asıl önemli soruyu cevaplamıyor: bu RTX 3060’taki model, karşısında kod yazacak kadar hızlı mı, yoksa bir de Ollama çalıştıran bir hobi projesi mi?
Sıra gerçek sayılara geldi.
Ölçülen: saniyede token sayısı
Her gün kullandığım model qwen2.5-coder:14b; Q4’e quantize edilmiş, ağırlıkları yaklaşık 9GB. Ollama WSL içinde değil, doğrudan Windows host’unda çalışıyor; flash attention ve 8-bit KV cache açık olduğu için 16k’lık bir context window de bu ağırlıkların üstüne, aynı 12GB’lık kartın içine sığıyor.
Bunu doğrudan box’ın GPU’sundan, tailnet üzerinden, 220 token’lık üretimlerle ölçtüm:
qwen2.5-coder:14b (14B dense, Q4_K_M, ~9GB) ~35 tok/s
deepseek-coder-v2:16b-lite (MoE, ~2.4B active) ~70 tok/s
14B’de saniyede otuz beş token, okuduğumdan daha hızlı; gerçek kodlamayı belirleyen sayı da işte bu dense iş atının hızı. MoE coder bunu kabaca ikiye katlıyor, 70’e yakın çıkıyor, çünkü parametrelerinin yalnızca yaklaşık 2.4 milyarı her token’da devreye giriyor; her milisaniyelik gecikmenin belli olduğu autocomplete tarzı prompt’lara bağladığım model de bu zaten. İkisi de kartın gerçek tavanının rahatça altında: 14B Q4 yaklaşık 9GB, RTX 3060 kabaca 360 GB/s taşıyor; yani tek başına bellek bant genişliği bile 9GB’lık bir modeli saniyede 40 token civarına sınırlıyor, 35 de bu tavana çoktan yaklaşmış durumda.
Bu ölçüm, bir süredir taşıdığım bir varsayımı da çürüttü: box’a uzaktan bağlanınca cevabın yavaşlayacağını sanıyordum. Üretim tamamen kart üzerinde koştuğu için, aynı odadan attığım istekle tailnet üzerinden bir kafeden attığım istek aynı token/saniye hızıyla geri geliyor. Ağ yalnızca ilk token’a birkaç milisaniye ekliyor, akışın geri kalanına hiçbir şey katmıyor. Tailscale nereye ulaşıyorsa box orada box hızında yanıt veriyor.
12GB tavanı
Bu box’taki RTX 3060’ın 12GB VRAM’i var; bütün kurulumun asıl kısıtı da bu.
Q4’teki bir 14B model, 16k-24k token’lık bir context window’a yer bırakacak şekilde bunun içine sığıyor. Ollama’nın bu box için kullandığım ayarları bu takası açıkça ortaya koyuyor:
OLLAMA_FLASH_ATTENTION=1
OLLAMA_KV_CACHE_TYPE=q8_0
OLLAMA_KEEP_ALIVE=-1
OLLAMA_NUM_PARALLEL=1
OLLAMA_MAX_LOADED_MODELS=1
OLLAMA_MAX_LOADED_MODELS=1, bu listedeki en dürüst satır. Bu kart bir seferde tek model tutuyor. Coder modelinden vision ya da reasoning modeline geçmek, birini unload edip ötekini yüklemek demek — her geçişte birkaç saniyelik ölü zaman.
Serinin önceki bölümlerindeki CI runner işi sırasında karta doğrudan baktığımda, ikinci bir servis devreye girmeden önce de sonra da VRAM 11919MiB / 12288MiB‘de duruyordu. Bu, modelin tam yerleşik ayak izi: ~9GB ağırlık, artı 16k context ve onun 8-bit KV cache’i. Yani kart neredeyse tamamen doluydu bile.
İşe yaramayan: 32B denemesi
14B modelle çalışmak rahatlayınca akla gelen ilk adım daha büyüğünü denemek oldu. qwen2.5-coder:32b zor refactor’larda belirgin biçimde daha güçlü; box da günün büyük bölümünde o VRAM’le başka bir şey yapmıyordu zaten.
Yüklendi, ama sığmadı. Kullanılabilir herhangi bir quantization’da 32B’lik bir model 12GB’ı aşıyor, taşan kısım da GPU bellek bant genişliğinin çok gerisindeki bir DDR4-3600 hattı üzerinden sistem RAM’ine dökülüyor. Yine de yanıt veriyor, ama saniyede 3 ila 4 token civarında, 14B’nin hızının onda biri kadar. Sıradan bir karşılıklı konuşma, her kelimenin sürünerek çıkışını izlemeye dönüşüyor. Yatmadan önce kuyruğa bırakılan bir iş için sorun değil. İnteraktif bir edit döngüsü için kullanılamaz.
qwen3-coder:30b ve 24B’nin üzerindeki her şey aynı duvara çarptı. Tavan, box boşta diye yükselmiyor; bu, işin değil kartın özelliği. Bu modeller onun yerine bir batch katmanına devredildi; sohbet etmek yerine gece boyunca koşuyorlar. 14B model tam da bu yüzden günlük iş atı olarak kaldı.
Bir model rafı
Daha büyük bir kart yerine, her biri elimdeki karta sığacak boyutta küçük modellerden bir raf kurdum. Hızlı bir MoE coder (deepseek-coder-v2:16b-lite) autocomplete tarzı prompt’ları hızlıca yanıtlıyor. Küçük bir vision modeli screenshot’ları ve mockup’ları okuyor. Bir reasoning modeli de, adım adım düşünen bir modelin işe yaradığı ara sıraki debug seanslarını üstleniyor. Her biri aynı 9-12GB’lık pencereye tek başına sığacak boyutta ve üst üste yığılmak yerine ihtiyaç hâlinde yükleniyor.
O sırada o diskte on beş model duruyordu. Herhangi bir anda hangisinin çalışacağına on iki gigabayt karar veriyor.
Claude’un yanında nasıl duruyor
Saniyedeki token, box’ın hızlı olup olmadığını söylüyor. Zor günlerde 300 dolarlık bir GPU’daki 14B modelin, Claude’a ödeme yapmanın yerini tutup tutamayacağını söylemiyor.
Dürüst cevabı hayır; sebebi de hızla ilgili değil. Rutin işte — bir fonksiyonu yeniden yazmak, bir API çağrısının etrafındaki boilerplate’i eklemek, bir kod bloğunun ne yaptığını açıklamak, bir yorumu çevirmek — yerel model çağrı başına $0’a, ihtiyacım kadar iş görecek şekilde okuyup yazıyor. Zor işte ise — bu context window’da hiç görmediği bir codebase’de bir bug’ı izlemek, uzun bir mimari tartışmayı sürdürmek — fark hemen ortaya çıkıyor. Claude’un doğru netleştirici soruyu sorduğu yerde yerel model tahmine başlıyor; ya da Claude’un fark ettiği, altı dosya ötedeki bir tutarsızlığı gözden kaçırıyor.
İki model de sohbet eder gibi akıcı; ikisi de insanı bekletmiyor. Aradaki fark, zor problemlerdeki derinlik. Aynı 14B modele ne kadar ekstra context window eklersen ekle, bu fark kapanmıyor.
Bundan çıkan pratik ayrım şu: küçük, mekanik isteklerin yükünü — eskiden boşu boşuna cloud kotası yakan istekleri — yerel model üstleniyor; Claude ise muhakeme isteyen çağrılara kalıyor. Claude planımın haftalık kullanım limiti eskiden birkaç günde biterdi; rutin isteklerin payı artık ona hiç dokunmadığı için çoğu hafta sonuna kadar dayanıyor.
Bir isteğin hangi tarafa gideceğine, her seferinde elle model seçmeme gerek kalmadan, otomatik karar vermek sıradaki problem: bu kararı gerçek zamanlı olarak bir router’a verdirmek.