Wird die Speicherverwaltung beim Programmieren zu einem irrelevanten Problem?
Speicherverwaltung (oder -steuerung) ist eigentlich der Hauptgrund, warum ich C und C ++ verwende.
Speicher ist jetzt vergleichsweise billig.
Nicht schnelles Gedächtnis. Wir sehen uns noch eine kleine Anzahl von Registern an, etwa 32 KB Datencache für L1 auf i7, 256 KB für L2 und 2 MB für L3 / Core. Das gesagt:
Wenn wir nicht in Bezug auf Zielplattformen mit strengen Einschränkungen des Arbeitsspeichers (dh eingebettete Systeme und dergleichen) sprechen, sollte die Speichernutzung heutzutage ein Problem bei der Auswahl einer Allzwecksprache sein?
Speicherauslastung auf einer allgemeinen Ebene, vielleicht nicht. Ein bisschen unpraktisch finde ich, dass ich die Idee eines Notizblocks nicht mag, der beispielsweise 50 Megabyte DRAM und Hunderte Megabyte Festplattenspeicher beansprucht, obwohl ich das zu wenig und reichlich mehr habe. Ich bin schon lange dabei und es fühlt sich für mich einfach komisch und unangenehm an, zu sehen, wie eine so einfache Anwendung relativ viel Speicherplatz für das benötigt, was mit Kilobyte machbar sein sollte. Das heißt, ich könnte vielleicht mit mir selbst leben, wenn mir so etwas begegnet wäre, wenn es immer noch nett und ansprechbar wäre.
Der Grund, warum mir die Speicherverwaltung in meinem Bereich wichtig ist, besteht darin, die Speichernutzung im Allgemeinen nicht so stark zu reduzieren. Hunderte von Megabyte Arbeitsspeicher belasten eine Anwendung nicht unbedingt auf nicht triviale Weise, wenn nicht häufig auf diesen Arbeitsspeicher zugegriffen wird (z. B. nur durch Klicken auf eine Schaltfläche oder eine andere Form der Benutzereingabe, die nur in Ausnahmefällen erfolgt) Es geht um koreanische Starcraft-Spieler, die möglicherweise millionenfach pro Sekunde auf eine Schaltfläche klicken.
Der Grund, warum es in meinem Bereich wichtig ist, dass der Speicher eng und dicht beieinander ist, auf den in diesen kritischen Pfaden sehr häufig zugegriffen wird (z. B. über jeden einzelnen Frame geschleift zu werden). Wir möchten nicht jedes Mal einen Cache-Fehler haben, wenn wir auf eines von Millionen Elementen zugreifen, auf die alle in einer Schleife bei jedem einzelnen Frame zugegriffen werden müssen. Wenn wir den Speicher in großen Blöcken von langsamem auf schnelles Speichern in der Hierarchie verschieben, z. B. 64-Byte-Cache-Zeilen, ist es sehr hilfreich, wenn diese 64-Byte-Blöcke alle relevanten Daten enthalten, wenn wir mehrere Datenelemente in diese 64-Byte-Blöcke einfügen können und wenn unsere Zugriffsmuster so sind, dass wir sie alle verwenden, bevor die Daten gelöscht werden.
Die Daten, auf die häufig zugegriffen wird, für die Millionen Elemente umfassen möglicherweise nur 20 Megabyte, obwohl wir Gigabyte haben. Es macht immer noch einen großen Unterschied, wie viele Frameraten diese Daten bei jedem einzelnen Frame durchlaufen, wenn der Speicher knapp und dicht beieinander ist, um Cache-Ausfälle zu minimieren. Hier ist die Speicherverwaltung / -steuerung von großem Nutzen. Einfaches visuelles Beispiel für eine Kugel mit einigen Millionen Eckpunkten:
Das obige ist tatsächlich langsamer als meine veränderbare Version, da es eine beständige Datenstrukturdarstellung eines Netzes testet, aber abgesehen davon hatte ich Mühe, solche Bildraten auch bei der Hälfte dieser Daten zu erreichen (zugegebenermaßen ist die Hardware seit meinen Kämpfen schneller geworden ), weil ich es nicht geschafft habe, Cache-Ausfälle und den Speicherbedarf für Mesh-Daten zu minimieren. Netze sind einige der schwierigsten Datenstrukturen, mit denen ich mich in diesem Zusammenhang befasst habe, weil sie so viele voneinander abhängige Daten speichern, die synchron bleiben müssen, wie Polygone, Kanten, Scheitelpunkte, so viele Texturabbildungen, wie der Benutzer anfügen möchte, Knochengewichte, Farbkarten, Auswahlsets, Morph-Ziele, Kantengewichte, Polygonmaterialien usw. usw. usw.
Ich habe in den letzten Jahrzehnten eine Reihe von Mesh-Systemen entworfen und implementiert, deren Geschwindigkeit oft sehr proportional zur Speichernutzung war. Obwohl ich mit so viel mehr Speicher arbeite als zu Beginn, sind meine neuen Mesh-Systeme über 10-mal schneller als mein erstes Design (vor fast 20 Jahren) und zu einem großen Teil, weil sie ungefähr 1/10 davon verbrauchen die Erinnerung. Die neueste Version verwendet sogar eine indizierte Komprimierung, um so viele Daten wie möglich zu komprimieren, und trotz des Verarbeitungsaufwands der Dekomprimierung hat die Komprimierung tatsächlich die Leistung verbessert, da wir wiederum so wenig wertvollen schnellen Speicher haben. Ich kann jetzt eine Million Polygonnetze mit Texturkoordinaten, Kantenfalten, Materialzuordnungen usw. zusammen mit einem räumlichen Index in etwa 30 Megabyte anpassen.
Hier ist der veränderbare Prototyp mit über 8 Millionen Vierecken und einem Unterteilungsschema für mehrere Reifen auf einem i3 mit einem GF 8400 (dies war vor einigen Jahren). Es ist schneller als meine unveränderliche Version, wird aber nicht in der Produktion verwendet, da ich die unveränderliche Version für viel einfacher zu warten und die Leistung nicht allzu schlecht finde. Beachten Sie, dass das Drahtgitter keine Facetten, sondern Flecken anzeigt (die Drähte sind tatsächlich Kurven, da sonst das gesamte Netz durchgehend schwarz ist), obwohl alle Punkte in einer Facette vom Pinsel geändert werden.
Auf jeden Fall wollte ich oben nur einiges davon zeigen, um einige konkrete Beispiele und Bereiche zu zeigen, in denen die Speicherverwaltung so hilfreich und hoffentlich auch so ist, dass die Leute nicht glauben, ich spreche nur aus meinem Hintern. Ich neige dazu, ein bisschen irritiert zu werden, wenn Leute sagen, Speicher sei so reichlich vorhanden und billig, weil es sich um langsamen Speicher wie DRAM und Festplatten handelt. Es ist immer noch so klein und wertvoll, wenn es um schnelles Gedächtnis geht, und die Leistung bei wirklich kritischen (dh nicht für alle Fälle üblichen) Pfaden bezieht sich darauf, auf diese kleine Menge an schnellem Gedächtnis zu spielen und es so effektiv wie möglich zu nutzen .
Für diese Art von Dingen ist es sehr hilfreich, mit einer Sprache zu arbeiten, mit der Sie übergeordnete Objekte wie beispielsweise C ++ entwerfen und diese Objekte dennoch in einem oder mehreren zusammenhängenden Arrays speichern können, wobei der Speicher von garantiert wird Alle diese Objekte werden zusammenhängend und ohne unnötigen Speicheraufwand pro Objekt dargestellt (Beispiel: Nicht alle Objekte benötigen Reflektion oder virtuellen Versand). Wenn Sie diese leistungskritischen Bereiche tatsächlich betreten, wird es tatsächlich zu einem Produktivitätsschub, wenn Sie die Speicherkontrolle beispielsweise über das Fummeln mit Objektpools und die Verwendung primitiver Datentypen ausüben, um Objekt-Overhead und GC-Kosten zu vermeiden und den Zugriff auf Speicher häufig zu halten zusammen zusammenhängend.
Daher ist Speicherverwaltung / -kontrolle (oder deren Fehlen) in meinem Fall tatsächlich ein dominierender Grund für die Wahl der Sprache, mit der ich Probleme am produktivsten angehen kann. Ich schreibe definitiv meinen Anteil an Code, der nicht leistungskritisch ist, und ich neige dazu, Lua zu verwenden, das von C aus ziemlich einfach einzubetten ist.