ausrichtung an 16byte grenze



  • hallo,

    was genau ist eigentlich eine "ausrichtung" an der 16byte grenze?? ich habe auf der seite:

    guckstu

    diesen befehl:

    int x __attribute__ ((aligned (16))) = 0;
    

    gefunden. im assembler forum (wo ich auf diese frage gekommen bin..) hiess es, ausrichtung an 16byte wäre wenn die adresse (in hex) mit 0 enden würde. das wird aber durch den befehl da oben nicht erreicht.. wenn ich mir die ausgabe meines testprogramms ansehe bringt der befehl da oben überhauptnix in der adressbildung der variable:

    0xbffffa68
    0xbffffa64
    0xbffffa60
    0xbffffa5c

    vielen dank im voraus

    gruss

    eviluser



  • std::cout << __alignof__ (x) << "\n";
    

    gibt weiterhin 4 aus.. (tut mir leid.. is ne etwas plumpe methode den thread zu beleben.. ich weiss)

    gruss

    eviluser



  • Tja, weiss nicht genau worauf du hinaus willst. Ausrichtung bedeutet einfach, dass die entsprechende Variable im Speicher an einer Grenze ausgerichtet wird, die einem ganzzahligen Vielfachen der Angabe entspricht. Gibst du halt zB 16 an, liefert die Adresse % 16 das Ergebnis 0. Vielleicht hat man ja das im Assembler Forum gemeint. Sinnvoll kann das zB für SSE eingesetzt werden, da dort bestimmte Befehle nur mit entsprechender Ausrichtung funktionieren, ansonsten gibts 'ne CPU Exception.



  • genau dafür brauche ichs.
    ich hab jetzt aus einer codesequenz die camper gepostet hat folgendes gebastelt:

    float *f = (float*) malloc(7*sizeof(float));
        f = (float*) (((unsigned)f)&-16);
    

    er hatte einen ähnlichen code.. nur dass es gleich nen 1000er block angelegt hat. ich brauche (für meine vektorklasse) aber nur 4 float variablen. um (mein system legt anscheinend immer brav mit 4byte abstand adressen an) an die nächste 0 zu kommen, reserviere ich also nen "7er" block, verändere meine startadresse zur nächsten 0 und verwende ab dort meine variable wie ein array... ich hätte lieber dieses memalign() oder devlspec() oder was auch immer verwendet.. aber solange das funktioniert.. ich verwende gcc2.95 vermutlich sind die meisten befehle die ich im netz finde für andere c-dialekte, da sie bei mir nicht funktionieren. das da oben ist die erste funktionierende methode.

    gruss

    eviluser



  • Wobei die Methode nicht gut ist, da du den "echten" von malloc zurückgelieferten Zeiger verwirfst. Du solltest aber in der Lage sein, angeforderten Speicher auch wieder freizugeben. Mal ganz abgesehen davon, dass man in C++ new/delete für dynamischen Speicher verwendet. Ich selber hatte mal das gleiche Problem (gibt hier irgendwo 'nen Thread) für 'ne 4x4 float Matrix Klasse. Hab aber letztendlich die Verwendung von SSE auf Eis gelegt, da die sauberste Lösung imo ein aligned_malloc ist. Dies ist aber nicht portabel und zudem auch nicht ganz unproblematisch, wenn man Klassen hat, wo mehrere Member überladene new/delete Operatoren benutzen.

    btw:
    gcc2.95? Da solltest du wirklich mal updaten, aktuell ist 3.4.x.



  • ok.. wie wärs dann mit dieser variante?

    float *v = new float[7], *backup = v;
    v = (float*) (((unsigned)v)&-16);
    

    um dann später im destruktor:

    delete [] backup;
    

    freizugeben.

    ich habe leider keine wahl 🙂 ich muss diese SSE klasse schreiben. eine schöne lösung wäre mir auch lieber. aber ein akzeptabler workaround, der möglichst lesbaren code hinterlässt ist mir auch recht.

    gruss

    eviluser



  • eviluser schrieb:

    ich habe leider keine wahl 🙂 ich muss diese SSE klasse schreiben

    Ich hab meine Matrix Klasse dann ganz normal gemacht und verlasse mich darauf, dass mein Compiler bei eingeschalteten SSE Erweiterungen genug optimiert.

    Irgendwie ist mir deine Funktion noch unsympatisch. Erstmal wandelst du von Zeiger nach int, und das kann auf unterschiedlichen Plattformen Probleme ergeben. Da wäre mir sowas wie ein pointer_to_int bzw. int_to_pointer Typ angenehmer. Auf einer normalen IA-32 Plattform wäre der Typ dann halt so definiert:

    typedef unsigned int pointer_to_int_type;
    

    Zudem glaube ich, dass hier

    (((unsigned)v)&-16
    

    ebenfalls noch ein Fehler liegt, da das Ergebnis eine Adresse sein kann, die vor dem eigentlichen Speicher liegt und dir somit nicht gehört. Evtl. sollte sowas helfen

    float* backup = new float[was_immer_du_brauchst];
    float* v = static_cast<float*>((pointer_to_int_type(backup) + 15) & -16);
    

    *ungetestet*

    Du solltest dann halt darauf achten, dass du mindestens 15 Bytes mehr reservierst, als du letztendlich brauchst.



  • hat das mit der 15 (bzw alignment-1) einen bestimmten grund?

    ich fuhrwerke gerade einbischen an dem gcc rum .. mit -msse schaltet man sse optimierung an?!? der generierte assembler code sieht für mich nicht so aus.

    gruss

    eviluser


  • Mod

    den original pointer der von new/malloc zurückgeliefert wird, darf man nat. nicht verwerfen 🙂

    im übrigen schreibt kein standard vor new statt malloc zu verwenden (beides hat seine berechtigung). ich hab ich in meinem code ganz bewusst malloc verwendet:

    es ist ja immer nötig, wenigstens 15 bytes mehr zu reservieren als gebraucht werden - mit new schafft man das nur, wenn der zugrundeliegende typ nur ein byte gross ist - in jedem falle muss man immer den speicherbedarf des typs kennen - bei malloc ist gleich klar, dass zunächst nur ein unstruktierter block reserviert wird, der dann erst über einen zweiten zeiger, der ausgerichtet ist, benutzbar wird.

    ich glaube mein code enthielt das +15 auch schon...

    ansonsten sind in allen implementationen, die speicherblöcke bereits wenigstens an 16 byte grenzen ausgerichtet, so dass man sich den aufwand ggf. sparen kann - es sei denn, es muss ganz sauber sein 🙂

    btw mit vc++/icc :

    __declspec(align(16)) sorgt automatisch für ausrichtung von variablen/funktionen - das klappt auch wenn die variable teil einer struktur/klasse ist - das attribut wird dann auf die umgebende struktur übertragen.

    es ist eigentlich nicht nötig nach int zu casten - char* oder size_t sollte genauso funktionieren



  • eviluser schrieb:

    hat das mit der 15 (bzw alignment-1) einen bestimmten grund?

    Ja. Bsp:

    00332ca8 // von new zurückgelieferte Adresse (gespeichert im Zeiger backup)
    & fffffff0 // -16
    = 00332ca0 // resultierende aligned Adresse (gespeichert im Zeiger v)
    

    Wie du siehst, gehört die Adresse nicht zu dem von new reservierten Bereich, da der erst ab 00332ca8 beginnt.
    Jetzt mit meiner Variante

    00332ca8 // von new zurückgelieferte Adresse (gespeichert im Zeiger backup)
    + 0000000f // + 15
    = 00332cb7
    & fffffff0 // -16
    = 00332cb0 // resultierende aligned Adresse (gespeichert im Zeiger v)
    

    Die Adresse liegt jetzt innerhalb des von new reservierten Bereiches (vorausgesetzt du hast genug Speicher reserviert 🙂 ).



  • camper schrieb:

    __declspec(align(16)) sorgt automatisch für ausrichtung von variablen/funktionen - das klappt auch wenn die variable teil einer struktur/klasse ist - das attribut wird dann auf die umgebende struktur übertragen.

    Das funktioniert aber nur, wenn die Struktur statisch instanziert wird. Bei dynamischer Instanzierung ist das reines Glück, wenn es funktioniert. Genau darüber bin ich nämlich damals gestolpert.



  • Hast Du mal posix_memalign() versucht(in der stdlib)?

    Solltest Du Compiler-Intrinsics für SSE verwenden, kannst Du auch mit _mm_loadu und _mm_storeu von nicht ausgerichteten Speicherstellen lesen, bzw. schreiben.(ist halt langsamer)

    Ausserdem wird der Datentyp __m128 immer 16-byte aligned angelegt;
    dann kannst Du mit

    union{
    __m128 sse_val;
    float float_val[4];
    }

    ein Array von 4 Floats(float_val) anlegen, die erstens an einer durch 16 teilbaren Speicherstellen beginnen, und zweitens kannst Du direkt mit sse_val rechnen.

    Gruß
    Michael


  • Mod

    groovemaster schrieb:

    camper schrieb:

    __declspec(align(16)) sorgt automatisch für ausrichtung von variablen/funktionen - das klappt auch wenn die variable teil einer struktur/klasse ist - das attribut wird dann auf die umgebende struktur übertragen.

    Das funktioniert aber nur, wenn die Struktur statisch instanziert wird. Bei dynamischer Instanzierung ist das reines Glück, wenn es funktioniert. Genau darüber bin ich nämlich damals gestolpert.

    auf dem stack klappts nicht (ausser mit icc), sehr wohl aber wenn das objekt sich auf dem heap befindet (und statisch geht ja sowieso).
    der __mm128 typ ist auch nur so definiert (xmmintrin.h)

    typedef struct __declspec(intrin_type) __declspec(align(16)) __m128 {
        float       m128_f32[4];
    } __m128;
    

    das ganze bleibt trotzdem compilerspezifisch, allgemein funktioniert, was vorher gepostet wurde.



  • hallo,

    mein gedanke bei der "konstruktion" war, wenn meine adressen an einer 4er grenze ausgerichtet sind, brauche ich bei jeder beliebigen adresse höchstens 3 "schritte" um zu einer adresse zu kommen die an der 16 ausgerichtet ist. also habe ich einfach speicherblöcke mit der grösse: (4+3)*sizeof(float) angelegt. das durch die berechnung die adresse vor den eigentlichen speicherblock gelegt werden konnte habe ich verschlafen 😮 (jetzt verstehe ich auch endlich was es mit der 15 auf sich hat). danke für eure hilfe 🙂

    gruss

    eviluser



  • camper schrieb:

    auf dem stack klappts nicht [...] und statisch geht ja sowieso

    Mit statisch meinte ich den Stack, dann nenn's halt automatisch.

    camper schrieb:

    sehr wohl aber wenn das objekt sich auf dem heap befindet

    Wie gesagt, das ist reines Glück sofern du kein aligned new/malloc verwendest.


Anmelden zum Antworten