Allocare e liberare un grosso pezzo di memoria all'avvio "ripulisce la memoria?"


18

Il libro Game Coding Complete, Fourth Edition , capitolo 5 ( Inizializzazione e spegnimento del gioco ), sezione Checking Memory contiene questo interessante esempio di codice:

bool CheckMemory(const DWORDLONG physicalRAMNeeded, const DWORDLONG virtualRAMNeeded)
{
    MEMORYSTATUSEX status; 
    GlobalMemoryStatusEx(&status);
    if (status.ullTotalPhys < physicalRAMNeeded) 
    {
        // you don’t have enough physical memory. Tell the player to go get a 
        // real computer and give this one to his mother. 
        GCC_ERROR("CheckMemory Failure: Not enough physical memory."); 
        return false;
    }
    // Check for enough free memory.
    if (status.ullAvailVirtual < virtualRAMNeeded) 
    {
        // you don’t have enough virtual memory available.
        // Tell the player to shut down the copy of Visual Studio running in the 
        // background, or whatever seems to be sucking the memory dry. 
        GCC_ERROR("CheckMemory Failure: Not enough virtual memory.");
        return false;
    }

    char *buff = GCC_NEW char[virtualRAMNeeded]; 
    if (buff)
    {
        delete[] buff;
    }
    else
    {
        // even though there is enough memory, it isn't available in one
        // block, which can be critical for games that manage their own memory 
        GCC_ERROR("CheckMemory Failure: Not enough contiguous memory."); 
        return false;
    }
}

Questo solleva alcune domande.

La prima parte chiede solo al sistema operativo (Windows) quanta RAM fisica è disponibile. La parte curiosa è la seconda, che alloca un enorme pezzo di memoria e la libera immediatamente:

char *buff = GCC_NEW char[virtualRAMNeeded]; 
if (buff)
{
    delete[] buff;
}

L'autore continua spiegando:

... questa funzione alloca e rilascia immediatamente un enorme blocco di memoria. Ciò ha l'effetto di fare in modo che Windows ripulisca tutti i rifiuti che si sono accumulati nel gestore della memoria e ricontrolla che puoi allocare un blocco contiguo delle dimensioni di cui hai bisogno. Se la chiamata ha esito positivo, hai essenzialmente eseguito l'equivalente di una macchina Zamboni attraverso la memoria del tuo sistema, rendendolo pronto per il tuo gioco per colpire il ghiaccio ...

Ma ho delle riserve su questo.

"Pulizia della spazzatura che si è accumulata nel gestore della memoria?" Veramente? Se il gioco è appena iniziato, non dovrebbero esserci rifiuti?

"Assicurati di poter allocare un blocco contiguo?" Nel caso molto specifico in cui gestirai la memoria da solo, ciò avrebbe un senso, ma tuttavia, se alloci molta memoria a destra della mazza, praticamente rendi impossibile l'esecuzione di qualsiasi altra applicazione il sistema mentre il tuo è acceso.

Inoltre, non è probabile che questo costringa il sistema operativo a impegnare tutta quella memoria, e di conseguenza sfratta molta memoria nello spazio su disco di swap, rallentando molto l'avvio della tua app?

È davvero una buona pratica?


3
La maggior parte dei sistemi operativi moderni non farà nulla quando un'app alloca una vasta area di memoria, usano l'allocazione ottimistica e in realtà non fanno nulla fino a quando non riempi la memoria, non riesco a immaginare che faccia qualcosa di più che essere una non-lente potenzialmente lenta
Valità

Eliminare una lunga striscia di terra nella giungla, scolpire una radio, cuffie, ecc. Dal legno provoca l'atterraggio di aeroplani e la consegna di rifornimenti?
Dan Neely,

10
Game Coding Complete contiene molte assurdità accoppiate a molte non-comprensione-C ++ e C-che-sembra-un-po-come-C ++ (anche dimostrato in questo esempio, controllando il risultato di operator newfor nullptr), se mi permetti dire. La cosa migliore che puoi fare con quel libro è accendere il tuo camino. Allocare e liberare un grande blocco di memoria ovviamente non "pulisce" la memoria.
Damon,

@Damon, non ho controllato, ma sospetto che abbiano sovraccaricato l' newoperatore globale di restituire null invece di lanciare bad_alloc. In caso contrario, sì, questo codice è ancora più insensato: P
glampert

