Go Map Performansı: Büyük Veri Setlerinde Optimizasyon

Go'da map (harita) kullanırken binlerce ya da milyonlarca veri işlerken hafızada ani artışlar ve hız düşüşü yaşanabilir. Sorun çoğunlukla dinamik büyümeden ve senkronizasyon eksikliğinden gelir.

Map'i baştan doğru boyutla oluşturmak ilk adım. Go, iç kapasiteyi aşınca tüm veriye yeni yer ayırır ve eski veriyi kopyalar. Bunun yerine make(map[string]int, 100000) ile ön ayırma yaparsanız bu maliyeti büyük ölçüde azaltırsınız.

İkinci sorun eş zamanlı (concurrent) erişim. Aynı anda birden fazla goroutine map'e yazarsa program çöker. Çözüm sync.RWMutex ile okuma-yazma kilitlemesi ya da sync.Map kullanmaktır.

Burada iki yaklaşımı karşılaştıran örnek kod var:

Reklam · TEST
AdSense Test ReklamıYazı arası (gönderi içi) · Yazı arası
1package main
2
3import (
4"fmt"
5"sync"
6"time"
7)
8
9func testRegularMap() {
10m:= make(map[string]int, 1000000) // Ön ayırma
11var mu sync.RWMutex
12start:= time.Now()
13
14for i:= 0; i < 1000000; i++ {
15mu.Lock()
16m[fmt.Sprintf("key%d", i)] = i
17mu.Unlock()
18}
19
20fmt.Printf("RWMutex map: %v\n", time.Since(start))
21}
22
23func testSyncMap() {
24var m sync.Map
25start:= time.Now()
26
27for i:= 0; i < 1000000; i++ {
28m.Store(fmt.Sprintf("key%d", i), i)
29}
30
31fmt.Printf("sync.Map: %v\n", time.Since(start))
32}
33
34func main() {
35testRegularMap()
36testSyncMap()
37}

Beklenen çıktı:

1RWMutex map: 450ms
2sync.Map: 280ms

sync.Map okuma ağırlıklı işlerde daha hızlı çalışır, çünkü okuma kilitlemesi gerektirmez. Ancak yazma yoğunsa RWMutex ile ön ayırma yapılmış normal map tercih edilebilir.

Sık yapılan hata: map'i hiç boyutlandırmadan milyonlarca veri eklemek. Bu durumda Go hafızayı 10-15 kez yeniden ayırır ve program yavaşlar. Veri sayısını önceden biliyorsanız mutlaka make(map[...], tahminiSize) kullanın.

Bir başka ipucu: çok sık erişilen veriler için sync.Map yerine local cache + periyodik senkronizasyon daha etkili olabilir. Büyük haritaları parçalara bölüp her parçaya ayrı mutex atamak da uyuşmazlığı azaltır.

Reklam · TEST
AdSense Test ReklamıGönderi altı · Görüntülü
konu hakkında tepkinizi verebilirsiniz

5 yorum

  • adlı kullanıcıya yanıt verdi
    persembeaksami

    abi doğru ben de ilk başta mapi normal değişken gibi düşünüyordum sonra productionda bir sürü goroutine çalıştırınca program direkt patladı o zaman anladım neden lock gerektiğini

  • adlı kullanıcıya yanıt verdi
    bosvakitlerde

    interface{} çok kötü ya sonra type assertion falan derdi oluyor

  • adlı kullanıcıya yanıt verdi
    persembeaksami

    sync Mapi denedim ama map[string]interface{} ile uğraştığım için biraz hantal oldu açıkçası RWMutex daha kontrollü geldi bana

  • adlı kullanıcıya yanıt verdi
    bosvakitlerde

    sync Map denedin mi RWMutexten daha basit oluyor bence özellikle çok goroutinein olduğu yerlerde

  • bosvakitlerde

    ya birçok kişi bunu bilmiyor galiba ben de şaşırdım ilk defa öğrendiğimde