Forum: PC-Programmierung C++ und Memory Pool


von Random .. (thorstendb) Benutzerseite


Lesenswert?

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?
1
  void* operator new(std::size_t n) { return thMalloc(n); }
2
  void operator delete(void* p)     {  ; }

von Peter II (Gast)


Lesenswert?

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.

von Tom (Gast)


Lesenswert?

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.

von Random .. (thorstendb) Benutzerseite


Lesenswert?

leider keine externe library ...

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Die Frage, die sich mir stellt, ist:
1
void operator delete(void* p)     {  ; }
Warum braucht der Destruktor - obwohl kein Code in 'delete' ist - 700ms, 
wenn ich 'new' überlade, und 13ms beim originalen 'new'?

von Vlad T. (vlad_tepesch)


Lesenswert?

kannst auch mal nach placement new suchen. musst nur beim delete 
aufpassen

von Bastler (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

Welche operatoren hast du ueberladen?
Die globalen oder die klassenspezifischen?

von Nase (Gast)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

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.

von Bastler (Gast)


Lesenswert?

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.

von Random .. (thorstendb) Benutzerseite


Lesenswert?

> Welche operatoren hast du ueberladen?
Beides versucht...

von Nase (Gast)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

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.

von Bastler (Gast)


Lesenswert?

> 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.

von Peter II (Gast)


Lesenswert?

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)

von Bastler (Gast)


Lesenswert?

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.

von PittyJ (Gast)


Lesenswert?

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.

von Bastler (Gast)


Lesenswert?

pittyJ wie Java?

von Nase (Gast)


Lesenswert?

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:
1
void *buf = malloc(1024 * 1024 * 100);
2
void *p = buf;
3
4
void *operator new (size_t size) throw(bad_alloc) {
5
    void *object = p;
6
    cout << "Allocating " << size << " bytes" << endl;
7
    *((char **) &p) += size;
8
    return object;
9
}

von Peter II (Gast)


Lesenswert?

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.

von MaWin (Gast)


Lesenswert?

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.

von Nase (Gast)


Lesenswert?

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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.