C++ und Memory Pool

OP Persönliche Seite #4221066
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)     {  ; }
Gast #4221071
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.
Gast #4221079
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.
Gast #4221217
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.
Gast #4221263
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.
Gast #4221267
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.
Gast #4221274
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.
Gast #4221278
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.
Gast #4221376
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.
Gast #4221397
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.
Gast #4221404
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)
Gast #4221412
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.
Gast #4222366
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
}
Gast #4223266
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.
Gast #4223671
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren