2009-10-06 6 views
19

Gibt es eine portable Weise zu bestimmen, was die maximal mögliche Ausrichtung für jeder Typ ist? Zum Beispiel erfordern auf x86 SSE-Anweisungen eine 16-Byte-Ausrichtung, aber soweit ich weiß, erfordern keine Anweisungen mehr als das, so dass jeder Typ sicher in einem 16-Byte-ausgerichteten Puffer gespeichert werden kann.Bestimmung der maximal möglichen Ausrichtung in C++

Ich muss einen Puffer (z. B. ein Char-Array) erstellen, wo ich Objekte beliebiger Typen schreiben kann, und so muss ich in der Lage sein, auf den Anfang des Puffers auszurichten.

Wenn alle Stricke reißen, ich weiß, dass ein char-Array mit new Zuteilung garantiert maximale Ausrichtung haben, aber mit der TR1/C++ 0x-Vorlagen alignment_of und aligned_storage, ich frage mich, ob es möglich wäre, das zu schaffen Puffer in-Place in meiner Pufferklasse, anstatt die zusätzliche Zeigerindirektion eines dynamisch zugewiesenen Arrays zu erfordern.

Ideen?

Ich weiß, es gibt viele Möglichkeiten zur Bestimmung der maximalen Ausrichtung für eine begrenzte Reihe von Typen: Eine Union, oder nur alignment_of von TR1, aber mein Problem ist, dass die Menge der Typen unbegrenzt ist. Ich weiß nicht im Voraus, welche Objekte im Puffer gespeichert werden müssen.

+0

portable in welcher Hinsicht genau sein? zu jedem Compiler? zu jedem Betriebssystem? zu jeder Architektur? –

+0

Nur portabel wie in "garantiert vom C++ Standard zu arbeiten". Natürlich könnte ich mich leicht auf meine eigenen Kenntnisse der Zielarchitektur verlassen und die maximale Ausrichtung fest codieren, aber es wäre schön, wenn die Sprache selbst die Werkzeuge zur Verfügung stellen würde, um dies zu beantworten. – jalf

+3

Beachten Sie, dass der Template-Parameter 'Align' von' std :: aligned_storage 'ein Standardargument von" default-alignment "hat, das definiert ist als" Der Wert von default-alignment soll die strengste Alignment-Anforderung für jedes C++ Objekt sein Typ, dessen Größe nicht größer als "Len" ist. " Ich weiß nicht, ob SSE-Typen als "C++ - Objekttypen" betrachtet werden, und die VC10-Standardbibliothek hat nicht das Standardargument, daher weiß ich nicht, was der beabsichtigte Wert ist (ich habe keine andere Standardbibliothek Implementierungen auf dieser Maschine). –

Antwort

9

In C++ 0x, die Align Template-Parameter von std::aligned_storage<Len, Align> haben ein Standard-Argument der "default-Ausrichtung", die als (N3225 §20.7.6.6 Tabelle 56) definiert ist:

Der Wert von Die Standardausrichtung muss die höchste Ausrichtungsanforderung für jeden C++ - Objekttyp sein, dessen Größe nicht größer als Len ist.

Es ist nicht klar, ob SSE-Typen als "C++ - Objekttypen" betrachtet werden.

Das Standardargument war nicht Teil des TR1 aligned_storage; Es wurde für C++ 0x hinzugefügt.

5

Kurz von einigen maximally_aligned_t Typ, dass alle Compiler getreu versprochen, für alle Architekturen überall zu unterstützen, sehe ich nicht, wie dies zur Kompilierzeit gelöst werden konnte. Wie Sie sagen, ist die Menge der möglichen Typen unbegrenzt. Ist die Extra-Zeiger-Indirektion wirklich so groß?

+0

Es mag nicht sein, aber ich bin neugierig, ob es eine Lösung gibt. C++ 0x fügt einige andere alignment-related Funktionen hinzu, und die Implementierung muss bereits die maximal mögliche Ausrichtung in anderen Fällen bestimmen (wenn ein char-Array dynamisch zugewiesen wird), also dachte ich, dass es eine obskure Standard-Bibliotheksvorlage geben könnte Wert. – jalf

+0

