PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : MSVC6 SP5 ist zu blöd für korrektes Alignment


zeckensack
2002-06-30, 16:05:00
Ich habe eine recht performancekritische Klasse, die exakt 64 Bytes groß ist (sizeof(zeckes_secret_special_weapon_class)==64).

Instanzen werden dynamisch erzeugt, geht auch nicht großartig anders. Und zwar vieeele davon, bis zu 512k (524288). Da kann ich mir keinen Verschleiß und auch keine Verschwendung teurer Cache-Lines leisten, da ich zu allem Überfluß in diesem Haufen auch noch binär suchen muß.

Wenn ich zum Erzeugen den 'new' Operator hernehme, dann werden die Objekte immer 16Byte neben eine 64 Byte-Grenze gelegt, obwohl angeblich laut Doku 'new' für alles perfektes Alignment liefern soll ... naja. Abgesehen davon, daß kein Prozessor, der in den letzten fünf Jahren verkauft wurde, 16 Byte Cachelines hat. 32 Byte würde ich ja noch halbwegs verstehen (P6-Core), aber das? :bonk:

Kann man das irgendwie abstellen, ohne gleich eine komplette eigene Speicherverwaltung schreiben zu müssen? Oder ist VC6 einfach nur zu dämlich für sowas?

Muß man dafür C++ Features über Bord schmeißen ( pointer=(*something_completely_different)allocation_pointer; ) ???

Btw, ich habe trotz intensiven Lesens immer noch nicht gerafft, ob evtl das Schreiben eines eigenen "operator new ()" helfen kann. Wäre für Aufklärung sehr dankbar.

Demirug
2002-06-30, 16:25:56
Hab dein Problem noch nicht ganz verstandenen. Daruam ein paar rückfragen:

1. Ist das Verhalten sowohl in Debug wie auch in Releasebuilds feststellbar?

2. Wird der Speicher als Array oder jedes Object einzeln angelegt?

zeckensack
2002-06-30, 16:36:11
Originally posted by Demirug
Hab dein Problem noch nicht ganz verstandenen. Daruam ein paar rückfragen:

1. Ist das Verhalten sowohl in Debug wie auch in Releasebuilds feststellbar?

2. Wird der Speicher als Array oder jedes Object einzeln angelegt?
1. Release-Build mit voller Optimierung, Zielarchitektur P6 (höher gibt's nicht), struct member alignment ganz rauf. Debug habe ich garnicht erst ausprobiert, das ist für das Problem eh uninteressant.

2. Jedes einzeln. Ist schwer anders zu realisiern (hint: keine echte App, sondern eine DLL, das Verhalten der nutzenden App ist also nicht vorhersehbar, Rest steht in meiner Sig :naughty: ). Ich mache auch ziemlich viel im Konstruktor/Destruktor und fände es unpraktisch und auch irgendwie peinlich Class::init() und Class::destroy() Funktionen zu machen ... gesetzt den Fall, daß es anders ginge ;(

Demirug
2002-06-30, 16:50:11
struct member alignment kannt du vergesehen. Damit legt man nur fest wie die einzelnen Elemente inerhalb der strucktur angeordnet werden. Auf den Memorymanager hat das keinen Einfluss.

Ich kenne ja nicht das Interval mit dem du die Objecte erzeugst und wieder zerstörst aber ich habe das gleiche Problem in unseren Kommunikationsservern. Dort werden massenhaft dynamisch viele kleine Objekte erzeugt und wieder zerstört. Am Anfang hat mir das Teil 80% CPU last gezogen nachdem ich die Speicherverwaltung an den Primären Stellen neu geschrieben habe waren wir bei 5%.

Was Peinlichkeiten angeht habe ich schon mehrfach für den Speed einige Regeln des guten Stills umgangen.

KiBa
2002-06-30, 17:43:49
Moin
Schonmal den Replacement-new() operator probiert?
Ansonsten ist das Schreiben eines eigenen new-operators nicht allzu schwer. Der Stroustrup sollte da Antworten liefern.
Ich habe mal ein Programm geschrieben, was mit malloc() einen Speicherbereich angefordert hat (mehrere MB), welchen ich dann mit meinen eigenen new()-Operatoren verwaltet habe. Geht schnell und ist relativ einfach zu benutzen, und das Allignment liegt in deiner Hand...

zeckensack
2002-06-30, 17:44:31
Die Objekte haben im Schnitt schon eine brauchbare Lebensdauer. Das Problem ist echt nur das Alignment, weil die komplette Liste bis zu 50000 mal pro Sekunde binär durchsucht wird. Und zwei Cachelines sind eben schlechter als eine ;)

Du schlägst also vor, einen eigenen Speicherpool zu verwalten, daraus alignete 64Byte-Happen zuzuweisen, und dann den Zeiger nach (*Class) zu casten?

Kennst du dich mit dem new operator aus? Kann man da irgendwie die eigene Speicherverwaltung einhängen, sodaß man wenigstens den Konstruktor-/Destruktormechanismus weiter verwenden kann???

Demirug
2002-06-30, 17:50:29
Originally posted by zeckensack
Die Objekte haben im Schnitt schon eine brauchbare Lebensdauer. Das Problem ist echt nur das Alignment, weil die komplette Liste bis zu 50000 mal pro Sekunde binär durchsucht wird. Und zwei Cachelines sind eben schlechter als eine ;)

