Bellek yetersiz durumlara nasıl hazırlanırsınız?


18

Bu, iyi tanımlanmış kapsamı olan oyunlar için kolay olabilir, ancak soru, oyuncunun herhangi bir şey oluşturmasına ve oluşturmasına izin verilen sanal alan oyunlarıyla ilgilidir .

Olası teknikler:

  • Üst limite sahip bellek havuzları kullanın.
  • Artık periyodik olarak ihtiyaç duyulmayan nesneleri silin.
  • Başlangıçta fazladan bellek ayırın, böylece daha sonra bir kurtarma mekanizması olarak serbest bırakılabilir. Yaklaşık 2-4 MB diyebilirim.

Bunun, 16 GB PC'nizden farklı olarak belleğin genellikle sınırlı olduğu mobil / konsol platformlarında gerçekleşmesi daha olasıdır . Bellek ayırma / dağıtma üzerinde tam kontrole sahip olduğunuzu ve çöp toplama işleminin dahil olmadığını varsayıyorum. Bu yüzden bunu C ++ olarak etiketliyorum.

Etkili C ++ Item 7 "Hafıza dışı koşullara hazırlıklı olun" hakkında konuşmamaya dikkat edin, bununla ilgili olmasına rağmen, oyun geliştirmeyle daha fazla ilgili bir cevap görmek istiyorum. olay.

Soruyu özetlemek gerekirse, sınırlı bellek konsolu / mobil platformlu bir platformu hedeflerken, sanal alan oyunları için yetersiz bellek koşullarına nasıl hazırlanırsınız?


Başarısız bellek ayırmaları, modern PC işletim sistemlerinde oldukça nadirdir, çünkü fiziksel RAM bittiğinde otomatik olarak sabit sürücüye değişecektir. Yine de kaçınılması gereken bir durum, çünkü takas fiziksel RAM'den çok daha yavaştır ve performansı ciddi şekilde etkiler.
Philipp

@Philipp evet biliyorum. Ama sorum daha çok konsol ve cep telefonu gibi sınırlı bellek cihazlarına yöneldiğimi düşünüyorum.
concept3d

Bu oldukça geniş bir sorudur (ve ifade edildiği gibi bir anket). Tek bir duruma daha spesifik olmak için kapsamı biraz daraltabilir misiniz?
MichaelHouse

@ Byte56 Soruyu düzenledim. Umarım şimdi daha tanımlanmış bir kapsama sahiptir.
concept3d

Yanıtlar:


16

Genel olarak, bellek yetersiz kalmaz. Bir oyun kadar büyük ve karmaşık yazılımda tek aklı başında seçenek sadece bellek ayırıcı (özellikle hata ayıklama yapılarda) en kısa sürede çökmesini / onaylamak / sonlandırmaktır. Yetersiz bellek koşulları bazı çekirdek sistem yazılımları veya sunucu yazılımları için test edilir ve bazı durumlarda işlenir, ancak genellikle başka yerlerde kullanılmaz.

Bir üst bellek kapağına sahip olduğunuzda, sadece bu kadar fazla belleğe ihtiyacınız olmadığından emin olursunuz. Örneğin, bir kerede maksimum izin verilen NPC'yi tutabilir ve bu sınır düşüldüğünde yeni zorunlu olmayan NPC'lerin üretilmesini durdurabilirsiniz. Temel NPC'ler için, ya temel olmayanları değiştirmelerini ya da tasarımcılarınızın tasarlayacağını bildiği temel NPC'ler için ayrı bir havuz / başlık alabilirsiniz (örneğin, yalnızca 3 temel NPC'niz varsa, tasarımcılar 3'ten fazla bir alan / yığın - iyi araçlar tasarımcıların bunu düzgün yapmasına yardımcı olacaktır ve test elbette önemlidir).

Özellikle kum havuzu oyunları için gerçekten iyi bir akış sistemi de önemlidir. Tüm NPC'leri ve öğeleri hafızada tutmanıza gerek yoktur. Dünyanın parçaları arasında ilerledikçe yeni parçalar akacak ve eski parçalar akacak. Bunlar genellikle arazinin yanı sıra NPC ve öğeleri de içerecektir. En fazla X eski parçanın saklanacağını ve proaktif olarak yüklenen Y yeni parçanın yükleneceğini bilerek, öğe sınırları üzerindeki tasarım ve mühendislik sınırları bu sistem göz önünde bulundurularak ayarlanmalıdır. hafızadaki X + Y + 1 parçalarının verileri.