1
@glampert: anche supponendo che sia così, operator deleteè necessario accettarlo nullptre trattarlo come no-op. Qualsiasi sovraccarico globale che non lo fa è rotto. Ciò significa che è assurdo in entrambi i casi. Proprio come supporre che l'allocazione di un enorme blocco di memoria e il rilascio di esso "magicamente" farà qualcosa di buono. Nella migliore delle ipotesi, non farà alcun danno (molto probabilmente, dal momento che le pagine non sono nemmeno toccate ... altrimenti potrebbe scambiare alcune pagine dal tuo set di lavoro che dovrai ricaricare in seguito).
Damon,

Risposte:


12

Una cosa che potrebbe fare una pre-allocazione di un grosso pezzo di memoria , se il tuo computer avesse poca RAM, potrebbe essere quella di forzare il sistema operativo a liberare spazio extra scambiando parti dello spazio di memoria di altri programmi sul disco.

Poiché tale scambio è generalmente un'operazione molto lenta che congela praticamente il tuo programma mentre sta accadendo, potrebbe esserci qualche vantaggio nel garantire che, se accadrà comunque, accadrà prima che inizi il gioco piuttosto che nel mezzo del gameplay.

Detto questo, forzare uno scambio su disco come questo non è esattamente affidabile al 100%:

  • In molte implementazioni di memoria virtuale, la semplice allocazione della memoria in realtà non attiva alcun scambio; piuttosto, succede solo quando si accede per la prima volta a ciascuna pagina della memoria allocata. (Questo è certamente vero nelle versioni moderne di Linux, con overcommit di memoria abilitato, come è per impostazione predefinita; non so con certezza come diverse versioni di Windows lo gestiscono.)

    Pertanto, se si desidera veramente che questo codice "ripulisca la memoria", è necessario probabilmente aggiungere una memset()chiamata o equivalente per scrivere effettivamente i dati in ogni parte dell'array (o almeno nei primi physicalRAMNeededbyte di esso) prima di rilasciarlo.

  • Inoltre, forzare il sistema operativo a scambiare altri programmi dalla RAM in questo modo non significa che ne rimarranno fuori. Windows è un sistema operativo multitasking e non appena uno di quei programmi scambiati vuole eseguire di nuovo e utilizzare i suoi dati scambiati, verrà ricambiato, potenzialmente congelando di nuovo il gioco.

    Pertanto, questo trucco è generalmente utile solo se la RAM viene consumata da grossi blocchi di dati a cui non si accede attivamente (come documenti di grandi dimensioni lasciati aperti in background in alcuni software di modifica). I browser Web tendono ad essere particolarmente problematici in questo senso, dal momento che, anche se non si sta effettivamente utilizzando una pagina Web aperta in una scheda di sfondo, la pagina potrebbe avere ancora degli script in esecuzione che la costringono a essere mantenuta nella RAM.

    (Ci sono modi per un programma per effettivamente dire il sistema operativo per bloccare una parte del suo spazio di memoria nella RAM e impedire che venga scambiato, ma questi in genere richiedono privilegi elevati. In ogni caso, il codice che hai postato non sembra essere usando tali metodi.)

Fondamentalmente, questo trucco sembra utile solo in quella situazione limite relativamente ristretta in cui l'utente avrebbe abbastanza RAM per eseguire il gioco (e qualsiasi altro software attivamente in esecuzione in background) senza scambiare, ma quella RAM è attualmente piena di file aperti inattivi e non utilizzati o altri dati che possono e devono essere scambiati sul disco mentre il gioco è in esecuzione. In tali situazioni, può essere efficace nel rendere il tuo gioco più fluido e reattivo, sostituendo occasionalmente piccole operazioni di swap durante il gioco con una singola durante l'avvio.


1
Dalla mia limitata conoscenza della gestione della memoria a basso livello, posso dire che la decisione di scambiare una pagina specifica è molto più complessa e prende in considerazione molti più fattori rispetto alla semplice quantità di memoria richiesta e alla quantità di memoria disponibile . Semplificare troppo e fare affidamento sulle supposizioni non è molto saggio.
Panda Pajama,

