aligned_storage / alignment_of
-
Moin,
Ich hatte Alignment bisher immer so verstanden, dass die Speicheradresse eines beispielsweise auf 8 Byte ausgerichteten Objekts durch acht teilbar sein muss. Ich war dementsprechend ziemlich überrascht, als MSVC 2008 ein anderes Verhalten an den Tag legte. Testcase wie folgt:
#include <cassert> #include <cstddef> #include <iostream> #include <type_traits> struct point { double x; double y; }; template<typename T, std::size_t N, std::size_t A = std::tr1::alignment_of<T>::value> class stack_container { public: stack_container() : storage_(local_storage()), size_(0) { assert(reinterpret_cast<std::size_t>(storage_) % A == 0); } private: inline T *local_storage() { return static_cast<T*>(static_cast<void*>(&local_storage_)); } std::tr1::aligned_storage<sizeof(T[N]), A> local_storage_; T *storage_; std::size_t size_; }; int main() { char c; stack_container<point, 100> f; }
Mit gcc läuft das durch, MSVC 2008 packt f.local_storage_ aber an eine nicht durch 8, sondern lediglich durch 4 teilbare Position. Der Code läuft trotzdem und macht was er soll, aber ich kann so ohne weiteres nicht sehen, ob es dadurch Performanceeinbußen gibt, und ich möchte natürlich ungern undefiniertes Verhalten erzeugen.
Habe ich missverstanden, was Alignment bedeutet, mache ich im Code einen Fehler, oder ist das ein MSVC 2008-Bug?
-
[unsinn][/unsinn]
-
Ich bin zwar kein Fachmann bezüglich Alignment, aber folgendes
könnte dir evtl. weiterhelfen:wikipedia schrieb:
Typischerweise erfolgt eine byteweise Adressierung des Arbeitsspeichers, und es kann auf eine Folge von m Bytes in einem Takt zugegriffen werden. Die Anzahl m ist die Breite des Datenbusses in Bytes. Übliche Datenbusbreiten sind 16 Bit (m = 2), 32 Bit (m = 4) und 64 Bit (m = 8).
http://de.wikipedia.org/wiki/Speicherausrichtung
Wieviel Bit hat dein System? 32 oder 64?
Gruß,
XSpille
-
Ich Vollhorst hab doch tr1::aligned_storage statt tr1::aligned_storage::type genommen. Der Testcase oben ist hinfällig, allerdings tritt das Problem gelegentlich immer noch auf. Folgendes:
#include <cassert> #include <cstddef> #include <iostream> #include <type_traits> struct point { double x; double y; }; template<typename T, std::size_t N, std::size_t A = std::tr1::alignment_of<T>::value> class stack_container { public: stack_container() : storage_(local_storage()), size_(0) { assert(reinterpret_cast<std::size_t>(storage_) % A == 0); } private: inline T *local_storage() { return static_cast<T*>(static_cast<void*>(&local_storage_)); } typename std::tr1::aligned_storage<sizeof(T[N]), A>::type local_storage_; T *storage_; std::size_t size_; }; void foo(int, int) { stack_container<point, 100> f; } int main() { int x = 0; foo(x, 2); }
frisst er nicht. Ich bin nicht sicher, was den Fehler auslöst - übergebe ich etwa statt x zwei Integerkonstanten an foo, tritt der Fehler nicht auf. Der gcc frisst es, das kann aber natürlich auch Zufall sein.
Ich produziere den Fehler hier mit MSVC 2008 auf WinXP 64 bit, aber es tritt unabhängig davon auf, ob ich für 32- oder 64-bit-Windows kompiliere.
-
Ich kann dieses Verhalten eigentlich nur unter 32-bit reproduzieren. Falls sonst niemandem ein Grund dafür einfällt müsste man überlegen ob dies ein Fehler im Compiler oder nur eine "Ungenauigkeit" ist. Das Beispiel
template<class T> struct Dummy { char _dummy; T _value; }; size_t aligmentDbl = sizeof(Dummy<double>) - sizeof(double);
zeigt, dass double in beiden Systemen (32 und 64 bit) ein 8-Byte alignment haben sollte.
Jetzt wissen wir aber, dass x86 keine strenge RISC Architektur ist und daher immer auf alle Speicheradressen direkt zugreifen kann. Darum könnte der Codegenerator für Stack-Variablen nur 32-bit alignments auch für 64-bit Typen benutzen, während der template-Mechanismus zur Feststellung in Datenstrukturen mit 64-bit einen "überzogenen" Wert angibt. Dieser "Fehler" würde sich dann aber nicht als Problem äußern. Unter 64-bit konnte ich das Problem auch nicht nachweisen (dort war der Stack 64-bit ausgerichtet), nur unter 32-bit - wo vielleicht gar kein alignment-Zwang notwendig ist.
Umgekehrt (CPU braucht alignment - Compiler ignoriert es) wäre es viel schlimmer ... denn dann würde eine Alignment-Exception von der Hardware her kommen.
Ich selbst hatte auf x86 nie Schwierigkeiten mit dem alignment, weil die Architektur keine Probleme damit hat. Erst beim Portieren auf ARM bin ich darauf gestoßen und benutze seither den oben stehenden Trick zur Feststellung bisher ohne Probleme.
lg XOR
PS:
Habe das Problem mit folgendem Code nachvollzogen, falls jemand Fehler findet, bitte melden - das Thema interessiert mich sehrstruct AlignmentException { }; template<class T> void check_alignment(T const & t) { struct Dummy { char _dummy; T _value; }; size_t myAlignment = sizeof(Dummy) - sizeof(T); size_t memaddr = reinterpret_cast<size_t>(&t); if(memaddr % myAlignment) { throw(AlignmentException()); } } ... ... char chr = 1; double dbl = 12; check_alignment(chr); check_alignment(dbl);
-
Siehe auch:
http://social.msdn.microsoft.com/Forums/de-DE/visualcplusde/thread/c73c2f1b-4b2f-4180-8291-e65463dae52a