Du schlägst also vor, einen eigenen Speicherpool zu verwalten, daraus alignete 64Byte-Happen zuzuweisen, und dann den Zeiger nach (*Class) zu casten?

Kennst du dich mit dem new operator aus? Kann man da irgendwie die eigene Speicherverwaltung einhängen, sodaß man wenigstens den Konstruktor-/Destruktormechanismus weiter verwenden kann???

Klar kann man in den new (und delete) operator eine eigene Speicherverwaltung einhängen. Ist bei Visual aber wegen des Debugheaps ein bischen Tricky. Ich habe darauf verzichtet und mir eine MemoryControl Klasse gschrieben.

Mal eine dumme Frage wie durchsuchts du etwas binär was nicht als Array angelegt wurde???

zeckensack
2002-06-30, 17:55:12
Originally posted by Demirug
Klar kann man in den new (und delete) operator eine eigene Speicherverwaltung einhängen. Ist bei Visual aber wegen des Debugheaps ein bischen Tricky. Ich habe darauf verzichtet und mir eine MemoryControl Klasse gschrieben.Und wie??? Erzählt doch mal! :)Mal eine dumme Frage wie durchsuchts du etwas binär was nicht als Array angelegt wurde??? Ich habe Zeiger, die nach dem Suchkriterium aufsteigend sortiert sind ;)

Demirug
2002-06-30, 18:02:33
Originally posted by zeckensack
Und wie??? Erzählt doch mal!:)

Welche Lösung die mit New Delete oder die Memorycontrol Klasse.

Wobei man sagen muss das mir das alignment egal ist und ich nur die ständigen new und deletes verhindern musste. Die Klasse liese sich aber auch darauf aufbohren.

Ich habe Zeiger, die nach dem Suchkriterium aufsteigend sortiert sind ;)

Ist ja dein code und ich sollte mich da raushalten. Aber ich kanns halt nicht lassen. Hast du nur eine solche Liste pro Process???

KiBa
2002-06-30, 18:21:00
Probiers doch lieber erstmal mit "Placement new"
http://gethelp.devx.com/techtips/cpp_pro/10min/10min0999.asp

Wenn du die Destruktoren automatisch aufgerufen haben willst, must du new und delete selbst überladen, da sollte sich in google was finden lassen...

zeckensack
2002-06-30, 18:23:34
Originally posted by Demirug
Welche Lösung die mit New Delete oder die Memorycontrol Klasse.Die mit new und delete. Das Speichermanagement krieg' ich schon hin. Aber mir ist völlig unklar,
1)Wo ich jetzt zB new und delete deklarieren muß
Als Memberfunktion oder global? New als Member, oder?

Class* Class::operator new(parameter für Konstruktor ???) {...} ?