3
Penso che sia importante ricordare che tutte queste sono supposizioni che potrebbero funzionare, non funzionare o sembrare funzionare, ma per ragioni completamente indipendenti. Per quanto ne so, nessuno dei comportamenti indicati in questa risposta è documentato e non sarebbe saggio fare affidamento su di essi. Un aggiornamento del sistema operativo, la modifica delle impostazioni, la modifica del compilatore o praticamente qualsiasi cosa possono far smettere di funzionare o persino avere un impatto negativo sulle prestazioni.
Panda Pajama,

26

Non so quanti anni ha quel writeup, ma direi che è piuttosto vecchio. In Windows moderno (XP e versioni successive, ma specialmente nelle versioni a 64 bit), direi che fare qualcosa del genere avrà un impatto minimo sull'avvio del gioco.

Prima di tutto, ricorda che lo spazio degli indirizzi è virtualizzato per ogni processo. Per quanto riguarda il processo, hai l'intero spazio degli indirizzi per te stesso e otterrai sempre memoria contigua (virtuale), indipendentemente dal fatto che tale memoria sia fisicamente contigua o meno.

In effetti, se la memoria è contigua o meno nella memoria fisica non è qualcosa su cui hai il controllo. Il sistema operativo sceglierà come allocare i blocchi fisici come meglio crede e nulla di ciò che fai a livello di applicazione può influire sui vantaggi (se presenti) della memoria fisica contigua.

È necessario preoccuparsi della frammentazione della memoria virtuale se si continua a allocare e liberare memoria di dimensioni diverse per un periodo di tempo molto lungo. Questo è ovviamente molto meno rilevante con uno spazio degli indirizzi a 64 bit. I giochi di solito non durano mesi, quindi molto probabilmente non ti dovrai preoccupare di questo, a meno che non stiamo parlando di server di giochi di lunga durata.

Anche se in una macchina a 32 bit allochi molta memoria di dimensioni selvaggiamente diverse, le allocazioni di memoria moderne sono piuttosto buone, quindi è improbabile che tu abbia problemi di frammentazione della memoria virtuale a meno che tu non li stia cercando attivamente

Ad essere sincero con te, non so cosa stia succedendo quello scrittore. Anche se lo spazio degli indirizzi è stato condiviso e hai ottenuto un enorme blocco contiguo nella memoria fisica, liberandolo, stai dicendo che non ti interessa più. Ci sono altri programmi in esecuzione in background che fanno le loro allocazioni, quindi anche se il tuo enorme malloc ha avuto successo, nessuno garantirà che anche quello successivo avrà successo.

Penso che sia un caso di "ha funzionato per qualche motivo che non capisco davvero, quindi ho inventato qualcosa per spiegarlo".


10
La RAM non è come un disco rigido rotante in cui avere blocchi contigui è in qualche modo "migliore" che no. Questo non è vero. l'accesso RAM contiguo è un ordine di grandezza più veloce dell'accesso casuale. I chip RAM sono suddivisi in sezioni per le quali è necessario passare una certa latenza e, cosa ancora più importante, i cali della cache L1 e L2 influiranno drasticamente sulle prestazioni quando la memoria è dispersa.
CaptainCodeman

@CaptainCodeman: Devo accettare che esca dal mio campo di conoscenza, ma in ogni modo, come programmatore di applicazioni Windows, non hai alcun controllo sul fatto che la tua memoria sia fisicamente contigua o meno. Chiedere un enorme blocco di memoria non cambierà molto.
Panda Pajama,

@CaptainCodeman in realtà ha un blocco virtuale contiguo essere in mod contiguo blocco 2 ^ N nella RAM lo accelererà a causa dell'allocazione della linea cache
maniaco del cricchetto

8
@CaptainCodeman Sì, l'accesso sequenziale alla DRAM è più veloce, ma stiamo parlando della mappatura virtuale-> fisica che si verifica in pagine di 4 KB (o più), quindi se tale mappatura è sequenziale o meno non dovrebbe avere un grande impatto sulla memoria velocità di lettura. Inoltre, il prefetch della cache e cose del genere funzionano nello spazio di indirizzi virtuali, non fisici.
Nathan Reed,

1
@NathanReed Abbastanza giusto, e grazie per il chiarimento. Stavo solo esprimendo che la velocità di accesso alla RAM non è del tutto uniforme nello spazio di memoria, come è stato suggerito.
CaptainCodeman
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.