Padding Bytes in Byte struct



  • @Wutz Naja also zumindest praktisch kann man sich schon darauf verlassen. Falls du anderer Meinung bist, wäre interessant wieso.



  • @hustbaer
    Weil der Standard das nicht garantiert.



  • @hustbaer sagte in Padding Bytes in Byte struct:

    Falls du anderer Meinung bist, wäre interessant wieso.

    Z.B. kann nicht jeder Prozessor einfach auf jedes Byte eines Datenwortes zugreifen.
    Da kann es günstiger (Laufzeit) sein, die Bytes auf Wortadressen zu legen.
    (als Datenwort sind hier Vielfache von einem Byte gemeint)

    Aber das wäre ein reales Beispiel, damit schlägt sich der Standard nicht rum.



  • @hustbaer sagte in Padding Bytes in Byte struct:

    @Wutz Naja also zumindest praktisch kann man sich schon darauf verlassen. Falls du anderer Meinung bist, wäre interessant wieso.

    Nein, man kann eben nicht. Das sind so Klassiker wenn Programme von einer Plattform auf eine andere portiert werden, an denen es knallt.



  • @DirkB sagte in Padding Bytes in Byte struct:

    Z.B. kann nicht jeder Prozessor einfach auf jedes Byte eines Datenwortes zugreifen.

    es soll prozessoren geben, die nur 16bit-speicherzugriffe können. zugriff auf eine ungerade adresse löst eine hardware exception aus.


  • Mod

    Der einzig sinnvolle Hintergrund der Frage kann ja wohl nur sein, dass der Threadersteller eine wie auch immer geartete Umwandlung vor hat, bei der Layoutkompatibilität von Nöten ist¹. Wenn man solche Pläne hat, muss man sich sowieso mit den Eigenschaften der unterliegenden Plattform auskennen und schreibt keinen portablen Code mehr. Und dann ist das gezeigte Vorhaben eine durchaus gute Vorgehensweise, da die nötige Eigenschaft im allgemeinen Fall fast immer gegeben ist, im speziellen Fall auch leicht überprüfbar ist, und der nötige Code recht einfach zu implementieren ist.

    ¹: Die wahre Frage ist natürlich: Warum? Was manni66 ja auch prompt gefragt hat, aber leider unbeantwortet geblieben ist.


  • Mod

    @Bushmaster sagte in Padding Bytes in Byte struct:

    @DirkB sagte in Padding Bytes in Byte struct:

    Z.B. kann nicht jeder Prozessor einfach auf jedes Byte eines Datenwortes zugreifen.

    es soll prozessoren geben, die nur 16bit-speicherzugriffe können. zugriff auf eine ungerade adresse löst eine hardware exception aus.

    Ist doch egal, denn das gilt dann ja auch für das Array.



  • Der Standard garantiert diesbezüglich nur (implizit), dass es nie führende Paddingbytes gibt.



  • Das ist mir alles klar. Allerdings, AFAIK:

    • Padding ist nicht zwischen Array-Elementen erlaubt
    • uint8_t muss, wenn es existiert, genau 8 bit gross sein, ebenfalls ohne Padding oder "unbenutzte" Bits
    • char muss mindestens 8 Bit gross sein
    • sizeof(char) ist garantiert 1

    Daraus folgt dann implizit z.B. dass sizeof(uint8_t) und alignof(uint8_t) beide ebenfalls 1 sind.

    Wo ich mir nicht sicher bin ist ob der Compiler Padding "einfach so" einfügen darf, also auch wenn es nicht nötig wäre um das Alignment-Requirement zu erfüllen.

    Wenn er das darf, dann ist es vom Standard aus nicht garantiert, ja.

    Ist es in der Praxis relevant? Weiss ich nicht. Wenn man sich darauf verlässt sollte man vermutlich mindestens irgend einen Test haben der sicherstellt dass es auch funktioniert. Oder halt ein uint8_t Array verwenden wie ja schon vorgeschlagen wurde.



  • ich nehme an, wenn man eine struktur aus lauter chars hat, dann müsste sizeof die anzahl der elemente ergeben. kommt was größeres raus, hat der compiler was angehängt.



  • @hustbaer Die Anforderung no padding gilt für den Typ uint8_t an sich. Hat nichts damit zu tun was zwischen struct-members freigelassen werden darf.



  • @Swordfish sagte in Padding Bytes in Byte struct:

    @hustbaer Die Anforderung no padding gilt für den Typ uint8_t an sich. Hat nichts damit zu tun was zwischen struct-members freigelassen werden darf.

    Ja, das verstehe ich schon.
    Aber es gibt auch die Anforderung dass Arrays "dicht" sein müssen.
    Und aus beiden Anforderungen zusammengenommen ergibt sich dann dass uint8_t Arrays vollständig "dicht" sein müssen.

    Oder was meinst du? Ich verstehe nämlich gerade nicht ganz warum du mich darauf hinweist.

    ps: Dass man die Garantie für Arrays nicht unbedingt auf structs ausweiten darf ist mir auch klar. Hatte ich denke ich auch schon vorhin geschrieben. Wo ich mir aber nicht sicher bin ist ob der Standard so formuliert ist dass der Compiler unnötiges Padding einfügen darf.



  • @hustbaer sagte in Padding Bytes in Byte struct:

    dass der Compiler unnötiges Padding einfügen darf.

    Wo?



  • @Bushmaster sagte in Padding Bytes in Byte struct:

    es soll prozessoren geben, die nur 16bit-speicherzugriffe können. zugriff auf eine ungerade adresse löst eine hardware exception aus.

    Aber auch da kommt man - mit etwas Aufwand - an die Daten.
    Wird der Aufwand gescheut, gibt es Padding.



  • @DirkB sagte in Padding Bytes in Byte struct:

    Aber auch da kommt man - mit etwas Aufwand - an die Daten.

    ja, besonders schreibzugriffe auf uint8_t sind von übel, weil zuerst das 16bit-wort gelesen werden muss, weil man ja die eine hälfte nicht zerstören will.



  • @Swordfish sagte in Padding Bytes in Byte struct:

    @hustbaer sagte in Padding Bytes in Byte struct:

    dass der Compiler unnötiges Padding einfügen darf.

    Wo?

    Zwischen struct/class Membern.
    Ich weiss nicht wie das im Standard genau formuliert ist, hab nicht nachgelesen. Eine "nicht-Standard" Quelle die ich auf die Schnelle gefunden habe hat es formuliert ala "may insert padding to meet the alignment requirements". Das wäre eine schwammige Formulierung die es weder explizit erlaubt noch explizit verbietet.



  • @Bushmaster sagte in Padding Bytes in Byte struct:

    @DirkB sagte in Padding Bytes in Byte struct:

    Aber auch da kommt man - mit etwas Aufwand - an die Daten.

    ja, besonders schreibzugriffe auf uint8_t sind von übel, weil zuerst das 16bit-wort gelesen werden muss, weil man ja die eine hälfte nicht zerstören will.

    Auf solchen Systemen darf es - wenn ich das alles richtig verstehe - sowieso kein uint8_t geben. Wegen memory model und so.
    Denn Schreib- und Lesezugriffe auf myUint8Array[0] in einem Thread dürfen mit Schreib- und Lesezugriffen auf myUint8Array[1] in einem anderen Thread nicht kollidieren.
    Zumindest ist das in C++ so, bitte korrigiert mich wenn das in C anders ist.

    Und das lässt sich einfach nur vernünftig machen wenn die CPU auch einzelne Bytes direkt schreiben kann. Wenn dazu mehrere Befehle nötig sind, kenne ich nur zwei Möglichkeiten das umzusetzen, und beide sind nicht "vernünftig":

    1. Atomics - viel zu teuer
    2. Hardware Transactional Memory - ebenfalls teuer und auf kaum einer CPU implementiert

    Falls ich hier nen Denkfehler habe, bitte ich ebenfalls um Korrektur.

    EDIT: OK, Annahme hier natürlich: das Ding unterstützt überhaupt echtes Multithreading. D.h. das Argument gilt natürlich nur für Systeme mit Multithreading. Hm. Schade 🙂



  • @hustbaer sagte in Padding Bytes in Byte struct:

    Auf solchen Systemen darf es - wenn ich das alles richtig verstehe - sowieso kein uint8_t geben. Wegen memory model und so.

    das könnte ja die cpu übernehmen.
    angenommen byte-adresse 123 soll geschrieben werden. dann lädt die cpu das 16-bit-wort von word-adresse 61 ändert das low-byte und schreibt das wort wieder in den speicher. für den asm-programmierer ist das eine atomare operation, die bloß mehr taktzyklen braucht als ein 16-bit-schreibzugriff.



  • @Bushmaster sagte in Padding Bytes in Byte struct:

    @hustbaer sagte in Padding Bytes in Byte struct:

    Auf solchen Systemen darf es - wenn ich das alles richtig verstehe - sowieso kein uint8_t geben. Wegen memory model und so.

    das könnte ja die cpu übernehmen.
    angenommen byte-adresse 123 soll geschrieben werden. dann lädt die cpu das 16-bit-wort von word-adresse 61 ändert das low-byte und schreibt das wort wieder in den speicher. für den asm-programmierer ist das eine atomare operation, die bloß mehr taktzyklen braucht als ein 16-bit-schreibzugriff.

    Solche Systeme kenne ich nicht und AFAIK gibt es auch keine aktuellen solchen die irgendwie relevant wären. Alles was ich kenne was auf Assemblerebene direkt 8-Bit Bytes adressieren kann, kann entweder auch ohne "read-modify-write" direkt 8 Bit breit auf den Hauptspeicher zugreifen -- oder es hat einen Datencache dazwischen der sowieso immer ganze Cache-Lines aus dem Hauptspeicher liest und irgendwann wieder zurückschreibt.

    Und bei den Systemen mit Datencache bleibt es dann auch egal ob du 8, 16 oder 32 Bit schreibst, weil die Cache-Lines nochmal deutlich breiter sind. Daher muss - wenn die Cache-Line nicht schon im Cache liegt - diese immer erst geladen werden bevor der geschriebene Wert hineingestanzt werden kann. Dazu haben solche CPUs dann üblicherweise sog. Store-Buffer, so dass sie mit der Bearbeitung des nächsten Befehls nicht warten müssen bis die Cache-Line geladen wurde und der Wert dort eingesetzt ist. Und auf das Zurückschreiben wird typischerweise sowieso nicht gewartet. Das passiert dann wenn es sein muss. z.B. wenn eine andere CPU/ein anderer Core die selbe Cache-Line haben möchte.

    Daher bin ich einfach davon ausgegangen dass @DirkB Systeme meint wo man wirklich mehrere Assemblerbefehle dazu braucht und das ganze nicht mehr atomar ist.


    Aber selbst wenn wir annehmen wir hätten so ein System. Dann wäre es IMO irgendwie komisch auf der einen Seite uint8_t anzubieten, was das arbeiten mit uint8_t Arrays recht langsam machen würde. Aber auf der anderen Seite dann Padding in Structs einzufügen, damit es in den Structs schneller geht.

    Padding zwischen mehreren direkt benachbarten uint8_t in einem Struct einzufügen ist für mich etwas was so grenzwertig beknackt ist, dass es vermutlich verschmerzbar ist wenn man es nicht berücksichtigt. Ich bin aber i.A. kein Fan von solchen Annahmen. Daher würde ich selbst bei dieser Annahme eine static Assertion oder notfalls einen Test schreiben so dass sichergestellt ist dass man mitbekommt wenn sie mal nicht mehr zutrifft. Aber ich würde niemand gröber beschimpfen nur weil er es nicht tut 🙂



  • @hustbaer sagte in Padding Bytes in Byte struct:

    Solche Systeme kenne ich nicht und AFAIK gibt es auch keine aktuellen solchen die irgendwie relevant wären.

    es geht noch schlimmer. bei einigen signalprozessoren ist jeder speicherzugriff immer 32bittig. erhöht man eine adresse um 1, ist man gleich 4 bytes weiter. 🙂

    The C operation sizeof returns very different results on the C3x/C4x than on traditional RISC/CISC processors. The sizeof(char), sizeof(short), and sizeof(int) are all 1 since each occupies a single addressable unit that is thirty-two bits wide.

    https://docs.rtems.org/releases/rtemsdocs-4.6.99.3/share/rtems/html/supplements/c4x/c4x00017.html