Bereinigt das Zuweisen und Freigeben eines großen Teils des Speichers beim Start den Speicher?

18

Das Buch Game Coding Complete, 4. Auflage , Kapitel 5 ( Game Initialization and Shutdown ), Abschnitt Überprüfung des Speichers, enthält dieses interessante Codebeispiel:

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;
    }
}

Dies wirft einige Fragen auf.

Der erste Teil fragt nur das Betriebssystem (Windows), wie viel physischer RAM verfügbar ist. Der merkwürdige Teil ist der zweite, der einen großen Teil des Speichers zuweist und ihn sofort freigibt:

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

Der Autor erklärt weiter:

... diese Funktion reserviert und gibt sofort einen riesigen Speicherblock frei. Dies bewirkt, dass Windows alle im Speichermanager angesammelten Abfälle bereinigt und überprüft, ob Sie einen zusammenhängenden Block so groß wie nötig zuordnen können. Wenn der Anruf erfolgreich ist, haben Sie im Wesentlichen das Äquivalent einer Zamboni-Maschine im Speicher Ihres Systems ausgeführt, um es für Ihr Spiel auf Eis zu legen ...

Aber ich habe meine Vorbehalte dazu.

Msgstr "Müll säubern, der sich im Speichermanager angesammelt hat?" "Ja wirklich?" Wenn das Spiel gerade erst begonnen hat, sollte es dann keinen Müll geben?

Msgstr "Sicherstellen, dass Sie einen zusammenhängenden Block zuordnen können?" In dem speziellen Fall, in dem Sie den Speicher selbst verwalten möchten, ist dies sinnvoll. Wenn Sie jedoch auf Anhieb viel Speicher zuweisen, können Sie praktisch keine andere Anwendung ausführen das System, während Ihr eingeschaltet ist.

Erzwingt dies wahrscheinlich nicht das Festschreiben des gesamten Arbeitsspeichers durch das Betriebssystem und folglich das Entfernen einer großen Menge Arbeitsspeichers auf den Swap-Speicher, wodurch der Start Ihrer App erheblich verlangsamt wird?

Ist das wirklich eine gute Übung?

glampert
quelle
3
Die meisten modernen Betriebssysteme werden nichts tun , was auch immer , wenn eine App allokiert eines großer Bereich des Speichers, sie optimistisch Zuordnung verwenden und eigentlich gar nichts tun , bis Sie den Speicher füllen, ich kann das nicht vorstellen , etwas mehr zu tun als ein potentiell langsam no-op zu sein
Vality
Wird ein langer Bodenstreifen im Dschungel gerodet, ein Radio, Kopfhörer usw. aus Holz geschnitzt, sodass Flugzeuge landen und Vorräte liefern?
Dan Neely
10
Game Coding Complete enthält eine Menge Unsinn gepaart mit einer Menge Unverständnis-C ++ und C-das-ein-bisschen-wie-C ++ aussieht (auch in diesem Beispiel demonstriert, das Ergebnis von operator newauf überprüfend nullptr), wenn Sie mir erlauben sagen. Das Beste, was Sie mit diesem Buch anfangen können, ist, Ihren Schornstein anzuzünden. Das Zuweisen und Freigeben eines großen Speicherblocks " räumt " natürlich nicht den Speicher auf.
Damon
@Damon, ich habe es nicht überprüft, aber ich vermute, sie haben den globalen newOperator zumindest überladen , um null zurückzugeben, anstatt zu werfen bad_alloc. Wenn nicht, dann ist dieser Code noch unsinniger: P
glampert
1
@glampert: Auch wenn dies der Fall operator deleteist, muss es akzeptiert nullptrund als No-Op behandelt werden. Jede globale Überladung, die das nicht tut, ist kaputt. Das heißt, es ist so oder so unsinnig. Genauso wie man annimmt, dass das Zuweisen und Freigeben eines riesigen Speicherblocks "magisch" etwas Gutes bewirkt. Im besten Fall schadet es nicht (höchstwahrscheinlich, da die Seiten nicht einmal berührt werden ... andernfalls werden möglicherweise einige Seiten aus Ihrem Arbeitsbereich ausgetauscht, die Sie später neu laden müssen).
Damon

Antworten:

12

Eine Sache , die einen großen Teil des Speichers im Voraus zugeordnet könnte tun, wenn Ihr Computer war wenig RAM, könnte sein , das Betriebssystem zu zwingen , etwas mehr Platz zu schaffen durch Teile anderer Programme Speicherplatz auf der Festplatte austauschen.

