[Gelöst] Empty Base Class Optimisation
-
Janjan schrieb:
Speed schrieb:
Janjan schrieb:
Nein, dies kann nicht der Fall sein. In Unions wird garantiert, dass _ALLE_ Elemente an der selben Speicherstelle beginnen.
Beginnen , Ja aber nicht Enden.
Das ergibt auch keinen Sinn.
union Foo { char a; int b; };
a und beginnen jeweils an der selben Stelle im Speicher, aber a endet logischerweise 3 bytes früher. Das Union hat die Größe von b, da es das größte Element ist.
ja, aber was wenn die Elemente als Synonyme, verwendet werden.
zB konnte im oben genannten Beispiel der Operator[] überladen werden.
-
@ Speed:
Bist du eigentlich sicher, dass duunion
brauchst? Damit handelt man sich nämlich genau solche Probleme ein. Überhaupt ist das Verhalten undefiniert, wenn du von einem anderen Member liest, als dem zuletzt beschriebenen. In C++ benötigt manunion
eigentlich recht selten.Ansonsten hat das Problem weniger mit C++ zu tun als mit deinem Compiler. Womöglich gibts eine Einstellung fürs Padding von Klassen.
-
Ich habe das Gefühl du missbrauchst Unions ganz extrem...
Alle Elemente teilen sich natürlich nicht den gesamten Speicherraum des Unions - das geht auch logischerweise nicht. Ein Datentyp char kann ja nicht plötzlich 20 byte groß sein, nur weil das Union 20 byte groß ist. Alle Member starten lediglich an der selben Adresse.
-
Nexus schrieb:
Ansonsten hat das Problem weniger mit C++ zu tun als mit deinem Compiler.
Ok.
-
Dieser Thread wurde von Moderator/in volkard aus dem Forum C++ in das Forum Compiler- und IDE-Forum verschoben.
Im Zweifelsfall bitte auch folgende Hinweise beachten:
C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?Dieses Posting wurde automatisch erzeugt.
-
Nexus schrieb:
@ Speed:
Bist du eigentlich sicher, dass duunion
brauchst? Damit handelt man sich nämlich genau solche Probleme ein. Überhaupt ist das Verhalten undefiniert, wenn du von einem anderen Member liest, als dem zuletzt beschriebenen. In C++ benötigt manunion
eigentlich recht selten.Ansonsten hat das Problem weniger mit C++ zu tun als mit deinem Compiler. Womöglich gibts eine Einstellung fürs Padding von Klassen.
Ich habe meine Gründe, diese Konstruktion zu verwenden, bin sogar gezwungen diese zu nutzen. Das Verhalten ist Definiert und analysiert man meine Klassen, kann man das verhalten nachvollziehen,selbst wenn die Member unerwartete Größen Besitzen.
Die notwendigkeit,ergab sich aus der Praxis und den Performance-ansprüchen und ist auch in vielen anderen Mathe-libs zu beobachten.
Beispiele kann ich, bei nachfrage Später, hier posten aber sinn und unsinn von Unions sind hier offtopic
-
Deine Erwartung das alle Member-Variablen den kompletten Speicherbereich eines Unions verwenden ist unsinnig.
Jede Variable benutzt den Speicher entsprechend ihrer Größe, nicht mehr, nicht weniger. Nur das alle Variablen an der selben Speicherstelle beginnen ist garantiert.
-
Janjan schrieb:
Ein Datentyp char kann ja nicht plötzlich 20 byte groß sein, nur weil das Union 20 byte groß ist. Alle Member starten lediglich an der selben Adresse.
Sollen sie auch nicht.
aber wie siehts mit folgendem Beispiel aus:
class X
{
public:int B[2];
int C[2];};
union
{
int A[4];
X B;
};
-
Undefiniertes Verhalten, da es nicht festgelegt ist, dass alle Member von class in einer Reihe im Speicher liegen. Dein Compiler kann Padding adden, wenn er es als nötig erachtet.
-
Janjan schrieb:
Undefiniertes Verhalten, da es nicht festgelegt ist, dass alle Member von class in einer Reihe im Speicher liegen. Dein Compiler kann Padding adden, wenn er es als nötig erachtet.
zwischen ints? extrem unrealistisch. das scheint mir jetzt paragraphenreiterei zu sein.
-
volkard schrieb:
Janjan schrieb:
Undefiniertes Verhalten, da es nicht festgelegt ist, dass alle Member von class in einer Reihe im Speicher liegen. Dein Compiler kann Padding adden, wenn er es als nötig erachtet.
zwischen ints? extrem unrealistisch. das scheint mir jetzt paragraphenreiterei zu sein.
Es ist unwahrscheinlich, das stimmt. Aber so ist es.
Wenn er ne komplexere Klasse hat (was höchstwahrscheinlich der Fall ist), dann trifft es zu.
Aber in seinem Beispiel wäre es festgelegt, dass X und int an der selben Stelle liegen. Wie die interne Struktur von X ist, ist ja eine vollkommene andere Sache.
-
Janjan schrieb:
volkard schrieb:
Janjan schrieb:
Undefiniertes Verhalten, da es nicht festgelegt ist, dass alle Member von class in einer Reihe im Speicher liegen. Dein Compiler kann Padding adden, wenn er es als nötig erachtet.
zwischen ints? extrem unrealistisch. das scheint mir jetzt paragraphenreiterei zu sein.
Es ist unwahrscheinlich, das stimmt. Aber so ist es.
Wenn er ne komplexere Klasse hat (was höchstwahrscheinlich der Fall ist), dann trifft es zu.
Aber in seinem Beispiel wäre es festgelegt, dass X und int an der selben Stelle liegen. Wie die interne Struktur von X ist, ist ja eine vollkommene andere Sache.
Es ist festgelegt wann sich wo die Member einer klasse, sich im Speicher befinden.
Und genau dass nutzt man aus, und um undefiniertes verhalten auszuschließen dürfen auch nur PODs verwendet werden. Virtuell , Zeiger etc sind in Unions verboten.Aber, Ich Zitiere :
volkard schrieb:
Warum steht da einmal 40, wo man doch 36 erwarten würde?
-
Bist du dir sicher? Gerade wegen Paddingsachen.
struct Fubar { char a; int b; char c; };
Die größe dieser Struktur ist in der Regel 12 bytes, denn es wird zu folgendem:
struct Fubar { char a; char p1[3]; int b; char c; char p2[3]; };
Man beachte _wo_ die Paddings sich befinden.
-
Speed schrieb:
Es ist festgelegt wann sich wo die Member einer klasse, sich im Speicher befinden.
Falsch.
C++-Standard 9.2/12 schrieb:
Nonstatic data members of a (non-union) class declared without an intervening access-specifier are allocated so that later members have higher addresses within a class object. The order of allocation of nonstatic data members separated by an access-specifier is unspecified (11.1). Implementation alignment requirements might cause two adjacent members not to be allocated immediately after each other; so might requirements for space for managing virtual functions (10.3) and virtual base classes (10.1).
Speed schrieb:
Und genau dass nutzt man aus
Ja, und genau hier liegt auch das Problem. Gerade wenn man sich auf so tiefer Abstraktionsebene bewegt, schadet es nicht, mit den Garantien des Standards und den Compilereigenheiten etwas vertraut zu sein.
Für deinen Fall kannst du versuchen, compilerabhängige Flags betreffend Padding zu setzen. Ich kenne mich mit g++ leider nicht so gut aus. Vielleicht ist der Compiler unter gewissen Umständen jedoch tatsächlich nicht in der Lage, die EBO (Empty Base Optimization) durchzuführen, sodass eine abgeleitete Klasse schlussendlich mehr Speicher als eigentlich erforderlich benötigt.
-
Janjan schrieb:
Bist du dir sicher? Gerade wegen Paddingsachen.
struct Fubar { char a; int b; char c; };
Die größe dieser Struktur ist in der Regel 12 bytes, denn es wird zu folgendem:
struct Fubar { char a; char p1[3]; int b; char c; char p2[3]; };
Man beachte _wo_ die Paddings sich befinden.
Paddings, hab ich beim Argumentieren vergessen...
Aber ist mein Problem wirklich dadurch erklärbar, wenn man beachtet das es sich um ints(die stehts einen Speicherblock ausfüllen) handelt?
In meinem Fall, besteht eine Klasse stehts aus einem Member der einen Speicherblock komplett ausfüllt(ints sind stehts plattform abhängig, char ist immer ein byte) und hat keine virtuellen vorfahren und funktionen die gemenaged werden müssen.Sei es doch der Fall, dann wär es mir umso wichtiger, wieso der effect nicht bei B1 und B2,auftritt, zu wissen.Auch interessant ist es zu erfahren ob es auch bei, anderen Compilern auftritt.
Edit:
union X { char a[sizeof(int)]; int b; };
-
Ich zitiere mal Nathan Myers:
Nathan Myers schrieb:
Update again: A whole family of related "empty subobject" optimizations are possible, subject to the ABI specifications a compiler must observe. (Jason Merrill pointed some of these out to me, years back.) For example, consider three struct members of (empty) types A, B, and C, and a fourth non-empty. They may, conformingly, all occupy the same address, as long as they don't have any bases in common with one another or with the containing class. A common gotcha in practice is to have the first (or only) member of a class derived from the same empty base as the class. The compiler has to insert padding so that they two subobjects have different addresses. This actually occurs in iterator adapters that have an interator member, both derived from std::iterator. An incautiously-implemented standard std::reverse_iterator might exhibit this problem.
Vor allem er kursive Teil dürfte interessant für Dich sein.
Den ganzen Text findest Du hier.
-
Tachyon schrieb:
Ich zitiere mal Nathan Myers:
Nathan Myers schrieb:
Update again: A whole family of related "empty subobject" optimizations are possible, subject to the ABI specifications a compiler must observe. (Jason Merrill pointed some of these out to me, years back.) For example, consider three struct members of (empty) types A, B, and C, and a fourth non-empty. They may, conformingly, all occupy the same address, as long as they don't have any bases in common with one another or with the containing class. A common gotcha in practice is to have the first (or only) member of a class derived from the same empty base as the class. The compiler has to insert padding so that they two subobjects have different addresses. This actually occurs in iterator adapters that have an interator member, both derived from std::iterator. An incautiously-implemented standard std::reverse_iterator might exhibit this problem.
Vor allem er kursive Teil dürfte interessant für Dich sein.
Den ganzen Text findest Du hier.Vielen Dank, hat mir weitergeholfen!
MFG Speed
-
BTW: VC liefert hier dreimal 36...
-
Hab ein bisschen rumprobiert und wiedermal hat mich g++ überrascht.
Wie wir festgestellt haben, werden 4Byte extra verbraucht, wenn beide Klassen vorfahren haben, richtig?
Der Nachfolgende Code beweist das es außerdem notwendig ist, dass es die selbe Klasse sein muss.
Wiso macht es einen unterschied, ob es die selbe Klasse ist?#include <iostream> class A1{}; class A2{}; class B3:A1{ int A[3]; }; class C3:A1{ B3 A[3];}; class C4:A2{ B3 A[3];}; int main() { std::cout<<" "<<sizeof(C3) <<" "<<sizeof(C4); //output[g++4.4.3]: 40 36 }
//Edit urghh steht ja bereits im Zitiertem Text ;P
-
Eine kleine Frage, was erhoffst du von deinen Versuchen? :o