Bazı oyunlar, iki geçişli bir yaklaşımla bellek dışı durumları ele almaya çalışır. Çoğu oyunun teknik olarak gereksiz önbelleğe sahip çok sayıda verisi (örneğin, yukarıda belirtilen eski parçalar) olduğunu ve bellek ayırmanın aşağıdaki gibi bir şey yapabileceğini unutmayın:

allocate(bytes):
  if can_allocate(bytes):
    return internal_allocate(bytes)
  else:
    warning(LOW_MEMORY)
    tell_systems_to_dump_caches()

    if can_allocate(bytes):
      return internal_allocate(bytes)
    else:
      fatal_error(OUT_OF_MEMORY)

Bu, sürümdeki beklenmedik durumlarla başa çıkmak için son bir önlemdir, ancak hata ayıklama ve test sırasında muhtemelen hemen çökmelidir. Bu tür şeylere güvenmek istemezsiniz (özellikle önbellekleri boşaltmanın bazı ciddi performans sonuçları olabileceğinden).

Ayrıca bazı verilerin yüksek çözünürlüklü kopyalarını dökmeyi de düşünebilirsiniz; örneğin, GPU belleğinde (veya paylaşılan bellek mimarisindeki herhangi bir bellekte) azalmışsanız, yüksek çözünürlüklü mipmap doku düzeylerini dökebilirsiniz. Bununla birlikte, buna değer olmak için genellikle çok fazla mimari çalışma gerektirir.

Bazı çok sınırsız sanal oyun oyunlarının PC'de bile kolayca çökebileceğini unutmayın (128 GB RAM'e sahip bir bilgisayarınız olsa bile ortak 32 bit uygulamaların 2-3 GB adres alanı sınırı olduğunu unutmayın. bit işletim sistemi ve donanım, daha fazla 32 bit uygulamanın aynı anda çalışmasına izin verir, ancak 32 bit ikilinin daha büyük bir adres alanına sahip olmasını sağlamak için hiçbir şey yapamaz). Sonunda, ya her durumda çalıştırmak için sınırsız bellek alanına ihtiyaç duyacak çok esnek bir oyun dünyasına sahipsiniz ya da sınırlı bellekte (veya arada bir yerde) her zaman mükemmel çalışan çok sınırlı ve kontrollü bir dünyaya sahipsiniz.


Bu cevap için +1. Sean'ın tarzını ve ayrık bellek havuzlarını kullanarak çalışan iki sistem yazdım ve ikisi de üretimde iyi çalıştı. Birincisi, çıkışı bir eğri üzerinde maksimum limit kapatma seviyesine geri döndüren bir yumurtlayıcıydı, böylece oyuncu asla ani bir azalma fark etmeyecekti (toplam iş hacminin bu güvenlik marjı tarafından düşürüldüğünü düşündü). İkincisi, başarısız bir tahsisin tasfiye ve yeniden tahsisi zorlayacağı için parçalar ile ilgilidir. ** Sınırlı bellekte her zaman mükemmel çalışan çok sınırlı ve kontrollü bir dünyanın ** uzun süre çalışan herhangi bir müşteri için hayati önem taşıdığını hissediyorum.
Patrick Hughes

Hata ayıklama derlemelerinde hata işlemede olabildiğince agresif olmaktan dolayı +1. Hata ayıklama konsolu donanımında bazen perakende satıştan daha fazla kaynağa erişebildiğinizi unutmayın. Hata ayıklama nesnelerini yalnızca perakende aygıtların sahip olacağı adres alanına tahsis ederek ve perakende eşdeğeri adres alanı kullanıldığında çökerek geliştirici donanımında bu koşulları taklit etmek isteyebilirsiniz.
FlintZA

5