Da ein solches Auswechseln in der Regel sehr langsam ist und Ihr Programm während des Spiels so gut wie einfriert, kann es von Vorteil sein, sicherzustellen, dass es, falls es trotzdem passieren sollte, vor dem Beginn Ihres Spiels und nicht mitten im Spielgeschehen stattfindet.

Das heißt, ein Swap-to-Disk wie dieses zu erzwingen, ist nicht gerade 100% zuverlässig:

  • Bei vielen Implementierungen von virtuellem Speicher wird durch das bloße Zuweisen von Speicher kein Swap ausgelöst. Dies geschieht nur, wenn Sie zum ersten Mal auf jede Seite des von Ihnen zugewiesenen Speichers zugreifen. (Dies trifft auf jeden Fall auf moderne Linux-Versionen zu, bei denen die Speicherüberlastung standardmäßig aktiviert ist. Ich weiß nicht genau, wie die verschiedenen Windows-Versionen damit umgehen.)

    Wenn Sie also wirklich möchten, dass dieser Code den Speicher aufräumt, sollten Sie wahrscheinlich einen memset()Aufruf oder eine Entsprechung hinzufügen , um Daten in jeden Teil des Arrays (oder zumindest in die ersten physicalRAMNeededBytes davon) zu schreiben, bevor Sie ihn freigeben.

  • Außerdem zwingt das Betriebssystem zu tauschen andere Programme aus dem RAM wie dies bedeutet nicht , dass sie dann bleiben davon aus. Windows ist ein Multitasking-Betriebssystem, und sobald eines dieser ausgelagerten Programme erneut ausgeführt und die ausgelagerten Daten verwendet werden sollen, wird es wieder eingelagert, wodurch das Spiel möglicherweise erneut eingefroren wird.

    Daher ist dieser Trick im Allgemeinen nur nützlich, wenn der RAM von großen Datenmengen aufgebraucht wird, auf die nicht aktiv zugegriffen wird (z. B. große Dokumente, die in einigen Bearbeitungssoftware im Hintergrund offen gelassen werden). Webbrowser sind in dieser Hinsicht in der Regel besonders problematisch, da auf der Seite möglicherweise noch Skripts ausgeführt werden, die das Speichern im RAM erzwingen, auch wenn Sie keine Webseite verwenden, die auf einer Hintergrundregisterkarte geöffnet wurde.

    (Es gibt Möglichkeiten, mit denen ein Programm das Betriebssystem anweist, einen Teil seines Speicherplatzes im RAM zu sperren und das Auslagern zu verhindern. Dies erfordert jedoch in der Regel erhöhte Berechtigungen. In jedem Fall scheint der von Ihnen bereitgestellte Code nicht zu sein mit solchen Methoden.)

Grundsätzlich scheint dieser Trick nur sinnvoll , in dieser relativ engen Grenzsituation , wo der Benutzer würde genug RAM Spiel (und andere Software aktiv im Hintergrund laufen) ohne Austausch, aber das RAM ist derzeit gefüllt mit inaktiven und nicht verwendeten offenen Dateien zu laufen oder andere Daten, die während des Spiels auf die Festplatte übertragen werden können und sollten. In solchen Situationen kann es hilfreich sein, Ihr Spiel flüssiger und reaktionsschneller erscheinen zu lassen, indem gelegentliche kleine Tauschoperationen während des Spiels durch eine einzige während des Startvorgangs ersetzt werden.

Ilmari Karonen
quelle
1
Aufgrund meines begrenzten Wissens über die Speicherverwaltung auf niedriger Ebene kann ich sagen, dass die Entscheidung, ob eine bestimmte Seite ausgetauscht werden soll oder nicht, viel komplexer ist und viel mehr Faktoren berücksichtigt, als nur die angeforderte Speicherkapazität und die verfügbare Speicherkapazität . Dies zu vereinfachen und sich im Allgemeinen auf Vermutungen zu stützen, ist nicht sehr weise.
Panda Pyjama
3
Ich denke, es ist wichtig, sich daran zu erinnern, dass all dies Vermutungen sind, die funktionieren, nicht funktionieren oder zu funktionieren scheinen, aber aus völlig unabhängigen Gründen. Soweit ich weiß, ist keines der in dieser Antwort genannten Verhaltensweisen dokumentiert, und es wäre unklug, sich auf sie zu verlassen. Ein Betriebssystem-Update, eine Änderung der Einstellungen, eine Änderung des Compilers oder so ziemlich alles kann dazu führen, dass dies nicht mehr funktioniert oder sogar die Leistung beeinträchtigt wird.
Panda Pyjama
26

Ich weiß nicht, wie alt das ist, aber ich würde sagen, es ist ziemlich alt. In modernen Windows-Versionen (XP und neuer, aber speziell in 64-Bit-Versionen) würde ich sagen, dass eine solche Vorgehensweise den Start Ihres Spiels kaum negativ beeinflusst.