Aber delete erscheint mir global irgendwie logischer ...
void operator delete(Class* object) ?

2)Ob und wie ich den Konstruktor/Destruktor explizit aufrufen muß.
Wobei man sagen muss das mir das alignment egal ist und ich nur die ständigen new und deletes verhindern musste. Die Klasse liese sich aber auch darauf aufbohren.

Ist ja dein code und ich sollte mich da raushalten. Aber ich kanns halt nicht lassen. Hast du nur eine solche Liste pro Process??? Nur eine. Und wenn du darauf hinauswillst ... ich habe nicht vor, mir die maximale Anzahl vorher als Array zu greifen ... das wären knackige 32MB, die in den meisten Fällen einfach nur brach liegen würden.

Mit Multitexturing würde sich der Spaß auf 64MB erweitern :o

Demirug
2002-06-30, 18:33:58
Originally posted by zeckensack
Die mit new und delete. Das Speichermanagement krieg' ich schon hin. Aber mir ist völlig unklar,
1)Wo ich jetzt zB new und delete deklarieren muß
Als Memberfunktion oder global?
Class* Class::operator new(parameter für Konstruktor ???) {...}
2)Ob und wie ich den Konstruktor/Destruktor explizit aufrufen muß.


Die operatoren sind memberfunctionen:

void* operator new (size_t)
void operator delete (void*, size_t)

Wenn man die operatoren überlät braucht man die Konstruktoren und Destruktoren nicht selbst aufzurufen. Den Trick damit das alle auch mit dm MemoryManager vom VC hamoniert habe ich jetzt aber nicht im Kopf. Da müsste ich morgen erst mal im Büro nachschauen.


Nur eine. Und wenn du darauf hinauswillst ... ich habe nicht vor, mir die maximale Anzahl vorher als Array zu greifen ... das wären knackige 32MB, die in den meisten Fällen einfach nur brach liegen würden.

Mit Multitexturing würde sich der Spaß auf 64MB erweitern :o

Ja genau darauf wollte ich hinaus. Aber du brauchst den Speicher ja nicht gleich zu alokieren. Es reicht ja erst mal nur einen Addressbereich von 64 MB zu resavieren und dann bei Bedarf in 4K blöcken zu alokieren.

Schon mal über eine Hashtable nachgedacht?

zeckensack
2002-06-30, 18:42:05
Originally posted by KiBa
Probiers doch lieber erstmal mit "Placement new"
http://gethelp.devx.com/techtips/cpp_pro/10min/10min0999.aspDas sieht ja exakt nach dem aus, was ich hier zu Fuß programmieren wollte :o
Cool, erstmal Danke =)

Hoffentlich kann VC das auch ...
*hoff*

zeckensack
2002-06-30, 18:45:50
Originally posted by Demirug
Die operatoren sind memberfunctionen:

void* operator new (size_t)
void operator delete (void*, size_t)

Wenn man die operatoren überlät braucht man die Konstruktoren und Destruktoren nicht selbst aufzurufen. Den Trick damit das alle auch mit dm MemoryManager vom VC hamoniert habe ich jetzt aber nicht im Kopf. Da müsste ich morgen erst mal im Büro nachschauen.



Ja genau darauf wollte ich hinaus. Aber du brauchst den Speicher ja nicht gleich zu alokieren. Es reicht ja erst mal nur einen Addressbereich von 64 MB zu resavieren und dann bei Bedarf in 4K blöcken zu alokieren.Das zielt beides in die Gleiche Richtung, oder? Also eigene Speicherverwaltung ohne eigene Steuerung der Objekt-Erstellung würde jetzt nicht viel Sinn machen, oder habe ich dich falsch verstanden???Schon mal über eine Hashtable nachgedacht? Hmm, bisher nicht. Ist aber eigentlich eine gute Idee :)

Demirug
2002-06-30, 18:53:43
Originally posted by zeckensack
Das zielt beides in die Gleiche Richtung, oder? Also eigene Speicherverwaltung ohne eigene Steuerung der Objekt-Erstellung würde jetzt nicht viel Sinn machen, oder habe ich dich falsch verstanden???