Uygulama genellikle en kötü senaryolarla hedeflenen platformda test edilir ve her zaman hedeflediğiniz platform için hazırlıklı olursunuz. İdeal uygulama asla çökmemelidir, ancak belirli cihazlar için optimizasyon dışında, düşük bellek uyarısı ile karşılaştığınızda çok az seçenek vardır.

En iyi uygulama önceden belirlenmiş havuzlara sahip olmaktır ve oyun en başından beri gerekli tüm belleği kullanır. Oyununuz 100 adetlik bir havuzdan daha fazla 100 birime sahipse ve hepsi bu. 100 birim hedeflenen bir cihaz için bellek gereksinimlerini aşarsa, birimi daha az bellek kullanacak şekilde optimize edebilir veya tasarımı maksimum 90 birim olarak değiştirebilirsiniz. Sınırsız şeyler inşa edebileceğiniz hiçbir durum olmamalı, her zaman bir sınır olmalıdır. Bir sandbox oyununun newher örnek için kullanması çok kötü olurdu, çünkü mem kullanımını asla tahmin edemezsiniz ve bir çökme bir sınırlamadan çok daha kötüdür.

Ayrıca oyun tasarımı her zaman en düşük hedeflenen cihazlara sahip olmalıdır, çünkü tasarımınızı "sınırsız" şeylerle temel alırsanız, bellek sorunlarını çözmek veya daha sonra tasarımı değiştirmek çok daha zor olacaktır.


1

Eh, başlangıçta hatta 16 MiB (sadece% 100 emin olmak için) yaklaşık tahsis edebilirsiniz .bssderleme zamanında ve benzeri bir imza ile, "güvenli ayırıcı" kullanın inline __attribute__((force_inline)) void* alloc(size_t size)( __attribute__((force_inline))bir GCC / olduğu mingw-w64kuvvetler kritik kod bölümlerinin inlining o özellik optimizasyonlar devre dışı bırakılsa bile, oyunlar için etkinleştirilmelerine rağmen) mallocdener void* result = malloc(size)ve başarısız olursa, önbellekleri düşürür, yedek belleği boşaltır (veya başka bir kodu bu .bssşeyi kullanmasını söyler, ancak bu cevap için kapsam dışıdır) ve kaydedilmemiş verileri temizleme (Minecraft benzeri bir parça kavramı kullanıyorsanız, dünyayı diske kaydedin saveAllModifiedChunks()). Ardından, malloc(16777216)(bu 16 MiB'yi yeniden ayırmak) başarısız olursa (tekrar,.bss ), oyunu sonlandırın ve gösterinMessageBox(NULL, "*game name* couldn't continue because of lack of free memory, but your world was safely saved. Try closing background applications and restarting the game", "*Game name*: out of memory", MB_ICONERROR)veya platforma özgü bir alternatif olabilir. Hepsini bir araya koy:

__attribute__((force_inline)) void* alloc(size_t size) {
    void* result = malloc(size); // Attempt to allocate normally
    if (!result) { // If the allocation failed...
        if (!reserveMemory) std::_Exit(); // If alloc() was called from forceFullSave() or reportOutOfMemory() and we again can't allocate, just quit, something is stealing all our memory. If we used the .bss approach, this wouldn't've been necessary.
        free(reserveMemory); // Global variable, pointer to the reserve 16 MiB allocated on startup
        forceFullSave(); // Saves the game
        reportOutOfMemory(); // Platform specific error message box code
        std::_Exit(); // Close silently
    } else return result;
}

Sen benzer bir çözüm kullanabilirsiniz std::set_new_handler(myHandler)nerede myHandlerolduğunu void myHandler(void)o zaman denir newbaşarısız:

void newerrhandler() {
    if (!reserveMemory) std::_Exit(); // If new was called from forceFullSave() or reportOutOfMemory() and we again can't allocate, just quit, something is stealing all our memory. If we used the .bss approach, this wouldn't've been necessary.
    free(reserveMemory); // Global variable, pointer to the reserve 16 MiB allocated on startup
    forceFullSave(); // Saves the game
    reportOutOfMemory(); // Platform specific error message box code
    std::_Exit(); // Close silently
}

// In main ()...
std::set_new_handler(newerrhandler);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.