Ja. Es ist eine interessante Frage, und ich wünschte, ich hätte eine bessere Antwort für Sie, aber ich glaube nicht, dass es eine normkonforme Methode gibt. maximal_ausgerichtet_t (oder besser, maximale_Ausrichtung) wäre jedoch nicht schwer zu implementieren; vielleicht sollten Sie es für C++ 1x vorschlagen :) –

1

Zuweisung von Speicher ausgerichtet ist schwieriger, als es aussieht - siehe zum Beispiel Implementation of aligned memory allocation

+0

Ich weiß, es ist schwierig. Das war nicht meine Frage. ;) Aber der Standard gibt einige Garantien, und besonders, wenn Sie C++ 0x berücksichtigen, haben Sie ein paar * Standard * -Werkzeuge, um zu helfen. – jalf

+1

Die Schwierigkeit gilt nicht für Jalf, weil er keinen allgemeinen Zuweiser macht. Alles, was er benötigt, ist, zusätzlichen Platz in seinem Puffer zu haben und den In-Buffer-Zeiger auf den nächsten gewünschten Ausrichtungsblock zu runden. – Potatoswatter

5

Leider max Ausrichtung gewährleistet ist viel härter als es sein sollte, und es gibt keine Garantie Lösungen AFAIK. Vom GotW Blog (Fast Pimpl article):

union max_align { 
    short  dummy0; 
    long  dummy1; 
    double  dummy2; 
    long double dummy3; 
    void*  dummy4; 
    /*...and pointers to functions, pointers to 
     member functions, pointers to member data, 
     pointers to classes, eye of newt, ...*/ 
}; 

union { 
    max_align m; 
    char x_[sizeofx]; 
}; 

Dies nicht als vollständig tragbare garantiert ist, aber es ist nah genug, weil es nur wenige oder keine Systeme, auf denen dies nicht funktioniert in der Praxis als erwartet.

Das ist über den nächsten "Hack", den ich dafür kenne.

Es gibt einen anderen Ansatz, den ich persönlich für super schnelle Zuweisung verwendet habe. Beachten Sie, dass es böse ist, aber ich arbeite in Raytracing-Bereichen, in denen Geschwindigkeit einer der größten Qualitätsmaßstäbe ist und wir den Code täglich grafisch darstellen. Dies beinhaltet die Verwendung eines Heap-Allokators mit vor-allokiertem Speicher, der wie der lokale Stapel funktioniert (erhöht nur einen Zeiger auf die Zuweisung und dekrementiert einen um die Freigabe).

Ich verwende es für Pimpls besonders. Es reicht jedoch nicht, den Allokator zu haben. Damit ein solcher Allokator funktionieren kann, müssen wir annehmen, dass Speicher für eine Klasse, Foo, in einem Konstruktor zugewiesen wird, der gleiche Speicher ebenfalls nur im Destruktor freigegeben wird und dass Foo selbst auf dem Stapel erstellt wird. Um es sicher zu machen, brauchte ich eine Funktion, um zu sehen, ob der "This" -Zeiger einer Klasse auf dem lokalen Stack ist, um festzustellen, ob wir unseren super schnellen haufenbasierten Stack Allocator verwenden können.Dazu mussten wir OS-spezifische Lösungen recherchieren: Ich verwendete TIBs und 10 für Win32/Win64, und meine Mitarbeiter fanden Lösungen für Linux und Mac OS X.

Das Ergebnis, nach einer Woche der Erforschung von OS-spezifischen Methoden zum Erkennen von Stack-Bereich, Ausrichtung Anforderungen, und eine Menge von Tests und Profilerstellung, war ein Allokator, der Speicher in 4 Taktzyklen nach unseren Tick Counter Benchmarks im Gegensatz zu etwa 400 Zyklen für Malloc/Operator neu zuordnen könnte (unser Test beteiligt Threadkonflikt, so dass malloc in Singlethread-Fällen wahrscheinlich ein wenig schneller ist, vielleicht ein paar hundert Zyklen). Wir fügten einen Pro-Thread-Heap-Stack hinzu und erkannten, welcher Thread verwendet wurde, was die Zeit auf ungefähr 12 Zyklen erhöhte, obwohl der Client den Thread-Allokator verfolgen kann, um die 4-Zyklen-Zuordnungen zu erhalten. Es löschte Speicherzuweisung basierte Hotspots von der Karte.

