24 puan yazan GN⁺ 2025-06-25 | 1 yorum | WhatsApp'ta paylaş
  • GPU'larda hesaplama hızı genellikle bellek erişim hızından çok daha yüksektir; bu yüzden bellek hiyerarşisi performansta darboğaz yaratır
  • Aritmetik yoğunluğa (Arithmetic Intensity, AI) göre iş yükleri bellek sınırlı veya hesaplama sınırlı olarak ayrılır; A100 GPU için eşik noktası yaklaşık 13 FLOPs/Byte'tır
  • Performans optimizasyonu için başlıca stratejiler Fusion ve Tiling'dir; Fusion gereksiz bellek gidiş gelişlerini azaltır, Tiling ise veri yeniden kullanımını en üst düzeye çıkarır
  • Senkronizasyon, Coalesced Load, bank conflict çözümü gibi GPU donanımının yapısal özelliklerini anlamak, yüksek performanslı kernel yazımı için önemlidir
  • Occupancy, thread divergence'ın azaltılması, quantization gibi ek unsurlar da gerçek performans üzerinde önemli etki yaratır

GPU'nun hesaplama ve bellek hiyerarşisi

  • GPU'lar genel olarak aritmetik işlem kapasitesi açısından bellek bant genişliğinden çok daha hızlıdır
  • Örneğin NVIDIA A100, yaklaşık 19.5 TFLOPS (32 bit kayan nokta) performans sunarken bellek bant genişliği yaklaşık 1.5TB/s düzeyindedir
  • 4 baytlık veri okunurken onlarca hesaplama yapılabildiğinden, performanstaki asıl darboğaz veri taşımadır
  • Global memory (VRAM) tüm verilerin bulunduğu yavaş, çip dışı bellektir; Streaming Multiprocessor (SM) ise hesaplamayı yürütür
  • Her SM içinde yüksek hızlı, çip içi Shared Memory (SRAM) bulunur ve bu alan programın doğrudan yönettiği bir cache olarak kullanılabilir
  • Thread, en küçük yürütme birimidir ve her thread kendi ayrı register kümesine sahiptir
  • 32 thread bir warp oluşturur; block ise aynı SM üzerinde çalıştırılan thread ızgarasıdır

Performans bölgeleri: bellek sınırlı vs hesaplama sınırlı

  • Bir kernel'in performansı ya bellek sınırlıdır (veri taşıma hızıyla sınırlı) ya da hesaplama sınırlıdır (SM işlem kapasitesiyle sınırlı)
  • Aritmetik yoğunluk (AI), Total FLOPs / Total Bytes Accessed olarak tanımlanır ve kritik bir göstergedir
  • Roofline modeli: x ekseni AI, y ekseni FLOPS/s olan grafikte kernel'in erişebildiği performansı gösterir
    • AI düşükse ve iş yükü bellek sınırlıysa diyagonal çizgi izlenir (bellek bant genişliği sınırı)
    • AI yüksekse ve iş yükü hesaplama sınırlıysa yatay çizgi izlenir (azami hesaplama performansı sınırı)
  • A100'ün ridge point değeri 19.5 TFLOPS / 1.5 TB/s ≈ 13 FLOPs/Byte'tır
  • AI yükseldikçe performans artar ve kernel hesaplama sınırlı bölgeye ulaşabilir

Aritmetik yoğunluğu artırma stratejileri

  • Basit model: 1 thread'in tek bir C[i,j] değeri hesaplaması → AI = 0.25 (çok düşük, bellek sınırlı)
  • Thread 2x2 tile hesaplasa bile AI = 0.5 olur (hala düşük)
  • AI'yi artırmak için birden fazla thread'in block düzeyinde büyük tile'ları Shared Memory'ye yükleyip veri yeniden kullanımını en üst düzeye çıkarması gerekir
  • Block içi thread işbirliğiyle AI > 13 seviyesine çıkarılarak hesaplama sınırlı bölgeye girilebilir

Overhead-bound durumu

  • CPU'nun (host) GPU'ya iş atadığı süreçte overhead oluşabilir
  • GPU kernel'leri çok küçükse veya çok fazlaysa, GPU iş bekler durumda kalabilir
  • Modern framework'ler eşzamansız yürütme kullanarak komut akışını önceden kuyruğa alır ve overhead'i azaltır

