Kafayı yiyecektim dostlar. Yaptığım oyunda, okuması kolay, düzenlemesi basit olsun diye save/load sistemini binary yazmaktan vazgeçip JSON'a geçtim. "Ne güzel, json.dumps() ile bir serialize, json.loads() ile geri alırım" diye düşünüyordum. Meğerse kendi başıma bela almışım.
Oyun ilerledikçe, karakter envanteri genişledikçe, dünya durumu karmaşıklaştıkça bir baktım ki save dosyası 10 MB'ı geçmiş! Şaka gibi ama, oyuncunun sadece 100 eşyası vardı. Her kayıtta bu dosyayı yazmak performansı yerin dibine sokmaya başladı.
Sorunu bulmak için save dosyasını açıp baktım. JSON'ın doğası gereği her şey metin (string) olarak yazılıyordu. En basitinden, bir eşyanın dayanıklılık değeri `100` iken, bu bile diskte `"durability": 100` olarak 15 karakterlik bir alan kaplıyordu. Binary'de bu belki 1-2 byte'tı.
Ayrıca JSON yapısı tekrarlanan anahtar isimlerini (key'leri) her seferinde yazıyordu. 100 eşyanın her biri için `"id"`, `"name"`, `"durability"` gibi anahtarlar sürekli tekrar ediyordu. Bu da inanılmaz bir yer israfıydı.
Python:
# JSON'da her eşya için aynı anahtarlar tekrar eder
{
"inventory": [
{"id": 1, "name": "Kılıç", "durability": 100},
{"id": 2, "name": "Kalkan", "durability": 85},
# ... 98 tane daha aynı yapı
]
}
StackOverflow'da bile doğru düzgün çözüm bulamadım, çünkü herkes "JSON kullan, okuması kolay" diyordu. Ama benim için artık okunabilirlik değil, verimlilik önemliydi. pickle güvenli değildi, kendi binary formatımı yazmam gerekiyordu.
struct kütüphanesine döndüm sonunda. Her veri tipi için sabit bir byte boyutu belirleyip, tüm save verisini bir bytearray'e paketledim. Envanter listesini önce sayısını (integer), sonra her eşyanın sabit boyuttaki verilerini arka arkaya yazarak kaydettim.
Python:
import struct
# Binary yazma örneği (sembolik)
save_data = bytearray()
# Envanterdeki eşya sayısını 4 byte integer olarak yaz
save_data.extend(struct.pack('I', len(inventory)))
for item in inventory:
# Her eşya için ID (4 byte), dayanıklılık (2 byte) vs. paketle
save_data.extend(struct.pack('I h', item.id, item.durability))
# Dosyaya yaz
with open('save.bin', 'wb') as f:
f.write(save_data)
Sonuç mu? Aynı veriyle 10 MB'tan ~150 KB'a düştüm! Performans artışı inanılmazdı.
JSON, konfigürasyon dosyaları veya API cevapları için harika. Ama saniyede onlarca kere yazılan, büyük veri kümeleri içeren oyun save'leri için değil. Bazen "kolay" olan, "doğru" olan olmayabiliyor.
Siz de böyle bir veri boyutu faciası yaşadınız mı? Ya da JSON'dan binary'e geçerken daha temiz bir yönteminiz var mı? Fikirlerinizi bekliyorum!