Hi,
kennt sich wer mit Visual Studio / C++ und Memoty Pool Techniken aus?
Folgendes Problem:
Der Abbau (Destruktoren) eines recht grossen Baums (60MB) aus Objekten
dauert sehr lange (ca. 1sec).
Im Baum werden sehr viele Objekte/Knoten angelegt, dazu kommt noch
einiges an 'std::string' in jedem Objekt.
Da mir des Konzept eines Memory Pools in C auf ähnlicher Datenstruktur
einen Gewinn von > Faktor 100 gebracht hat, wollte ich das in C++
ebenfalls anwenden.
Jetzt habe ich folgendes ausprobiert, und wundere mich, warum:
- originales new/delete: 63ms / 730ms
- überladenes new/delete (ohne Funktion): 43ms / 700ms
- nur 'delete' überladen, aber ohne Funktion: 63ms / 13ms
In meinem Memory Pool werden immer 2MB Blöcke allociert, an der
Geschwindigkeit ändert sich auch dann nichts, wenn ich die Blockgrösse
auf 200MB anhebe (nur noch 1 Block).
---
Eigentlich könnte ich mir den Destruktor Aufruf komplett sparen, wenn
man über einen Aufruf gegen Ende des Programms den kompletten Speicher
freigeben könnte.
Weiss jemand Rat?
Random .. schrieb:> Eigentlich könnte ich mir den Destruktor Aufruf komplett sparen, wenn> man über einen Aufruf gegen Ende des Programms den kompletten Speicher> freigeben könnte.
warum freigeben? Ja, es ist nicht sauber, habe ich aber auch schon
gemacht (hatte das gleiche Problem). Das Betriebssystem räumt es eh am
ende auf.
Vielleicht http://www.boost.org/doc/libs/1_58_0/libs/pool
Using pool interfaces, you can choose to run their destructors or just
drop them off into oblivion; the pool interface will guarantee that
there are no system memory leaks.
When should I use Pool?
Pools are generally used when there is a lot of allocation and
deallocation of small objects. Another common usage is the situation
above, where many objects may be dropped out of memory.
Speicher zurückzugeben macht doch nur Sinn, wenn man ihn nicht mehr
braucht UND der Prozess aber weiterlaufen soll.
Einen Pool nutzt mal entweder um viele "gleichgrosse" Objekte in einem
"Array" zu halten und/oder diese vor der eigentlichen Speicherverwaltung
zu verstecken. Ein Slot in einem solchen "Array" ist frei oder belegt,
aber niemals zu klein oder zu groß. Und man kann man Z.B. nach der
Funktion die diese x-Tausend Objekte intern braucht, einfach den ganzen
200MB Bloch wieder zurückgeben.
Bastler schrieb:> Einen Pool nutzt mal entweder um viele "gleichgrosse" Objekte in einem> "Array" zu halten und/oder diese vor der eigentlichen Speicherverwaltung> zu verstecken.Gleichgroß ist aber keine Voraussetzung dafür.
Es kann durchaus sinnvoll sein, viele kleine Objekte aus einem großen
Speicherstück zu reservieren. Vorallem wenn das Betriebssystem nicht
beliebig kleine Objekte zuteilen kann.
Außerdem sparts den Verwaltungsaufwand.
Nase schrieb:> Es kann durchaus sinnvoll sein, viele kleine Objekte aus einem großen> Speicherstück zu reservieren. Vorallem wenn das Betriebssystem nicht> beliebig kleine Objekte zuteilen kann.
das BS kann meist eh nicht kleiner als 4k - den Rest macht den
C-Runtime. Damit hat man eigentlich schon einen Memory Pool.
Somacht das eigentlich schon die RuntimeLib, großen Block vom OS und
dann aufteilen. Nur kann ich, wenn ich die Größe meiner Objekte kenne,
dies "verschnittfrei" tun. Und da ich, und nicht die, den Block geholt
hab, kann ich den bei Nichtmehrbenutzung am Stück zurückgeben. Dazu muß
ich nicht jedes einzelne Objekt freigeben. Laufzeitunterschied bei 1000
kleinen Objekten: gerne mal Faktor 1000. (Nicht ganz aber sicher 100)
Standardspeicherverwaltungen durchlaufen da auch schon gerne mal Länge
Listen von Objekte. Mit entsprechenden Auswirkungen auf das Workingset
des Prozesses.
Peter II schrieb:> das BS kann meist eh nicht kleiner als 4k - den Rest macht den> C-Runtime. Damit hat man eigentlich schon einen Memory Pool.
Und jede Menge möglicherweise überflüssiger Verwaltungsinformationen.
Etwa Blockgröße und nächster freier Block. Immerhin geht die
Laufzeitumgebung ja davon aus, dass Objekte auch einzeln zurückgegeben
werden. Und dazu muss sie sich irgendetwas über die ausgeteilten Objekte
merken. Wenigstens muss sie irgendwann mal nachschauen, ob sie einen
ganzen "4k-Block" zurückgeben kann.
Knackpunkt ist, dass die Objekte nicht mehr weiterverwendet werden
sollen. Damit erübrigt sich praktisch alles an Verwaltung.
Nase schrieb:> Und jede Menge möglicherweise überflüssiger Verwaltungsinformationen.> Etwa Blockgröße und nächster freier Block.
da er aber
> Im Baum werden sehr viele Objekte/Knoten angelegt, dazu kommt noch> einiges an 'std::string' in jedem Objekt.
speichern will, hat er schon mal mehre unterschiedlich große Objekte.
Und die String werden wohl nicht alle gleich lang sein.
Ich würde überlegen, ob man nicht eine feste größe von char[xx]
verwenden kann statt einen objekt, das spart das new und delete
komplett.
Und ob nun 60 oder 100Mbyte genutzt werden, spielt auf aktuellen PC
nicht wirklich eine rolle.
> Und ob nun 60 oder 100Mbyte genutzt werden, spielt auf aktuellen PC
nicht wirklich eine rolle.
Vom Platz her stimmt das, aber auch Speicherbandbreite ist endlich.
Wenn eine nicht besonders ausgefeilte Speicherverwaltung die 100MB auch
nur teilweise durchläuft, laut Murphy in mindesten dem Abstand einer
Cache-Line-Breite, beser noch immer eine anderen Page, dann stöhnt der
ahnungslose PC-Benutzer vor lauter Cache- und TBL-Nachladen, über die
lahme Software trotz n-GHz Rechner.
Zwischen "Programm läuft" und "Programm läuft flott" gibt es nämlich
einiges Hardware-Spezifisches zu beachten.
Bastler schrieb:> Wenn eine nicht besonders ausgefeilte Speicherverwaltung die 100MB auch> nur teilweise durchläuft, laut Murphy in mindesten dem Abstand einer> Cache-Line-Breite, beser noch immer eine anderen Page, dann stöhnt der> ahnungslose PC-Benutzer vor lauter Cache- und TBL-Nachladen, über die> lahme Software trotz n-GHz Rechner.
aber im schlimmsten fall ruft er für jedes Objekt noch den String ab,
also ein zusätzlicher speicherzugriff.
Sind aber alles nur Vermutungen, vermutlich gibt es eine andere Lösung
als das Problem in der Speicherverwaltung zu suchen.
(z.b. eventuell werden ja sogar die Strings ständig vergrößert)
Ja Objekte sind eben schön bequem. Weil sie Probleme mit der
Speicherverwaltung machen können, kann man nun auf sie verzichten. Oder
man verbessert die Speicherverwaltung. Der eine Weg führt zu Moby.
Einen Thread zum Löschen anlegen. Der läuft dann mit niedriger Priorität
im Hintergrund und das Programm kann schon was anderes im Vordergrund
durchführen.
Peter II schrieb:> speichern will, hat er schon mal mehre unterschiedlich große Objekte.> Und die String werden wohl nicht alle gleich lang sein.
Es ist doch völlig unerheblich, wie groß die Objekte sind.
Prinzipiell reicht das doch:
Nase schrieb:> Prinzipiell reicht das doch:
schön, aber was kann man damit noch praktisches anfangen? Man kann ihn
nie wieder freigeben, weil alles aus dem Programm auf dem "haufen" ist.
Da kann man es auch sein lassen und einfach das freigeben weglassen.
Peter II schrieb:> Nase schrieb:> Prinzipiell reicht das doch:>> schön, aber was kann man damit noch praktisches anfangen? Man kann ihn> nie wieder freigeben, weil alles aus dem Programm auf dem "haufen" ist.>> Da kann man es auch sein lassen und einfach das freigeben weglassen.
Du kannst aber stückchenweise allokieren, und wenn du ALLES nicht mehr
brauchst, auf einen Schlag schnell zurückgeben.
Z.B. Laden einer Datei mit Aufbau von Dstenstrukturen, ob INI-Datei oder
Datenbank mit Index, und bei Fehlschlag oder schliessen der Datei alles
auf einen Schlag freigeben.
Natürlich muss es komplexer sein als Nase sein Beispiel, passendere
Grösse vorab allokieren, Speicherüberlauf testen etc.
Neben diesen allocs aus dem Pool wird das Programm noch andere allocs
aus normalem Speicher machen, für Dinge die nicht im selben Moment
zurückgegeben werden sollen.
Peter II schrieb:> Da kann man es auch sein lassen und einfach das freigeben weglassen.
Das geht aber nur ein einziges Mal, nämlich beim Beenden des Prozesses.
Möglicherweise möchte man aber mehrere Bäume aufbauen und wieder
freigeben.
Problematisch ist bei meinem Minimalbeispiel übrigens, dass kein
ausgerichteter Speicher zugeteilt wird. Das kann schiefgehen.