Denken Sie zunächst daran, dass der Adressraum für jeden Prozess virtualisiert wird. Für Ihren Prozess steht Ihnen der gesamte Adressraum zur Verfügung, und Sie erhalten immer zusammenhängenden (virtuellen) Speicher, unabhängig davon, ob dieser physisch zusammenhängend ist oder nicht.

In der Tat haben Sie keinen Einfluss darauf, ob der Speicher im physischen Speicher zusammenhängend ist oder nicht. Das Betriebssystem wählt aus, wie die physischen Blöcke zugewiesen werden sollen, und nichts, was Sie auf Anwendungsebene tun, kann sich auf die Vorteile (falls vorhanden) eines zusammenhängenden physischen Speichers auswirken.

Sie müssen sich um die Fragmentierung des virtuellen Speichers kümmern, wenn Sie weiterhin sehr lange Speicher unterschiedlicher Größe zuweisen und freigeben. Dies ist natürlich bei einem 64-Bit-Adressraum weniger relevant. Spiele werden in der Regel nicht monatelang ausgeführt, sodass Sie sich höchstwahrscheinlich nicht darum kümmern müssen, es sei denn, es handelt sich um Server mit langer Laufzeit.

Selbst wenn Sie in einem 32-Bit-Computer viel Speicher mit sehr unterschiedlichen Größen zuweisen, ist die Speicherzuweisung in der heutigen Zeit recht gut. Daher ist es unwahrscheinlich, dass Probleme mit der Fragmentierung des virtuellen Speichers auftreten, wenn Sie nicht aktiv danach suchen

Um ehrlich zu sein, ich weiß nicht, worum es bei diesem Schriftsteller geht. Selbst wenn der Adressraum gemeinsam genutzt wurde und Sie einen riesigen zusammenhängenden Block im physischen Speicher erhalten haben, sagen Sie, dass Sie sich nicht mehr darum kümmern, indem Sie ihn freigeben. Im Hintergrund werden andere Programme ausgeführt, die ihre eigenen Zuordnungen vornehmen. Selbst wenn Ihr großes Einkaufszentrum erfolgreich ist, kann niemand garantieren, dass das nächste Programm ebenfalls erfolgreich ist.

Ich denke, das ist ein Fall von "es hat aus irgendeinem Grund funktioniert, den ich nicht wirklich verstehe, also habe ich mir etwas ausgedacht, um es zu erklären".

Panda-Pyjama
quelle
10
RAM ist nicht wie eine rotierende Festplatte, bei der zusammenhängende Blöcke in irgendeiner Weise "besser" sind als nicht. Das ist nicht wahr. Der zusammenhängende RAM-Zugriff ist um eine Größenordnung schneller als der Direktzugriff. RAM-Chips sind in Abschnitte unterteilt, für die eine gewisse Latenzzeit zu wechseln ist. Noch wichtiger ist, dass L1- und L2-Cache-Ausfälle Ihre Leistung drastisch beeinträchtigen, wenn der Speicher verstreut ist.
CaptainCodeman
@CaptainCodeman: Ich muss akzeptieren, dass dies von meinem Fachgebiet abweicht, aber als Windows-Anwendungsprogrammierer haben Sie keine Kontrolle darüber, ob Ihr Speicher physisch zusammenhängend ist oder nicht. Wenn Sie nach einem riesigen Speicherblock fragen, ändert sich nicht viel.
Panda Pyjama
@CaptainCodeman haben tatsächlich zusammenhängende virtuelle Block in zusammenhängenden Mod 2 ^ N Block im RAM wird es aufgrund der Cache-Zeilenzuweisung beschleunigen
Ratschen-Freak
8
@CaptainCodeman Ja, der sequentielle DRAM-Zugriff ist schneller, aber wir sprechen von der virtuellen-> physischen Zuordnung, die in Seiten von 4 KB (oder mehr) erfolgt. Ob diese Zuordnung sequentiell ist oder nicht, sollte also keinen großen Einfluss auf den Arbeitsspeicher haben Lesegeschwindigkeiten. Außerdem werden Cache-Vorablesezugriffe und ähnliches im virtuellen Adressraum ausgeführt, nicht im physischen.
Nathan Reed
1
@ NathanReed Fair genug, und danke für die Klarstellung. Ich habe nur zum Ausdruck gebracht, dass die RAM-Zugriffsgeschwindigkeit über den gesamten Speicherbereich nicht ganz einheitlich ist, wie vorgeschlagen wurde.
CaptainCodeman