Während Sie nicht durch all diese Schwierigkeiten gehen müssen, könnte das Schreiben eines schnellen Allokators einfacher und allgemeiner anwendbar sein (z. B. die Menge des zuzuweisenden Speicherplatzes zur Laufzeit bestimmen) als etwas wie max_align Hier. max_align ist einfach genug zu verwenden, aber wenn Sie Geschwindigkeit für Speicherzuweisungen sind (und vorausgesetzt, Sie haben bereits Ihren Code profiliert und gefunden Hotspots in malloc/free/operator new/delete mit wichtigen Mitwirkenden in Code haben Sie die Kontrolle über) Wenn Sie Ihren eigenen Zuordner schreiben, kann das den Unterschied ausmachen.

+0

+1. Wow, 100 mal schnellere Zuteilung. Danke für das Teilen dieser Informationen. –

+1

Hmm, das ist sehr interessant. Aber ich habe nicht wirklich nach einer schnellen Zuteilung gefragt (in meinem Fall ist das eigentlich ein relativ einfaches Problem zu lösen, weil ich nicht mit den üblen Fällen umgehen muss wie du - und ich muss sagen, ich bin beeindruckt du hast es funktioniert). Aber meine Frage war einfach sicherzustellen, dass Objekte an korrekt ausgerichteten Adressen zugewiesen werden. – jalf

+0

@jalf Ah, Entschuldigung, normalerweise denke ich, wenn man anfängt, in Ausrichtungsprobleme zu gehen und verschiedene Datentypen in einem einzigen Puffer zu speichern, ist es oft mit einem Speicherzuordner und Leistung im Hinterkopf. Ich fürchte, ich kenne keinen tragbaren Weg, um die maximale Ausrichtung für einen bestimmten Typ zu gewährleisten. Normalerweise musste ich in solchen Fällen ziemlich plattformspezifisch werden. Um zu versuchen, auf Nummer sicher zu gehen, habe ich oft Opt-in-Ansätze verwendet (ein Typ, dessen Ausrichtung unbekannt ist, wird auf Doppel-Vierwortgrenzen ausgerichtet, die maximal mögliche Ausrichtung, die mir bekannt ist). – stinky472

-2

Dies ist, was ich benutze. Wenn Sie Speicher zuweisen, wird außerdem ein neues() 'd-Array von Zeichen mit einer Länge größer oder gleich max_alignment an max_alignment ausgerichtet, sodass Sie dann Indizes in diesem Array verwenden können, um ausgerichtete Adressen zu erhalten.

enum { 
      max_alignment = boost::mpl::deref< 
       boost::mpl::max_element< 
         boost::mpl::vector< 
          boost::mpl::int_<boost::alignment_of<signed char>::value>::type, 
          boost::mpl::int_<boost::alignment_of<short int>::value>::type, 
          boost::mpl::int_<boost::alignment_of<int>::value>::type,        boost::mpl::int_<boost::alignment_of<long int>::value>::type, 
          boost::mpl::int_<boost::alignment_of<float>::value>::type, 
          boost::mpl::int_<boost::alignment_of<double>::value>::type, 
          boost::mpl::int_<boost::alignment_of<long double>::value>::type, 
          boost::mpl::int_<boost::alignment_of<void*>::value>::type 
         >::type 
        >::type 
       >::type::value 
      }; 
     } 
+0

Leider gewährleistet dies keine geeignete Ausrichtung für SSE-Typen. – jalf

9

In C++ 11 std :: max_align_t in header cstddef definiert ist ein POD-Typ, deren Anforderung der Ausrichtung zumindest so streng ist (so groß) wie die von jedem skalare Typ.

Mit dem neuen alignof Betreiber wäre es so einfach wie alignof(std::max_align_t)

+0

für Compiler, die alignof (dh MSVC11) nicht unterstützen, können Sie std :: alignment_of :: value –

+0

Das funktioniert gut, bis es [einen Fehler in der Standardbibliothek] (https: // github .com/cameron314/simultaneousqueue/issues/64): - / – Cameron