Jan H. schrieb:> Woher weiß ich, wie viele "Padding" Bytes der Compiler einfügt?
Die ABI-Definition wäre wohl die Stelle, wo das definiert ist. Meistens
läuft es darauf hinaus, dass die Adresse beim Zugriff auf einen
primitiven Datentyp immer ein ganzzahliges Vielfaches von dessen Größe
sein muss.
> Hat das was mit der CPU zu tun?
Ja. Je nach CPU sind Speicherzugriffe mit falschem Alignment langsamer
oder gar nicht möglich.
> Gibt es das nur bei Strukturen?
Ja.
Jan H. schrieb:> Woher weiß ich, wie viele "Padding" Bytes der Compiler einfügt?> Hat das was mit der CPU zu tun?
Ja. Der AVR ist ein 8Bitter, der fügt also nichts ein.
Beim ARM kannst Du Dir mit sizeof die Größe einer Struct in Bytes
ausgeben lassen.
> Je nach CPU sind Speicherzugriffe mit falschem Alignment langsamer> oder gar nicht möglich.
Oder die niederwertigsten Adressbits werden stillschweigend ignoriert
(und man bekommt deshalb Daten aus dem falschen Speicherbereich).
Rolf M. schrieb:> Jan H. schrieb:>> Woher weiß ich, wie viele "Padding" Bytes der Compiler einfügt?>> Die ABI-Definition
Für was steht das?
Wo finde ich das?
Wenn man jetzt eine Struktur hat wie z.B
1
struct
2
{
3
uint8_tx;
4
uint8_ty;
5
}test_t;
Müsste diese jetzt auf einem AVR ( 8 Bit ) 2 Byte groß sein?
Jan H. schrieb:> Müsste diese jetzt auf einem AVR ( 8 Bit ) 2 Byte groß sein?
Wurde schon beantwortet.
Jan H. schrieb:> Wo finde ich das?
Falsche Frage!
Die Antwort lautet:
Im Internet!
Eigentlich muss sie heißen:
Wie finde ich das?
Und die Antwort lautet:
In dem man danach sucht!
Unter 3 Minuten suchen:
https://gcc.gnu.org/wiki/avr-gcc#ABI
Rolf M. schrieb:>> Gibt es das nur bei Strukturen?>> Ja.
Nein! Auch zwischen zwei völlig unabhängigen Variablen kann der Linker
Leerraum lassen, damit der Zugriff besser flutscht.
Bei einer Struktur könnte man mit sizeof() rausfinden, wieviel Platz
frei gelassen wurde.
Wenn man keine Struktur hat oder genauer wissen will wo, gäbe es noch
die Möglichkeit einen Pointer auf einen Integerwert zu casten und da
etwas heraus zu lesen.
Oder wenn es nur Dich interressiert : lies doch mal aufmerksam die
.map-Datei durch.
Meine Meinung zu dem Thema ist :
Vernünftige Software sollte einfach funktionieren, egal wo ihre
Variablen im Speicher liegen.
Rolf M. schrieb:> Jan H. schrieb:>> Hat das was mit der CPU zu tun?>> Ja. Je nach CPU sind Speicherzugriffe mit falschem Alignment langsamer> oder gar nicht möglich.
Für alle AVR-CPUs genügt ein Alignment von 1 Byte, und die
Zugriffsgeschwindigkeit hängt bei AVRs nicht vom Alignment ab. Ich lass
mich da aber gerne eines besseren belehren.
Jan H. schrieb:> Moin,>> Woher weiß ich, wie viele "Padding" Bytes der Compiler einfügt?> Hat das was mit der CPU zu tun?> Gibt es das nur bei Strukturen?>>
Compiliert mit avr-gcc [-Os] -fno-pack-struct und verschiedene
Compilerversionen (v8, v6.4.1, v4.7.2, WinAVR-20100110 (v4.3.3)).
Wie es für andere AVR C-Compiler aussieht weiß ich nicht.
Das AVR-Backend des GCC macht keine Änderungen am Default, und
GCC-Default bestimmt Paddings anhand der Größe eines Words:
1
/* Width of a word, in units (bytes). */
2
#define UNITS_PER_WORD 1
http://gcc.gnu.org/viewcvs/gcc/trunk/gcc/config/avr/avr.h?view=markup#l114
Ein formales ABI gab es für AVR nicht, d.h. das ABI des avr-gcc /
avr-g++ definiert sich dadurch, wie Denis damals das Backend
implementierte. ABI-Dokus wie
http://gcc.gnu.org/wiki/avr-gcc
entstanden erst viel später, also reverse engineered. Und zwar anhand
dessen, was avr-gcc konkret implementiert. Layout von Structs / Unions
wird im GCC-Wiki allerdings nicht behandelt.
Habe ich das jetzt richtig verstanden?
Wenn man jetzt ein 32 Bit System nutzen würde und ich in einer Struktur
jetzt nur ein Byte reservieren würde, würde er die restlichen 3 Bytes
mit Padding Bytes füllen?
Jan H. schrieb:> Habe ich das jetzt richtig verstanden?> Wenn man jetzt ein 32 Bit System nutzen würde und ich in einer Struktur> jetzt nur ein Byte reservieren würde, würde er die restlichen 3 Bytes> mit Padding Bytes füllen?> struct u32> {> uint8_t x;> }
Kommt wie gesagt auch die ABI an. Aus dem Bauch heraus würde ich sagen,
ja meistens trifft deine Aussage zu.
So, um es auf den Punkt zu bringen:
Das Ganze ist vom jeweiligen Compiler abhängig.
Wenn man es also richtig machen will, darf man die Größe der Struktur
nicht selber berechnen. sizeof wurde ja schon gennant.
fop schrieb:> Rolf M. schrieb:>>> Gibt es das nur bei Strukturen?>>>> Ja.>> Nein! Auch zwischen zwei völlig unabhängigen Variablen kann der Linker> Leerraum lassen, damit der Zugriff besser flutscht.
Bei völlig unabhängigen Variablen ergibt sich die Frage nicht, eben weil
sie völlig unabhängig von einander sind. Die können auch komplett anders
sortiert sein als im Quellcode. So landen z.B. mit 0 initialisierte
globale Variablen oft in ganz anderen Speicherbereichen als welche, die
mit einem anderen Wert initialisiert werden. Auch ein "packing" kann man
über mehrere Variablen, die nichts miteinander zu tun haben, nicht
machen.
> Meine Meinung zu dem Thema ist :> Vernünftige Software sollte einfach funktionieren, egal wo ihre> Variablen im Speicher liegen.
Ja. Allerdings will Software manchmal auch mit der Umwelt kommunizieren,
und muss man sich ggf. mit solchen Themen beschäftigen.
Johann L. schrieb:> Für alle AVR-CPUs genügt ein Alignment von 1 Byte, und die> Zugriffsgeschwindigkeit hängt bei AVRs nicht vom Alignment ab. Ich lass> mich da aber gerne eines besseren belehren.
Das stimmt. Ich hatte übersehen, dass es um AVR geht, weil das im Text
des Postings nicht erwähnt wurde.
> Compiliert mit avr-gcc [-Os] -fno-pack-struct und verschiedene> Compilerversionen (v8, v6.4.1, v4.7.2, WinAVR-20100110 (v4.3.3)).
Warum schaltest du das Packing denn explizit aus?
Jan H. schrieb:> Habe ich das jetzt richtig verstanden?> Wenn man jetzt ein 32 Bit System nutzen würde und ich in einer Struktur> jetzt nur ein Byte reservieren würde, würde er die restlichen 3 Bytes> mit Padding Bytes füllen?> struct u32> {> uint8_t x;> }
Nicht gezwungenermaßen. Aber bei sowas:
1
structu32
2
{
3
uint32_ty;
4
uint8_tx;
5
}
Denn man kann ja auch ein Array aus diesen Strukturen machen. Bei Arrays
gibt's aber kein Padding. Damit bei einem u32[2] das y vom zweiten
Element auch aligned ist, müssen nach dem x vom ersten aber drei
Padding-Bytes kommen, die Teil der struct sein müssen. Also wird diese
struct auf gängigen 32-Bit-Systemen vermutlich 64 Bit groß sein.
Rolf M. schrieb:> Bei völlig unabhängigen Variablen ergibt sich die Frage nicht,> eben weil sie völlig unabhängig von einander sind. Die können> auch komplett anders sortiert sein als im Quellcode.
Naja, wenn ich zwei globale Variablen definiere und gleich
initialisiere, dann tauchen die mit hoher auch nebeneinander im Binary
auf.
Und auch dort darf der Compiler padding einführen (und tut es auf
manchen Architekturen auch, z.B. ia64).
S. R. schrieb:> Rolf M. schrieb:>> Bei völlig unabhängigen Variablen ergibt sich die Frage nicht,>> eben weil sie völlig unabhängig von einander sind. Die können>> auch komplett anders sortiert sein als im Quellcode.>> Naja, wenn ich zwei globale Variablen definiere und gleich> initialisiere, dann tauchen die mit hoher auch nebeneinander im Binary> auf.
Wie gesagt: Wenn du z.B. die eine mit 0 initialisierst und die andere
mit 1, ist die Wahrscheinlichkeit sehr gering, dass sie nebeneinander
landen.
> Und auch dort darf der Compiler padding einführen (und tut es auf> manchen Architekturen auch, z.B. ia64).
Ich würde hier nicht von Padding sprechen. Davon spricht man meiner
Meinung, wenn zwischen zusammengehörigen Daten Füllbytes einführt. Wenn
die alle separat sind, wird einfach für jede einzeln geschaut, dass sie
mit passendem Alignment im Speicher liegt. Gut, wenn die alle
hintereinander im Speicher landen, ist das Prinzip natürlich sehr
ähnlich, aber der Compiler bzw. Linker könnte sich genauso gut
entscheiden, alle globalen 4-Byte-Variablen in einen gemeinsamen
Speicherblock zu legen und alle 2-Byte-Variablen in einen anderen, um
möglichst wenig Verschnitt zu haben.
S. R. schrieb:> Rolf M. schrieb:>> Bei völlig unabhängigen Variablen ergibt sich die Frage nicht,>> eben weil sie völlig unabhängig von einander sind. Die können>> auch komplett anders sortiert sein als im Quellcode.>> Und auch dort darf der Compiler padding einführen (und tut es auf> manchen Architekturen auch, z.B. ia64).
Rein technisch ist das kein Padding. Ein Padding wäre Teil der internen
Repräsentation, GCC erreicht das z.B. mit .space.
Was du meinst sind die Lücken, die durch das Alignment von Objekten
entstehen, diese werden bei dem GNU-Tools vom Locator eingefügt. Der
Compiler kann hier nix einfügen, da er keine Adressen kennt. Was der
Compiler einfügt sind Direktiven wie .p2align N vor einem Object, damit
dessen Startadresse durch 2**N teilbar ist. Alternative Direktive ist
.balign 2**N. (Semantik von .align ist abhängig vom Target wie .p2align
oder wie .balign. Mehr Argumente sind möglich, z.B. für Maximalanzahl
einzufügender Bytes, wie sie gerne bei Code-Alignment zum Einsatz
kommen.)
Will man den Verschnitt aufgrund Alignment minimieren, kann man im
Linker-Script anweisen, die Objekte nach ihrem Alignment sortieren zu
lassen.
> Naja, wenn ich zwei globale Variablen definiere und gleich> initialisiere, dann tauchen die mit hoher auch nebeneinander im Binary> auf.
Es soll schon Spezialisten gegeben haben, die sich auf eine bestimmte
Reihenfolge der Objekte verlassen haben, und mit der nächsten
Binutils-Version hat plötzlich nix mehr funktioniert. Grund: Binutils
verwendete intern andere Algorithmen, so dass die Objekte in anderer
Reihenfolge ausgegeben wurden.
Rolf M. schrieb:> Johann L. schrieb:>> Compiliert mit avr-gcc [-Os] -fno-pack-struct und verschiedene>> Compilerversionen (v8, v6.4.1, v4.7.2, WinAVR-20100110 (v4.3.3)).>> Warum schaltest du das Packing denn explizit aus?
Manche GUI setzt ungefragt -fpack-struct. Wenn man das Beispiel in so
einer Umgebung testen würde, wäre es nichtssagend wegen -fpack-struct.
Daher als Erinnerung dass man den Default für einen sinnvollen Test
braucht.
Rolf M. schrieb:>> Naja, wenn ich zwei globale Variablen definiere und gleich>> initialisiere, dann tauchen die mit hoher auch nebeneinander im Binary>> auf.>> Wie gesagt: Wenn du z.B. die eine mit 0 initialisierst und die andere> mit 1, ist die Wahrscheinlichkeit sehr gering, dass sie nebeneinander> landen.
Darum schrieb ich "gleich initialisiere", so dass beide entweder in .bss
oder in .data landen.
Wenn ich die eine mit 1 und die andere mit 2 initialisiere, sieht das
mit der Wahrscheinlichkeit wieder völlig anders aus...
>> Und auch dort darf der Compiler padding einführen (und tut es auf>> manchen Architekturen auch, z.B. ia64).> Ich würde hier nicht von Padding sprechen.
Ich meinte, dass einzelne Variablen z.B. immer auf 128 Bit aufgeblasen
werden (unabhängig vom Datentyp), damit der Zugriff darauf effizienter
ist. Das ist im Prinzip auch eine Art, Füllbytes einzuführen, aber auf
einer anderen Ebene. Wenn du das anders bezeichnen möchtest, dann sehe
ich das auch ein.