İçeriğe geç
Muhammet Şafak
en
Soran: Arda Cevaplandı:

Go'da GC baskısını sync.Pool ve escape analizi ile nasıl azaltırım?


Soru

Go ile yazdığımız bir veri ingest servisi saniyede 50.000 küçük JSON paketi alıp işleyip veritabanına yazıyor. Yoğun yükte GC devreye girince CPU tavan yapıyor ve "Stop-the-World" duraklamaları API yanıt sürelerinde spike yaratıyor. Bellekteki nesne tahsisini azaltmak için `sync.Pool` ve escape analysis'i bu senaryoda nasıl uygularım?

Cevap

Kısa cevap: GC duraklamaları bir semptom; asıl düşman tahsis hızı (allocation rate). Önce tahsisleri kesin, GC zaten rahatlar.

Yaşadığınız spike’lar GC’nin “kötü” olmasından değil — saniyede 50k paket, saniyede yüz binlerce kısa ömürlü nesne demek; GC bu çöpü toplamak için sürekli ve agresif çalışıyor, Stop-the-World duraklamaları da yanıt süresine yansıyor. Çözüm GC’yi kapatmak değil, ona daha az iş vermek.

  1. Hot path’te sync.Pool ile yeniden kullanın. Ingest yolundaki kısa ömürlü buffer’ları, struct’ları ve decoder’ları her pakette new’lemek yerine havuzdan al-geri verin. Kritik detay: Put etmeden önce nesneyi resetleyin ki eski veri sızmasın. Bu, en sıcak yoldaki tahsis basıncını dramatik düşürür.
  2. Tahsisi kökten azaltın. Slice/map’leri biliyorsanız baştan make([]T, 0, n) ile boyutlandırın; bytes.Buffer ve JSON decoder’ı tekrar kullanın; gereksiz []bytestring kopyalarından kaçının. Tek bir kopya 50k kez çarpınca dağ olur.
  3. Escape analizini çalıştırın, stack’te tutun. go build -gcflags=-m çıktısı size hangi değerin heap’e “kaçtığını” (escape) söyler. Bir pointer’ı fonksiyondan dışarı kaçırmak, bir değeri interface’e koymak çoğu kaçışın nedenidir; bunları stack’te tutarsanız GC o nesneyi hiç görmez — en ucuz tahsis, hiç yapılmayan tahsistir.
  4. Kör gitmeyin: pprof ile önce/sonra ölçün. pprof’un alloc_space profili size en çok tahsis eden satırı söyler; GODEBUG=gctrace=1 ile GC sıklığını ve duraklama süresini izleyin. Değişikliği uygulamadan önce ve sonra aynı yükle ölçün — “iyileştirdim” diye düşündüğünüz şey çoğu zaman ölçünce yerinde sayıyordur.
  5. Pooling’in tuzağına dikkat. Başka heap verisine pointer tutan nesneleri havuzlamak ters tepebilir: havuz o nesneyi canlı tuttuğu için işaret ettiği büyük graf da serbest kalmaz, bellek beklediğinden çok şişer. Havuzda yalnızca düz, kendi içinde kapalı buffer/struct’ları tutun.

Sonuç: Sıralama net: profilleyin → en çok tahsis edeni öldürün → hayatta kalanları havuzlayın → sonra GOGC/GOMEMLIMIT ayarlayın. GOGC ve soft memory limit (GOMEMLIMIT) GC’yi seyrekleştirir ama saniyede 50k tahsis yapan bir yolu düzeltmez; onlar son ince ayar, ilk hamle değil. Asıl kazanç, GC’ye en sıcak yolda hiç çöp üretmemekten gelir.

İlgili Yazılar

Etiketler: #Performans#Go
Uzmanlık: Go Geliştirici
Paylaş:

Yorumlar

Yorum yapmak için GitHub hesabınızla giriş yapmanız yeterli. Yorumlar GitHub Discussions üzerinde saklanır.

Diğer Sorular

Tüm sorular

Sitede Ara

Yazı, proje ve sayfalarda arama yapmak için yazmaya başlayın.

Esc ile kapat Pagefind ile güçlendirildi