Mehr oder minder schon. Wenn du aber über die Speicherfunktionen von Windows dafür sorgst das die Objekte hintereinander liegen kannst du das ganze als Array verwalten und sparst das Zeigerarray.

Da ich den genauen Anwendungsfall nicht kennen bin ich mir aber nicht sicher ob ein Array oder eine Hashtable besser wäre.

zeckensack
2002-06-30, 18:55:36
Originally posted by zeckensack
Hoffentlich kann VC das auch ...
*hoff* Geil! Compiliert zumindest ohne Fehler :)
Muß das später mal ausgiebig testen, ob's auch das macht, was ich will. Also heute abend erstmal Memory-Manager proggen. Juppi! :jump1:

zeckensack
2002-06-30, 19:06:59
Originally posted by Demirug


Mehr oder minder schon. Wenn du aber über die Speicherfunktionen von Windows dafür sorgst das die Objekte hintereinander liegen kannst du das ganze als Array verwalten und sparst das Zeigerarray.

Da ich den genauen Anwendungsfall nicht kennen bin ich mir aber nicht sicher ob ein Array oder eine Hashtable besser wäre. Hmmm. Das Zeigerarray kann ich vertretbar sortiert halten, weil's pro Objekt nur 4 bytes braucht und nur mittelmäßig oft umgeschichtet werden muß.

Mit der Hashtable bräuchte ich das Zeigerarray überhaupt nicht mehr ... außerdem kann ich in meinem speziellen Fall die Hashtable 100%ig Kollisionsfrei halten, ganz dickes Plus.

Schlecht ist allerdings, daß ich ab und zu auch 'zwischen' zwei Objekten linear suchen muß, die Hashtable enthält unter Umständen soviel 'Luft', daß die Vorteile wieder dahin wären.
Ich muß das mal theoretisch aufstellen, was öfter vorkommt, und was mehr Bandbreite und Takte verbrät ...

Unregistered
2002-07-01, 08:41:44
Mal mit dem MSVC6 Processor Pack probiert? Afaik ist da der Compiler und der Heapmanager so erweitert worden, daß Alignments angegeben werden können. So spart man sich alle Handarbeit *g*

Pitchfork

zeckensack
2002-07-02, 00:54:51
Originally posted by Unregistered
Mal mit dem MSVC6 Processor Pack probiert? Afaik ist da der Compiler und der Heapmanager so erweitert worden, daß Alignments angegeben werden können. So spart man sich alle Handarbeit *g*

Pitchfork Sorry für die späte Antwort ...

Ich habe alle Updates drauf.
Service Pack 5 und auch das Processor Pack, vielleicht doppelt gemoppelt, aber viel hilft viel =).
Wie gesagt, bei mir macht das Teil einfach nichts sinnvolles.

Werde demnächst sowieso komplett auf GCC umsteigen, mal schauen was der so macht ...
Mir geht nämlich die MS-Update-Politik auf den Sack: VC.net gibt's auch einzeln, 'braucht' aber WinXP, kost wieder 'nen Haufen Schotter, obwohl's eigentlich ein kostenloser Bugfix sein sollte und kann wahrscheinlich nicht mal optimieren ...

Visual Studio .NET enthält VC.NET, wird aber paradoxerweise auch auf Win98 unterstützt *grrr*
Für knackige 2000€ ist man dabei :jedifire:

zeckensack
2002-07-02, 18:34:26
Originally posted by zeckensack
Geil! Compiliert zumindest ohne Fehler :)
Muß das später mal ausgiebig testen, ob's auch das macht, was ich will. Also heute abend erstmal Memory-Manager proggen. Juppi! :jump1:
'Heute abend' war wohl etwas zu voreilig, aber jetzt hab' ich's gemacht ... was soll ich sagen ...

Klappt wunderbar! Der Memory-Manager hat mich 'ne halbe Stunde gekostet, geht ja noch einigermaßen ;)
Placement new ... die beste Erfindung seit dem geschnittenen Brot. Vielen Dank nochmal :)