Performans artırmak için iki temel strateji: Fusion ve Tiling

Operator Fusion

  • Basit bir işlem zincirinde, örneğin y = relu(x + 1), her işlem ayrı bir kernel olarak çalışırsa veri global memory ile gidip gelir
  • Fusion, birden fazla işlemi tek kernel içinde birleştirerek ara değerleri global memory'ye yazmadan, hesaplamayı register içinde yapar ve yalnızca son çıktıyı kaydeder
  • Örnek: Triton, torch.compile Inductor gibi JIT derleyiciler bunu otomatikleştirebilir

Tiling

  • Matris çarpımı gibi daha karmaşık işlemlerde tek thread modeliyle AI düşük kalır
  • Block düzeyinde tile'lara bölündükten sonra, block içindeki tüm thread'ler işbirliği yaparak veri tile'larını Shared Memory'ye yükler ve büyük ölçekli veri yeniden kullanımı sağlar
  • Hesaplama genellikle şu 3 adımlı kalıbı izler: "Load (global -> Shared Memory) - Synchronize (senkronizasyon) - Compute (hesaplama)"

Coalesced Load ve vektörleştirme

  • Veriyi global memory'den Shared Memory'ye taşırken Coalesced Access önemlidir (bir warp içindeki 32 thread'in art arda gelen 128 baytlık bölgeye erişmesi)
  • Vektörleştirme ile (ör. float4) tek seferde birden fazla veri yüklemek, donanım kaynaklarını daha verimli kullanır ve bellek bant genişliğinden daha iyi yararlanır
  • Veri hizalaması (alignment) zorunludur; matristeki bayt sayısını belirleyen K değerinin 4'ün katı olması verimlilik için önemlidir

Shared Memory bank'leri ve bank conflict

  • Shared Memory, 32 bağımsız bank'ten oluşur; bir warp içindeki 32 thread farklı bank'lere eriştiğinde çakışma olmadan çalışır
  • Satır bazlı erişim çakışma yaratmazken sütun bazlı erişim çakışma yaratır (aynı bank'e erişim)
  • B tile'ı, "yükle ve transpose et" stratejisiyle Shared Memory'ye transpoze edilerek yazılır; böylece hesaplama sırasında satır erişimi ağırlıklı düzenle bank conflict önlenir

Hızlı çip içi hesaplama kalıpları

Temel strateji 1: bir thread, bir output hesaplar

  • BLOCK_DIM=32 kısıtı altında AI en fazla 8 olur; bu da hesaplama sınırlı bölgeye girmeye yetmez

Strateji 2: bir thread, birden fazla output hesaplar

  • BLOCK_DIM=16, TILE_DIM=64 olduğunda bir thread 4x4 output hesaplar → AI=16
  • AI>13 olduğu için A100 üzerinde compute-bound performansa ulaşılabilir
  • Shared Memory'den float4 gibi vektörleştirilmiş yüklemelerle verimli hesaplama yapılabilir

Tile'lamanın pratik sınırı: tile quantization

  • Matris boyutu tile boyutunun katı değilse, sınır block'ları gerçekte gerekenden daha büyük alanları hesaplar (gereksiz işlem) ve padding uygulanır
  • Sınırdaki thread'ler guard koşullarıyla gereksiz bellek erişimini engeller, ancak hesaplama döngüsü aynı kaldığı için anlamsız işlemler (ör. C += A * 0) yine yapılır

Ek performans ayarı unsurları

Occupancy ve latency hiding

  • Bir warp bellek okuma gibi uzun beklemelere girdiğinde, SM boş kalmamak için hemen başka bir warp'a geçer (latency hiding)
  • Aynı anda birden çok Thread Block atanırsa, yüksek occupancy sayesinde bekleme süresi azaltılabilir
  • Block veya tile boyutu fazla büyürse resident block sayısı düşer, occupancy azalır ve performans gerileyebilir

Thread divergence'ı azaltma

  • Warp içinde if-else dallanması olursa iki yol sıralı çalıştırılır ve etkin performans yaklaşık yarıya düşer
  • min, max gibi branchless yaklaşımlarla dallanmayı azaltmak gerekir

Quantization

  • FP32'den FP16/BFP16 gibi daha düşük hassasiyete geçildiğinde, hem bellek taşıma miktarı hem de işlenebilir veri sayısı yaklaşık 2 kat iyileşir
  • A100 üzerinde FP16 hesaplama 312 TFLOPS'a ulaşabilir (FP32'nin 19.5 TFLOPS değerine göre teorik olarak 16 kata kadar)
  • Quantization ile Roofline üzerinde hem sağa (bellek verimliliği) hem yukarıya (tepe hesaplama performansı) ilerlemek mümkündür

Genel özet

  • GPU performansının temel sınırı, bellek bant genişliği ile çip içi hesaplama kapasitesi arasındaki dengesizlikten kaynaklanır
  • Performans artışı, veri yeniden kullanımını en üst düzeye çıkarma (Tiling) ve ara bellek trafiğini en aza indirme (Fusion) ile elde edilir
  • Yüksek performanslı kernel yazımı ve optimizasyonu için donanım yapısını (warp, bank, coalesced access, senkronizasyon) anlamak gerekir
  • Pratikte occupancy, dallanmanın azaltılması, quantization gibi ek etmenler gerçek hızı doğrudan etkiler
  • Yüksek performanslı GPU hesaplama tasarımı; teorik AI artışı, donanım özelliklerinden yararlanma ve gerçek veri yerleşimi ile boyutlarına uyum sağlama gibi birleşik değerlendirmeler gerektirir

1 yorum

 
GN⁺ 2025-06-25
Hacker News görüşü
  • Tüm program optimizasyonunun derleyici seviyesinde ne kadar iyi yapıldığını merak ediyor; her bir LLM mimarisini tek tek optimize etmeye dayanan mevcut yaklaşımın biraz geride kalmış gibi hissettirdiğini düşünüyor.

  • Aynı 4070 üzerinde llama.cpp ve vllm çalıştırarak daha fazla prompt’u batch halinde işlemeye çalışma deneyimini paylaşıyor; batch 8’den itibaren llama.cpp ciddi biçimde yavaşlıyor, GPU kullanım oranı iyi görünse de gerçekte darboğaz oluştuğunu açıklıyor, vllm ise bunu çok daha iyi yönetiyor.

    • vllm, paged KV cache ve GPU’nun tercih ettiği fully coalesced layout kullandığı için batch’e optimize performans sunuyor; buna karşılık llama.cpp, tekil prompt için iyi olan flat layout kullandığından batch durumunda L2 bellek erişim deseni bozuluyor ve hız düşüyor.

    • llama.cpp içinde KV tensor’ü [seq, head, dim] biçiminden [head, seq, dim] biçiminde interleave edince, vllm’in fused attention kernel’a veri besleme yöntemine yaklaşarak hesaplama performansında hemen yaklaşık 2 kat artış gördüğünü paylaşıyor.

    • Darboğazın nedeni GPU’nun kendisi değil; shared memory erişiminin ve global read’in nasıl tasarlandığı. vllm tam da bunu layout değişikliğiyle hedef alıyor.

    • Bu darboğaz analizinin 2 günden fazla sürdüğünü, yalnızca GPU kullanım grafiğine bakarak bunun anlaşılamadığını ve çoğunu deneme-yanılmayla fark ettiğini söylüyor.

    • Bu tür deneyleri daha kolay, hot reload benzeri bir şekilde tekrarlamanın bir yolu olup olmadığını soruyor.

    • GPU’nun darboğaz olmadığını söylemiş olsa da, gerçekte bellek layout’undaki verimsizliğin sonuçta GPU’nun hesaplama verimliliğini düşüren darboğaz olduğu belirtiliyor.

    • DeepSeek çalışanlarının dün açıkladığı nano-vllm projesinden bahsediliyor; yalnızca 1200 satır olmasına rağmen vanilla vllm’den daha hızlı sonuç verdiği paylaşılıyor https://github.com/GeeeekExplorer/nano-vllm

    • llama.cpp içindeki değişen layout’un pull request olarak gönderilip gönderilmediği soruluyor; 2 kat iyileşmenin herkes için büyük kazanç olabileceği görüşü dile getiriliyor.

    • ik_llama.cpp adlı projeyi de denemesini öneriyorlar https://github.com/ikawrakow/ik_llama.cpp

  • Bunun iyi bilgi içeren bir makale olduğunu, fakat içeriğin daha çok NVIDIA’nın GPU mimarisini geliştirirken seçtiği unsurlarla ilgili olduğunu; diğer şirketlerle farkları yanlış anlamamak gerektiğini söylüyor.

    • Örneğin AMD Instinct MI300, FP32’de azami 160 TFLOPS ve 6TB/s HBM3/3E bant genişliğiyle ridge-point’i değiştiriyor; bu da A100’ün 13 FLOPs/byte değerinin iki katı olan 27 FLOPs/byte anlamına geliyor. Büyük HBM belleği (128~256GB) ise tiling depth ile occupancy arasındaki pratik ödünleşimleri de değiştiriyor. Ancak bu tür GPU’lar pahalı ve CUDA desteği yok; bu da ayrı bir ödünleşim.

    • AMD, hesaplama yazılımına daha çok odaklanana kadar görünürlüğü esasen NVIDIA GPU’ların koruyacağı görüşü paylaşılıyor.

  • Spoiler olarak, aslında önemli olanın GPU’nun nasıl çalıştığı değil, bunun makine öğrenimi hesaplamalarında nasıl kullanıldığı olduğu vurgulanıyor.

    • Aslında içeriğin CUDA’nın genel bir özeti olmaya daha yakın olduğu, relu örneği ve torch dışında makine öğrenimiyle çok ilgili olmadığı eleştirisi yapılıyor.
  • Mutlaka kontrastlı renkler kullanılması gerektiği, okunabilirliğin önemli olduğu söyleniyor.

    • font-weight: 300 kullanma deneyimi paylaşılıyor; çoğu Mac tasarımcısının font smoothing ayarlarına göre geliştirme yaptığı, bu yüzden genelde “normal” görünsün diye ayarladıkları şeyin Mac’te ince fontları bile yarı kalın gösterdiği, bu nedenle tasarımcıların “normal” hissi vermek için daha ince font seçme eğiliminde olduğu belirtiliyor. İlgili bağlantı da paylaşılıyor https://news.ycombinator.com/item?id=23553486

    • Yazarın içeriği dark mode’da düzenleyip biçimlendiriyor olabileceği tahmin ediliyor; edge://flags/#enable-force-dark uygulanırsa bağlantıların iyi göründüğü de belirtiliyor.

    • Yazının bağlantıları ve kod blokları içindeki yorumları okumanın özellikle daha fazla çaba gerektirdiği, kontrastın artırılması gerektiği söyleniyor; buna rağmen içeriğin kalitesinin çok yüksek olduğu değerlendiriliyor.

    • Web sitesinin metinde alfa saydamlığı kullanarak kontrastı ciddi biçimde düşürmesinin büyük bir hata olduğu eleştiriliyor.

  • Bu yazı için aslında “Nvidia GPU’lar hakkında temel gerçekler” gibi bir başlığın daha uygun olabileceği öneriliyor. WARP teriminin de modern Nvidia GPU’larına özgü olduğu, 2003 civarındaki Nvidia GPU’ların yalnızca video oyunu render etmek için kullanılan donanım olduğu ve bugünün genel amaçlı hesaplama GPU’larından tamamen farklı olduğu açıklanıyor. Sonuç olarak, yazının tüm GPU’lara uygulanabilecek genel bir açıklama olmadığı özetleniyor.

  • Gerçekten çok iyi bir başlangıç kaynağı olduğu için teşekkür ediliyor; AI PC’yi kendi başına toplarken GPU üzerine günlerce araştırma yaptığını, bu yazının mutlaka bilinmesi gereken öz noktaları ve yüksek katma değerli uygulama alanını (üretken yapay zeka) çok iyi toparladığını, bu yüzden çok yardımcı olduğunu anlatıyor. Özellikle A100 GPU’nun bellek hiyerarşisi diyagramının çok faydalı olduğu söyleniyor.

  • ASCII diyagram kullanılmasına şaşırıldığını belirtiyor.