Mal ne Frage an euch. Bei einem 32-Bit-Mikrocontroller, wie z.B. dem ESP32, ist ja nach wie vor der RAM oft das Problem, wenn Projekte umfangreich werden. Wie handhabt ihr so "Wald-und-Wiesen-Variablen" wie z.B. Schleifenzähler? Man braucht ja ständig so eine Variable und weiß im Voraus, dass nur Werte von vielleicht 1 bis 10 vorkommen. Meine erste Reaktion ist dann immer, uint8_t zu verwenden, damit RAM gespart wird. Aber eventuell ist das gar nicht der richtige Weg und man sollte uint32_t nehmen, weil der µC das nativ kann? Besser wäre sicher, der Compiler (gcc) würde selbst entscheiden, aber kann er das überhaupt?
Franz schrieb: > Mal ne Frage an euch. Bei einem 32-Bit-Mikrocontroller, wie z.B. dem > ESP32, ist ja nach wie vor der RAM oft das Problem, wenn Projekte > umfangreich werden. Wie handhabt ihr so "Wald-und-Wiesen-Variablen" wie > z.B. Schleifenzähler? Man braucht ja ständig so eine Variable und weiß > im Voraus, dass nur Werte von vielleicht 1 bis 10 vorkommen. > Meine erste Reaktion ist dann immer, uint8_t zu verwenden, damit RAM > gespart wird. Aber eventuell ist das gar nicht der richtige Weg und man > sollte uint32_t nehmen, weil der µC das nativ kann? Besser wäre sicher, > der Compiler (gcc) würde selbst entscheiden, aber kann er das überhaupt? Das ist in den allermeisten Fällen scheißegal. Der Schleifenzähler wird mit einiger Wahrscheinlichkeit sowieso in einem MCU-Register landen und nicht im RAM. Da diese Register nunmal bei einer 32Bit-MCU eben 32Bit breit sind, spricht absolut nix dagegen, einfach das generische int oder unsigned int zu verwenden.
Franz schrieb: > Bei einem 32-Bit-Mikrocontroller, wie z.B. dem > ESP32, ist ja nach wie vor der RAM oft das Problem, wenn Projekte > umfangreich werden. Programmieren lernen hilft wohl am besten ...
Versuche nicht, mit solchen 'Tricks' zu optimieren. Die Compiler können das alles besser. Schleifenzähler landen sowieso auf dem Stack, da spart man nichts. Versuche lieber, die Algorithmen ordentlich zu machen. Das bringt mehr.
PittyJ schrieb: > Schleifenzähler landen sowieso auf dem Stack Das wäre eher die Ausnahme als die Regel. Dazu muss innerhalb der Schleife schon ganz ordentlich was passieren... Und wenn das passiert, spielt es wiederum kaum noch eine Rolle, wo genau der Schleifenzähler liegt oder wie groß er ist...
Franz schrieb: > Meine erste Reaktion ist dann immer, uint8_t zu verwenden, damit RAM > gespart wird. Aber eventuell ist das gar nicht der richtige Weg und man > sollte uint32_t nehmen, weil der µC das nativ kann? Auf einem 32-Bitter lohnt sich das eher nicht. Erstens wird der Schleifenzähler sehr wahrscheinlich in einem Register gehalten, zweitens sind dort 8-Bit-Variablen ggf. sogar ineffizienter, da z.B. ein ARM gar nicht in 8 Bit rechnen kann. Er muss das im schlimmsten Fall durch Einfügen zusätzlicher Instruktionen nachbilden.
:
Bearbeitet durch User
Schleifenzähler mit "int", Felder in Strukturen die später im RAM rumliegen mit (u)int_least(8|16|32|64)_t entsprechend des notwendigen Wertebereich und mit Rücksicht aufs Alignment in der Struktur angeordnet.
Franz schrieb: > ei einem 32-Bit-Mikrocontroller, wie z.B. dem > ESP32, ist ja nach wie vor der RAM oft das Problem Der ESP32 hat mindestens 320 kiB RAM. Wenn dir das nicht reicht, dann machst du ziemlich sicher etwas extrem Spezielles, oder etwas extrem Falsches.
Andreas M. schrieb: > Schleifenzähler mit "int", Felder in Strukturen die später im RAM > rumliegen mit (u)int_least(8|16|32|64)_t entsprechend des notwendigen > Wertebereich und mit Rücksicht aufs Alignment in der Struktur > angeordnet. Allgemeiner für Faule: Nicht nur Schleifenzähler in nativer Grösse, also "int" oder "unsigned", sondern generell alle lokalen skalaren Variablen. Einzig auf 8-Bittern ist das ungünstig, da in diesem Fall der C Standard der nativen Grösse von Vars zuwider läuft. Will man das aufgrund Portabilität einrechnen, gibts die (u)int_fastN_t für lokale Vars.
:
Bearbeitet durch User
(prx) A. K. schrieb: > Einzig auf 8-Bittern ist das ungünstig, da in diesem Fall der C Standard > der nativen Grösse von Vars zuwider läuft. Will man das aufgrund > Portabilität einrechnen, gibts die (u)int_fastN_t für lokale Vars. Ja das stimmt. Auf dem AVR kann man ne ganze Menge code sparen wenn in "if", "for" usw peinlich darauf achtet auf allen Seiten des Vergleichsoperators den für den Wertebereich passenden "..fast.." Datentyp verwendet.
Andreas M. schrieb: > Auf dem AVR kann man ne ganze Menge code sparen wenn in > "if", "for" usw peinlich darauf achtet auf allen Seiten des > Vergleichsoperators den für den Wertebereich passenden "..fast.." > Datentyp verwendet. Das ist allerdings eine urban legend. Die Typen der stdint.h sind ja nun nichts weiter als typedefs auf die C-Datentypen, und sind (nicht nur beim AVR) einfach nur typedefs auf die Typen ohne _fast. Das da ein Compiler in Schleifen sich einen "schnelleren" Typ aussuchen könnte, ist reines Wunschdenken. Oliver
Oliver S. schrieb: > Die Typen der stdint.h sind ja nun nichts weiter als typedefs auf die > C-Datentypen, und sind (nicht nur beim AVR) einfach nur typedefs auf die > Typen ohne _fast. Das da ein Compiler in Schleifen sich einen > "schnelleren" Typ aussuchen könnte, ist reines Wunschdenken. Moin, der IAR Compiler für Arm (gerade mit v9.30.1 getestet) löst "_fast8" und "_fast16" unter der Haube anders auf, als die entsprechenden "normalen" Datentypen: z.B.
1 | ... |
2 | #define __UINT8_T_TYPE__ unsigned char |
3 | ... |
4 | #define __UINT16_T_TYPE__ unsigned short int |
5 | ... |
6 | #define __UINT_FAST8_T_TYPE__ unsigned int |
7 | ... |
8 | #define __UINT_FAST16_T_TYPE__ unsigned int |
9 | ... |
Gruß, Michael
Michael F. schrieb: > der IAR Compiler für Arm (gerade mit v9.30.1 getestet) löst "_fast8" und > "_fast16" unter der Haube anders auf, als die entsprechenden "normalen" > Datentypen: Das mag schon sein. gcc macht das für x86 auf Linux auch, auf Windows aber nicht. YMMMV. Andreas M. schrieb: > AVR Auf einem 8-bitter wie dem AVR kann das nichts bringen, denn da ist nun mal ein (u)int8_t der schnellste Datentyp, und für alle größeren gilt: je größer, desto langsamer. Oliver
Beitrag #7118562 wurde von einem Moderator gelöscht.
blubblub schrieb im Beitrag #7118562: > Seit ewigen Zeiten: > > typedef unsigned char uc; > typedef unsigned int ui; > > Und aus die Maus. Ist halt doof. Besonders, seitdem es die stdint.h gibt.
blubblub schrieb im Beitrag #7118562: > typedef unsigned char uc; > typedef unsigned int ui; > > Und aus die Maus. So ähnlich habe ich das auch lange gemacht (und teilweise noch heute). Ich verwendete dann als short form u8, u16 ... als Type.
:
Bearbeitet durch User
Beitrag #7118593 wurde von einem Moderator gelöscht.
Ganz dumme Idee(Hat sich aber Bewährt) Temporäre Schleifenzähler(Wen der Speicher und die Register knapp werden), vor allem wenn sie gerade mal ein halbes Byte groß sind, verwende ich gerne Register von unbenutzten Pheripherien. etwa AD Zwischenregister, Port Interruptpolaritäts Register usw... Ganz Favorisiert auch Timer CC oder Zählerregister die nicht gebraucht werden.. Davon liegen in den meisten Applikationen immer einige Brach, und sind auch noch nur 8 oder 16 Bit breit! (auch bei 32 Bit µC's) Sind auch noch oft schneller als RAM-Zugriff, und wenn man mit DMA arbeitet, belegen sie kein DMA Kanal :-) Also kann DMA auf Speicher zugreifen, während die CPU grad am Incrementieren ist usw... Timer-Register mit Autoinkrement Funktion und Interrupt Auslösung bei "0" sind da ebenfalls sehr beliebt als Schlaufenzähler, da sie so Konfiguriert werden können das sie bei Abfrage grad direkt Inkrementiert werden, was dann ein Vergleichen und Inkrementieren erspart. Und der Interrupt wird auf Schleifenende-Handling gesetzt. Spart dann noch ein Sprungbefehl bei der Bedingung in der eine Schlaufe auch in der Schlaufe beendet werden soll. 73
blubblub schrieb im Beitrag #7118593: > Einer meiner ersten benutzten Compiler konnte genau > eine Variable in ein Register legen. > > register cx; Jaja, die guten alten Zeiten. Das war bestimmt noch auf Lochkarten. Ist zwar nett, aber heute doch völlig irrelevant. Oliver
blubblub schrieb im Beitrag #7118593:
> Ne. Kein Compiler wird mit der stdint.h besser.
Der Compiler nicht, aber man muss nicht mehr so unwartbaren Rotz mit
überflüssigen selbstdefinierten Typen schreiben, wie Du es tust.
Hast Du Dir für die anderen Datentypen, die C kennt, auch eigene Defines
gefrickelt?
Wie wäre es mit "g" (Ganzzahl), "gg" (große Ganzzahl) anstelle von int
und long?
"gp" (Ganzzahl positiv) statt unsigned int?
Oder mit "b" (Bruchzahl) und "gb" (große Bruchzahl) anstelle von float
und double?
Oder mit "t" (Text) statt char?
"tz" statt char *, "gz" statt int *, "gpz" statt unsigned int * ...
Beitrag #7118673 wurde von einem Moderator gelöscht.
Oliver S. schrieb: > Das da ein Compiler in Schleifen sich einen > "schnelleren" Typ aussuchen könnte, ist reines Wunschdenken. Kein Wunschdenken und genau das tut er. Genau genommen tut es allerdings derjenige, der das Include-Files dazu schreibt, und bei einer 32-Bit RISC für int_fast8_t einen 32-Bit Typ einsetzt, bei 8-Bit AVR aber einen 8-bit Typ.
MaWin schrieb: > Der ESP32 hat mindestens 320 kiB RAM. Dann lohnt sich natürlich keinerlei Mikro-Optimierung. Franz schrieb: > z.B. dem > ESP32, ist ja nach wie vor der RAM oft das Problem, wenn Projekte > umfangreich werden. Wirklich? Speicherst Du darin Telefonbücher ab?
Es ist halt einfach Bad practise eigene Datentypen selbst zu definieren, die es in der stdint.h schon gibt. Steht in sämtlichen aktuellen Coding guide lines - da gibt's auch nichts zu diskutieren. Kompatibilität wurde schon genannt. Fehlerrisiko bei Architekturwechsel ist ein anderer Punkt. "Not invented here" ist immer schlecht. Wer über seinen Tellerrand schauen kann, nimmt die weit verbreiteten, getesteten und erprobten Lösungen.
Oliver S. schrieb: > gcc macht das für x86 auf Linux auch, auf Windows > aber nicht. YMMMV. x86 ist ein Grenzfall, weil diese Architektur auch in Registern mit 8-Bit Datentypen zurecht kommt, besonders in der x86_64 Variante. Deshalb kann man bei int_fast8_t Gründe für beide Varianten finden - 8 Bit weils in Register möglich ist, 32 Bit weil seit dem Pentium Pro bestimmte Ablauf-Abhängigkeiten der Mikroarchitekturen dafür sprechen.
:
Bearbeitet durch User
Franz schrieb: > Meine erste Reaktion ist dann immer, uint8_t zu verwenden, damit RAM > gespart wird. uint8_t wird wahrscheinlich auf ARM im RAM auf 4 Byte aligned. Schau dir einfach mal die Adressen von deinen Variablen an.
Ausrichter schrieb: > uint8_t wird wahrscheinlich auf ARM im RAM auf 4 Byte aligned. Nein. Oliver S. schrieb: > Die Typen der stdint.h sind ja nun nichts weiter als typedefs auf die > C-Datentypen, und sind (nicht nur beim AVR) einfach nur typedefs auf die > Typen ohne _fast. Das da ein Compiler in Schleifen sich einen > "schnelleren" Typ aussuchen könnte, ist reines Wunschdenken. Nein. Beim AVR ist uint_fast8_t "unsigned char", beim ARM und amd64 hingegen "unsigned int". Und genau so ist es auch definiert: der architekturspezifische Datentyp mit dem am schnellsten Operationen für einen Wertebereich von "0-255" durchgeführt werden können. Dem entgegen ist uint8_t auf allen Architekturen ein typedef auf "unsigned char". (Wenn unsigned char 8 bit hat, sonst existiert das typedef nicht) Ein uint_least8_t ist auf allen Architekturen, die 8-Bit Datenzugriffe unterstützen ebenfalls ein typedef auf "unsigned char". Es gibt aber z.B. DSP die können nicht weniger als 16 Bit. Da ist uint_least8_t ein typedef auf "int". (16 bit) Wenn ich portablen Code schreiben möchte der eine Zahl zwischen 0 und 255 und der sowohl auf AVR, ARM, x86, MSP430 oder sonstwas die bestmögliche Performance/Codegröße erreichen soll, dann nimmt man eben uint_fast8_t. Fertig. Und will man einen 8 bit Wert nicht nur temporär im Stack/Register verwenden, sondern irgendwo als Data/Bss/Heap speichern, dann nimmt man eben uint_least8_t für die Felddeklaration, dann läuft der Code auch auf den oben erwähnten DSP Architekturen ohne irgendwelche Klimpzüge. Wenn man natürlich Projekte immer nur eng in einer Platform denkt und entwickelt, muss man darüber nicht nachdenken.
Andreas M. schrieb: > Ausrichter schrieb: >> uint8_t wird wahrscheinlich auf ARM im RAM auf 4 Byte aligned. > > Nein. Das würde der C-Standard auch verbieten. Das Alignment eines Datentyps kann nie größer sein als der Datentyp selbst, denn in einem Array müssen alle Elemente immer direkt aufeinander folgen, ohne Leerbytes dazwischen. Andreas M. schrieb: > Wenn ich portablen Code schreiben möchte der eine Zahl zwischen 0 und > 255 und der sowohl auf AVR, ARM, x86, MSP430 oder sonstwas die > bestmögliche Performance/Codegröße erreichen soll, dann nimmt man eben > uint_fast8_t. Fertig. Und will man einen 8 bit Wert nicht nur temporär > im Stack/Register verwenden, sondern irgendwo als Data/Bss/Heap > speichern, dann nimmt man eben uint_least8_t für die Felddeklaration, > dann läuft der Code auch auf den oben erwähnten DSP Architekturen ohne > irgendwelche Klimpzüge. Das ist eine sehr gute Beschreibung der Idee hinter diesen Datentypen. Es scheint erstaunlich wenige zu geben, die diesen eigentlich einfachen Sachverhalt erfassen.
Beitrag #7118954 wurde vom Autor gelöscht.
Rolf M. schrieb: > Das würde der C-Standard auch verbieten. Das Alignment eines Datentyps > kann nie größer sein als der Datentyp selbst, denn in einem Array müssen > alle Elemente immer direkt aufeinander folgen, ohne Leerbytes > dazwischen. Ob zwei getrennt definierte skalare chars 1 Byte auseinander liegen, oder 4, ist vom Standard m.E. nicht erfasst. Auch deshalb nicht, weil aufeinanderfolgend definierte Skalare nicht notwendigerweise in dieser Ordnung im Speicher liegen.
:
Bearbeitet durch User
Rolf M. schrieb: > denn in einem Array müssen > alle Elemente immer direkt aufeinander folgen, ohne Leerbytes > dazwischen. Da wirds nun philosophisch, das Wesen der Leere betreffend. ;-) Denn __float80 sind genau genommen nur 10 Bytes gross. Aber um nicht in die Bredouille zu kommen, tut der Compiler so, als wären sie 16 Bytes gross, und legt so effektiv Leerbytes dazwischen. Aber das ist halt eine andere Art von Leere, als jene, die du meinst.
:
Bearbeitet durch User
(prx) A. K. schrieb: > Ob zwei getrennt definierte skalare chars 1 Byte auseinander liegen, > oder 4, ist vom Standard m.E. nicht erfasst. Auch deshalb nicht, weil > aufeinanderfolgend definierte Skalare nicht notwendigerweise in dieser > Ordnung im Speicher liegen. Das ist richtig, hat aber nichts mit Alignment zu tun. Aber wenn es einen Typ uint8_t gibt, muss man daraus auch Arrays bilden können, und da ist eben definiert, dass die Elemente direkt aufeinander folgen müssen. Das ist wiederum nur möglich, wenn das Alignment für uint8_t 1 Byte ist, denn sonst wäre der Compiler ja gezwungen, zusätzliche Bytes zwischen den Elementen einzufügen. Ein uint8_t mit 4-Byte-Alignment lässt sich daher in C nicht standardkonform umsetzen. (prx) A. K. schrieb: > Rolf M. schrieb: >> denn in einem Array müssen >> alle Elemente immer direkt aufeinander folgen, ohne Leerbytes >> dazwischen. > > Da wirds nun philosophisch, das Wesen der Leere betreffend. ;-) Gemeint sind Bytes, die nicht zum Objekt selbst gehören, sondern nur Zwischenräume zwischen den Objekten auffüllen. > Denn __float80 sind genau genommen nur 10 Bytes gross. Aber um nicht in > die Bredouille zu kommen, tut der Compiler so, als wären sie 16 Bytes > gross, und legt so effektiv Leerbytes dazwischen. Aber das ist halt eine > andere Art von Leere, als jene, die du meinst. Er legt genau genommen keine Leerbytes dazwischen, sondern macht sie zum Teil des Typs selbst. Das ist durchaus erlaubt, aber nicht für uint8_t, da der nicht größer sein kann als ein Byte - was wieder dazu führt, dass ein uint8_t mit einem Alignment von mehr als 1 Byte nicht standardkonform umsetzbar ist.
Andreas M. schrieb: > Wenn ich portablen Code schreiben möchte der eine Zahl zwischen 0 und > 255 und der sowohl auf AVR, ARM, x86, MSP430 oder sonstwas die > bestmögliche Performance/Codegröße erreichen soll, dann nimmt man eben > uint_fast8_t. Fertig. Man muß halt im Hinterkopf haben, daß der tatsächliche Datentyp kein uint8_t sein muß, und alle möglichen daraus resultierende Unterschiede zum uint8_t beachten. So etwas wie
1 | if (i++ > 0 ) ... |
funktioniert dann nicht mehr wie erwartet. Oliver
:
Bearbeitet durch User
Rolf M. schrieb: > Das ist richtig, hat aber nichts mit Alignment zu tun. Doch, aber an anderer Stelle. Bei der Speicherzuordnung im Linker können andere Alignment-Regeln gelten, als in der Sprache selbst.
Rolf M. schrieb: > Er legt genau genommen keine Leerbytes dazwischen, sondern macht sie zum > Teil des Typs selbst. Philosophische Betrachtungen sind nicht so dein Ding? ;-) Meine Maschine fasst diese Bytes jedenfalls nicht an und schreibt brav ihre 10 Bytes und keines mehr. Also mögen sie zum Typ gehören, sind aber leer. Alignment-Bytes sind halt anders leer.
:
Bearbeitet durch User
(prx) A. K. schrieb: > Philosophische Betrachtungen sind nicht so dein Ding? ;-) Ich betrachte es halt "sprachphilosophisch". > Also mögen sie zum Typ gehören, sind aber leer. Alignment-Bytes sind halt > anders leer. Sie sind genauso leer, nur sind sie eben nicht Teil des Typs. Das erkennt man z.B. auch per sizeof. So ist sizeof(__float80) nicht 10, sondern 16.
Oliver S. schrieb: > Andreas M. schrieb: >> Wenn ich portablen Code schreiben möchte der eine Zahl zwischen 0 und >> 255 und der sowohl auf AVR, ARM, x86, MSP430 oder sonstwas die >> bestmögliche Performance/Codegröße erreichen soll, dann nimmt man eben >> uint_fast8_t. Fertig. > > Man muß halt im Hinterkopf haben, daß der tatsächliche Datentyp kein > uint8_t sein muß, und alle möglichen daraus resultierende Unterschiede > zum uint8_t beachten. So etwas wie > if (i++ > 0 ) ... > > funktioniert dann nicht mehr wie erwartet. > > Oliver Man muß auch im Hinterkopf behalten, dass der tatsächliche Datentyp ein uint32_t sein kann und damit mehr Speicherbandbreite braucht. Damit kann aus "fast" schnell "slow" werden, wenn die Bandbreite nicht mehr ausreicht.
Das passiert aber nur auf einer 32-bit Architektur und dort sind 8-bit Zugriffe bei mir bekannten Architekturen niemals schneller als 32-bit. In Strukturen kann es aus Speichergründen durchaus Sinn machen uint8_t statt der fast Variante zu verwenden, darum ging es hier aber nicht.
avr schrieb: > Das passiert aber nur auf einer 32-bit Architektur und dort sind 8-bit > Zugriffe bei mir bekannten Architekturen niemals schneller als 32-bit. Ja?! Deswegen hat der unit_fast8_t auch evtl. 32 Bit. Und wenn es etwas wie SIMD gibt, wird es spannend was tatsächlich schneller ist.
Ne, wird es nicht, außer du zeigst mir einen Compiler, der von sich auch aus SIMD Instruktionen verwendet. Das schweift gerade vom Thema ab. Dieser Satz ist einfach Käse. Das passiert bei korrektem Einsatz von uint8_fast nicht. mh schrieb: > Damit kann aus "fast" schnell "slow" werden, wenn die Bandbreite nicht > mehr ausreicht.
avr schrieb: > Ne, wird es nicht, außer du zeigst mir einen Compiler, der von sich auch > aus SIMD Instruktionen verwendet. gcc & clang > Das schweift gerade vom Thema ab. Dieser Satz ist einfach Käse. Das > passiert bei korrektem Einsatz von uint8_fast nicht. Und warum nicht?
avr schrieb: > Ne, wird es nicht, außer du zeigst mir einen Compiler, der von sich auch > aus SIMD Instruktionen verwendet. Ich kann dir gleich drei nennen: GCC und die Compiler von Intel und Microsoft. Die Frage müsste ja schon fast eher lauten: Gibt's noch welche, die das nicht tun? Siehe https://www.google.com/search?q=automatic+vectorization
:
Bearbeitet durch User
Mag sein, hab ich aber im embedded Bereich noch nie beim GCC gesehen. Bisher habe ich in hot loops immer intrinsics nutzen müssen.
Auf keinen Fall uint8_t benutzen. Hier mal ein Beispiel wo es langsamer ist als int: https://godbolt.org/z/v8T96jz8d
Das sind zwei vollkommen unterschiedliche Funktionen, die unterschiedliche Ergebnisse liefern. Warum sollten die die gleiche Laufzeit haben?
int_ist_besser schrieb: > Hier mal ein Beispiel wo es langsamer ist als int: ziemlicher Käse ist es.
Andreas M. schrieb: > Wenn ich portablen Code schreiben möchte... Wenn du so etwas schreibst, liegt die Vermutung nahe, daß du noch nie in dieser Verlegenheit warst. Also reine Theoretisiererei. Wenn man wirklich portables Zeugs schreiben will, dann nimmt man das wenige, was die Sprachdefinition bietet und richtet sich danach. Immer in der Hoffnung, daß der zur jeweiligen Architektur gehörige Compiler das Richtige draus macht. Also keine programmiertechnischen "Husarenstücke" und z.B. int mit 16 Bit, auch wenn's vielleicht auf anderen Maschinen in 64 Bit gerechnet wird. Und wenn man Daten erzeugt oder bearbeitet, die auf Maschinen verschiedener Endianess richtig verstanden werden sollen, dann muß man sich überlegen, wie man das anstellt. Beispiel: eine .wav Datei auf verschiedenen Rechnern. W.S.
Franz schrieb: > immer uint8_t verwenden Bei einer struct aus gemischten uint8_t uint16_t uint32_t gibt es auf dem Cortex-M gute Chance auf Hardfault wegen Unaligned Access
Lothar schrieb: > Bei einer struct aus gemischten uint8_t uint16_t uint32_t gibt es auf > dem Cortex-M gute Chance auf Hardfault wegen Unaligned Access Für das nötige Alignment sorgt der Compiler schon selbst.
:
Bearbeitet durch User
(prx) A. K. schrieb: > Für das nötige Alignment sorgt der Compiler schon selbst Viele portierte Treiber z.B. Ethernet gehen aber über Pointer + Offset in eine Daten struct, da ist dann Auto Align nicht gut. Muss mal testen was gcc bei union macht ...
Lothar schrieb: > Viele portierte Treiber z.B. Ethernet gehen aber über Pointer + Offset Ja, aber derjenige weiss dann genau, was er tut. Oder sollte es. Portablität von Daten über Architekturen hinweg ist eine vollkommen andere Baustelle, als alles, was hier bisher zur Sprache kam. In Netzwerk-Code kann es vorkommen, dass ein Performance-Fan einen Frame auf eine MOD 4 == 2 Adresse legt, damit das Alignment vom IP-Header passt, weil der übliche Ethernet-Header 14 Bytes gross ist. Das ist dann aber schon Hardcore-Optimierung.
:
Bearbeitet durch User
Lothar schrieb: > Bei einer struct aus gemischten uint8_t uint16_t uint32_t gibt es auf > dem Cortex-M gute Chance auf Hardfault wegen Unaligned Access So ein Quatsch.
W.S. schrieb: > Immer in der Hoffnung, daß der zur jeweiligen Architektur gehörige > Compiler das Richtige draus macht. Wenn ich Software entwickle, dann hoffe ich nicht. Der Compiler macht das daraus, was ich will. Den Rest von Deinem Anfeindungen lasse ich mal unkommentiert. Wenn Du Google bedienen könntest, dann würdest Du ziemlich schnell herausfinden was ich mache.
W.S. schrieb: > Wenn man wirklich portables Zeugs schreiben will, dann nimmt man das > wenige, was die Sprachdefinition bietet und richtet sich danach. Immer > in der Hoffnung, daß der zur jeweiligen Architektur gehörige Compiler > das Richtige draus macht. Oder man lernt einfach die Sprache und weiß dann, was der Compiler draus macht. Das ist deutlich besser als hoffen. > Also keine programmiertechnischen "Husarenstücke" und z.B. int mit 16 Bit, > auch wenn's vielleicht auf anderen Maschinen in 64 Bit gerechnet wird. Und > wenn man Daten erzeugt oder bearbeitet, die auf Maschinen verschiedener > Endianess richtig verstanden werden sollen, dann muß man sich überlegen, > wie man das anstellt. Beispiel: eine .wav Datei auf verschiedenen Rechnern. Da vermischst du aber zwei Dinge, die man grundsätzlich sauber trennen sollte, nämlich programminterne Daten und solche, die zum Datenaustausch mit externen Systemen oder Peripherie da sind. Für erstere ist der Datentyp zu bevorzugen, der eine für die gegebene Aufgabe ausreichende Größe hat und dabei am effizientesten umsetzbar ist, für letztere muss es natürlich die exakt richtige Größe sein. Da nimmt man, wenn es portabel sein soll, natürlich keinen int, sondern z.B. uint16_t. Um Endianness muss man sich dann eh gesondert kümmern. Aber auch das macht man natürlich nicht überall kreuz und quer im Programm, sondern nur an der Kommunikationsschnittstelle, die das erfordert.
:
Bearbeitet durch User
Rolf M. schrieb: > Da > nimmt man, wenn es portabel sein soll, natürlich keinen int, sondern > z.B. uint16_t. Um Endianness muss man sich dann eh gesondert kümmern. Da hast du ungewollt mal wieder gezeigt, wie albern im Grunde solche Umbenennungen wie uint16_t sind. Solange man bloß rechnet, ist es egal, solange man sich auf das Zugesicherte veläßt (z.B. mindestens 16 bit) und wo es darum geht, Daten in vorgegebene Strukturen einzupassen, versagt sowas bereits beim int bzw. uint16_t, weil das eben die Endianness ignoriert. Und eine universelle Endianness hat bislang noch keiner erfunden. W.S.
W.S. schrieb: > Und eine universelle Endianness hat bislang noch keiner erfunden. Wie viele erfolgreiche Big Endian Implementierungen gibts denn noch? Da hilft auch der Umstand nicht weiter, dass man im Netzwerk mit Big Endian einfacher dran ist. ARM hält sich zwar aus dem Streit vornehm raus und lässt bei deren Cores beides zu, aber die realen Systeme sind m.W. heute alle Little Endian. Bleibt eigentlich nur POWER. Die Apples sind allerdings nicht so meine Welt. Da wurde von Big Endian 68K und PowerPC zwischendrin auf Little Endian x86 und dann ARM gewechselt. Das hätte man als Anwendungsentwickler eigentlich merken müssen.
:
Bearbeitet durch User
(prx) A. K. schrieb: > Wie viele erfolgreiche Big Endian Implementierungen gibts denn noch? Das gesamte Internet.
MaWin schrieb: > (prx) A. K. schrieb: >> Wie viele erfolgreiche Big Endian Implementierungen gibts denn noch? > > Das gesamte Internet. Das stimmt zwar, wirft aber die Frage auf: Wenn praktisch alle heute noch relevanten Systeme little endian benutzen, sollte man dann nicht mal endlich den Netzwerk-Scheiß den Realitäten anpassen und so enorme Mengen an Rechenzeit und damit direkt proportional Energie einsparen? ;o) So viel zum Spaß, aber hier doch noch eine ernst gemeinte Frage: wer ist eigentlich ursprünglich auf die schwachsinnige Idee gekommen, hier big endian zu verwenden und warum? Little endian war eigentlich schon immer verbreiteter (weil's schlicht auf Hardware- und Software-Ebene letztlich deutlich praktischer ist, der einzige echte Nachteil ist und war ja immer nur die schlechtere Menschenlesbarkeit von Dumps).
c-hater schrieb: > Little endian war eigentlich schon immer > verbreiteter Viele in der Entstehungszeit des TCP/IP bestehenden Architekturen waren big endian.
:
Bearbeitet durch User
c-hater schrieb: > der einzige echte Nachteil ist und war ja > immer nur die schlechtere Menschenlesbarkeit von Dumps Was gerade bei Netzwerktechnik ein riesiger Nachteil wäre.
MaWin schrieb: > Was gerade bei Netzwerktechnik ein riesiger Nachteil wäre. Reine Gewöhnung. Man muss nur von rechts nach links dumpen. Der VAX Assembler machte das in seinen Listings.
:
Bearbeitet durch User
c-hater schrieb: > wer ist eigentlich ursprünglich auf die schwachsinnige Idee gekommen Das ARPAnet entstand vermutlich auf einer big endian Maschine.
MaWin schrieb: > Arabische Zahlen sind aber eben nun mal big-endian. Mein Arabisch ist nicht so brilliant, aber soweit ich weiss, schreiben die von rechts nach links. Das wäre dann little endian. Also alles bloss ein grandioses Missverständnis, als unsere Vorfahren römische Schrift und arabische Zahlen kombinierten? ;-)
:
Bearbeitet durch User
MaWin schrieb: > (prx) A. K. schrieb: >> Reine Gewöhnung > Arabische Zahlen sind aber eben nun mal big-endian. Du meinst damit das in der westlichen Welt verwendete Dezimalsystem mit Arabischen Ziffern? Weil Zahlen in der deutschen Sprache sind weder Big noch Little-Endian oder dezimal ("dreizehntausendzweihundertelf"). @Autor von "Beitrag #7121643 wurde vom Autor gelöscht." (glaube A.K. (prx)) War der Inhalt falsch?
:
Bearbeitet durch User
Beitrag #7121643 wurde vom Autor gelöscht.
Mombert H. schrieb: > Du meinst damit das in der westlichen Welt verwendete Dezimalsystem mit > Arabischen Ziffern? Ja? > Weil Zahlen in der deutschen Sprache sind weder Big > noch Little-Endian oder dezimal Davon sprach ich nicht. (prx) A. K. schrieb: > Mein Arabisch ist nicht so brilliant Darum ging es nicht.
Mombert H. schrieb: > War der Inhalt falsch? Ging zu weit vom Thema weg. Mir wärs ausserdem lieber, wenn ich nicht ahnungslos drüber spekulieren muss, sondern ein Kenner der Sprache sich äussert.
(prx) A. K. schrieb: > sondern ein Kenner der Sprache sich äussert. Es geht nicht - und ging nie - um die arabische Sprache.
MaWin schrieb: > Es geht nicht - und ging nie - um die arabische Sprache. Mir aber schon. Ich nahm mir die Freiheit, neben den Zahlen auch deren Ursprung zu betrachten. ;-)
:
Bearbeitet durch User
(prx) A. K. schrieb: > Mir schon Und warum machst du dafür nicht deinen eigenen Offtopic-Thread auf? Ich sehe jedenfalls keinen Zusammenhang zu diesem Thread.
Siehst du, genau deshalb hatte ich einen meiner Beträge gelöscht.
Weder noch. Da wird man doch sowieso durch ein Array oder String iteririeren. Und sowohl strlen() als auch sizeof geben "size_t" zurück.
W.S. schrieb: > Solange man bloß rechnet, ist es egal, solange man sich auf das > Zugesicherte veläßt (z.B. mindestens 16 bit) > und wo es darum geht, Daten in vorgegebene Strukturen einzupassen, > versagt sowas bereits beim int bzw. uint16_t, weil das eben die > Endianness ignoriert. uint16_t ist halt nicht dafür gedacht, sämtliche Kommunikationsprobleme zu lösen. Mir wäre aber auch keine andere General-Purpose-Sprache bekannt, die solche Datentypen bietet. c-hater schrieb: > Little endian war eigentlich schon immer verbreiteter Das Internet-Protokoll stammt von Anfang der 80er. Da sah die Welt in der Hinsicht etwas anders aus, gerade auch bei den Systemen, die mit dem Internet verbunden werden sollten. Die heute so verbreiteten Little-Endian-Architekturen spielten da allesamt keine Rolle oder existierten noch gar nicht.
MaWin schrieb: >> Weil Zahlen in der deutschen Sprache sind weder Big >> noch Little-Endian oder dezimal > Davon sprach ich nicht. Ändert nichts daran, dass es relevant ist. MaWin schrieb: > (prx) A. K. schrieb: >> Reine Gewöhnung > > Arabische Zahlen sind aber eben nun mal big-endian. Wie A. K. gesagt hat: "Reine Gewöhnung". Wenn man von Kindern in der Schule erwarten kann, dass sie 13211 fließend in dreizehntausendzweihundertelf wandeln können. Dann kann man von Personen die sich mit Netzwerkdumps beschäftigen, ähnliche Gehirnleistung erwarten.
Mombert H. schrieb: > Wie A. K. gesagt hat: "Reine Gewöhnung". Wenn man von Kindern in der > Schule erwarten kann, dass sie 13211 fließend in > dreizehntausendzweihundertelf wandeln können. Dann kann man von Personen > die sich mit Netzwerkdumps beschäftigen, ähnliche Gehirnleistung > erwarten. Bei Big-Endian brauche ich keine Gewöhnung, Wandlung oder ähnliche Gehirnleistung. Niemand hat jemals gesagt, dass Little-Endian unlesbar wäre. Aber Big-Endian ist unbestreitbar einfacher zu lesen.
MaWin schrieb: > Aber Big-Endian ist unbestreitbar einfacher zu lesen. Aber dann bitte auch mit passender Bitreihenfolge. Also Bit 0 oben und Bit 31 oder 63 unten. Sonst passt das nicht zusammen. Bytes von links nach rechts und Bits von rechts nach links zu nummerieren ist Unfug. Motorola lernte das auf die harte Tour, weshalb die ab 68020 hinzu gekommenen Bitfelder von links nach rechts und die vorher schon bestehenden Einzelbits von rechts nach links nummeriert wurden. IBM ist da konsequent und sowohl bei deren Mainframes als auch bei den POWER Prozessoren ist Bit 0 oben. TI ging noch einen Schritt weiter, indem bei den 16-Bit TI-990(0) die Bytes der 8-Bit Operationen nicht wie sonst üblich rechtsbündig in den Registern lagen, sondern linksbündig. M.a.W: Die Dumps mögen big endian natürlicher wirken. Aber bei anderen Aspekten ist das genau umgekehrt.
:
Bearbeitet durch User
Rolf M. schrieb: > Das Internet-Protokoll stammt von Anfang der 80er. Da sah die Welt in > der Hinsicht etwas anders aus, gerade auch bei den Systemen, die mit dem > Internet verbunden werden sollten. Die heute so verbreiteten > Little-Endian-Architekturen spielten da allesamt keine Rolle oder > existierten noch gar nicht. x86 existierte zu dieser Zeit bereits und war auch schon in in recht breitem Einsatz, jedenfalls dürfte die Zahl der existierenden x86-Systeme schon 1980 die Zahl aller anderen im praktischen Einsatz befindlichen Systeme mit mehr als 8 Bit deutlich überstiegen haben. Aber OK, x86 gab's erst seit 1982 in einer notdürftigen 32Bit-Variante. Und mit dem Internet verbunden waren die Dinger tatsächlich eher selten, höchstens indirekt via "Terminal-DFÜ".
Rolf M. schrieb: > Das Internet-Protokoll stammt von Anfang der 80er. Es entstand bereits in den 70ern. https://en.wikipedia.org/wiki/Internet_Protocol#Version_history c-hater schrieb: > x86 existierte zu dieser Zeit bereits In der Entstehungszeit noch nicht, da wurde ja nicht in 6 Tagen erfunden. Zudem waren x86er in diesem Zusammenhang vollkommen irrelevant. TCP/IP entstand auf Minicomputern. Auch die ersten Formen der Vernetzung von PCs hatten noch lange Zeit nichts mit TCP/IP zu tun, basierten auf NetBIOS und Novell.
:
Bearbeitet durch User
(prx) A. K. schrieb: > Rolf M. schrieb: >> Das Internet-Protokoll stammt von Anfang der 80er. > > Es entstand bereits in den 70ern. > https://en.wikipedia.org/wiki/Internet_Protocol#Version_history Ich hatte mich jetzt auf den ersten RFC für IPv4 von 1980 bezogen. Das hat's für mich offiziell gemacht. Aber stimmt natürlich, Entwicklung und damit wohl auch die Entscheidung für big endian gab es schon deutlich früher.
Beitrag #7122442 wurde vom Autor gelöscht.
c-hater schrieb: > So viel zum Spaß, aber hier doch noch eine ernst gemeinte Frage: wer ist > eigentlich ursprünglich auf die schwachsinnige Idee gekommen, hier big > endian zu verwenden und warum? Was ist "hier"? Meinst du in diesem Forum? Und "verwenden"? Ich selber habe lange Zeit die Fujitsu FR benutzt und die waren damals schneller und auch billiger als die damaligen ARM's oder die kleineren und inzwischen ausgestorbenen NEC 78K3 und K4. Eben deshalb habe ich immer mit Systemen verschiedener Endianness zu tun gehabt einschließlich solcher Dinge, die auf beiden Seiten funktionieren mußten - und jetzt kommen mir hier einige Jungspunde vor die Nase, die meinen, daß man mit uint16_t die ultimative Portabilität gefunden hätte, obwohl sie kaum etwas anderes als Atmel AVR und STM32 und den GCC gesehen haben und Assembler für einen neuen Blumendünger halten oder sich gar zu der Aussage hinreißen lassen "ich programmiere nicht auf Registerebene". Ganz generell kann man sagen, daß es auch in der Prozessorgestaltung historisch gewachsene Dinge gibt, wie z.B. die Endianness, die zwei getrennte Welten (Motorola versus Intel) bewirkt hatten. Da hast du zumindest die historischen Wurzeln zu deiner Frage. Ich bin auch nicht recht froh über darüber, daß gar viele Architekturen inzwischen ausgestorben sind. Anstelle von Synergie-Effekten sieht man inzwischen, daß der geistige Horizont der Leute kleiner geworden ist. Setze mal einen der Programmierer, die sich hier großartig vorkommen, an ein Projekt mit einem µC von Freescale. Huch, das ist ja kein STM32! W.S.
W.S. schrieb: > Eben deshalb habe ich > immer mit Systemen verschiedener Endianness zu tun gehabt einschließlich > solcher Dinge, die auf beiden Seiten funktionieren mußten - und jetzt > kommen mir hier einige Jungspunde vor die Nase, die meinen, daß man mit > uint16_t die ultimative Portabilität gefunden hätte Kodierung von Daten auf Netzwerken, Bussen etc hat nur am Rande was mit Portabilität ein Applikation zu tun. Das Ausgangsthema dieses Threads sind innerhalb des selben Systems verwendete Variablen und Felder. Und da ist die Byte-Order vollkommen irrelevant. Wenn es um Datenaustausch mit anderen Systemen geht, dann muss man sich natürlich Gedanken um die Kodierung der Daten machen. Wenn man dann die entsprechenden uint<n>_t Typen zusammen mit gepackten Strukturen verwendet dann funktioniert das ganz gut und bleibt lesbar. Manche finden es besser wenn man die Bytes händisch wieder zusammensetzt. Wenn man dafür die uint<n>_t typen in gepackten aber ausgerichten Strukturen benutzt tut sich der Compiler einfacher beim optimieren. W.S. schrieb: > etwas anderes als Atmel AVR und STM32 und den GCC gesehen haben und > Assembler für einen neuen Blumendünger halten oder sich gar zu der > Aussage hinreißen lassen "ich programmiere nicht auf Registerebene". Wo steht das hier im Thread? Btw. Kannst Du denn AVR, 8051, ARM, LX6 und Risc-V Assembler nicht nur lesen sondern auch schreiben? Wenn nicht dann würde ich mal ganz still mit solcher Selbstbeweihräucherung sein.
W.S. schrieb: > und jetzt > kommen mir hier einige Jungspunde vor die Nase, die meinen, daß man mit > uint16_t die ultimative Portabilität gefunden hätte, Abgesehen davon, daß du das Thema "endianess" hier in dem Thread aufgebracht hast, hat das eigentlich nie jemand gemeint oder behauptet. Das die stdint.h-Datentypen keine systemübergreifende Kompatibilität beim Datenaustausch bieten, und auch nie dafür gedacht waren, dürfte so ziemlich jedem klar sein. Nur dir nicht. Die bieten einfach nur Datentypen für das jeweilige System in einer definierten Bitbreite. Klingt einfach, ist auch so. Oliver
Andreas M. schrieb: > Wenn man dann die entsprechenden uint<n>_t Typen zusammen mit gepackten > Strukturen verwendet dann funktioniert das ganz gut und bleibt lesbar. Allerdings kann nicht jede CPU problemlos auf Datentypen mit falschem Alignment zugreifen.
Rolf M. schrieb: > Allerdings kann nicht jede CPU problemlos auf Datentypen mit falschem > Alignment zugreifen. Bei gepackten Strukturen schon.
MaWin schrieb: > Bei gepackten Strukturen schon. Die CPU selbst nicht unbedingt und einen Sprachstandard für gepackte Strukturen gibt es m.W nicht. Wenn der Compiler es unterstützt, die CPU aber nicht, baut er sich die Zugriffe eben umständlich zurecht.
:
Bearbeitet durch User
(prx) A. K. schrieb: > und einen Sprachstandard für gepackte Strukturen gibt es m.W nicht. Wenn > der Compiler es unterstützt, die CPU aber nicht, baut er sich die > Zugriffe eben umständlich zurecht. Ja eben dass ist ja die Aufgabe des Compilers. GCC und clang setzen beide das Alignment von Felder in gepackten Strukturen erst mal auf 1. Damit machen die erstmal nur noch Bytezugriffe, muss man dann nicht händisch machen. Kann man trotzdem drüber diskutieren. Manche bevorzugen Bytegymnastik. Hat auch seine Vorteile, steht dann explizit da. Ob ein ARM unaligned Speicherzugriff kann oder nicht hängt auch von den Einstellungen der MPU oder MMU ab. In Strongly Ordered oder Device Memory getaggten Bereichen ist das grundsätzlich nicht zulässig.
MaWin schrieb: > Rolf M. schrieb: >> Allerdings kann nicht jede CPU problemlos auf Datentypen mit falschem >> Alignment zugreifen. > > Bei gepackten Strukturen schon. Der CPU ist egal, ob ein float in einer gepackten Struktur steht oder nicht. Von Strukturen weiß die nix. Andreas M. schrieb: > Ja eben dass ist ja die Aufgabe des Compilers. GCC und clang setzen > beide das Alignment von Felder in gepackten Strukturen erst mal auf 1. Was passiert denn, wenn ich über einen Zeiger auf das Element zugreife? Generell bin ich bei gepackten Strukturen vorsichtig. Die haben bei mir in der Vergangenheit schon zu sehr merkwürdigen Verhaltensweisen geführt.
Wer möchte, der kann es auf dem PC ausprobieren. Steht Bit 18 von CR0 auf 1, kann man mit Bit 18 von EFLAGS den Alignment Check in Ring 3 aus und einschalten.
:
Bearbeitet durch User
ARM Cores vor ARMv5, also auch die früher beliebten Mikrocontroller mit ARMv4 ARM7TDMI Cores, hatten eine sehr kreative Art, mit Alignment umzugehen. Damals waren die ARM Cores sehr einfach aufgebaut und alle Load-Daten liefen unweigerlich durch den Barrel Shifter, gesteuert über die Bits 0..1 der Adresse. Der rotierte das adressierte Byte passend ins unterste Byte des Registers. Der einzige Unterschied zwischen Byte- und Word-Load bestand in der Maskierung der Bits 8..31. Das ist allerdings nicht immer das, was man von einem misaligned Load erwartet, d.h. die Daten, die man auf diese Art erhält, sind Schrott, wenn sie eine Wortgrenze überschreiten.
:
Bearbeitet durch User
Rolf M. schrieb: > Von Strukturen weiß die nix. Aber der Compiler, ihr Superspezialisten. Gepackte Strukturen sind ein Compiler-/Sprachkonstrukt. Das hat mit der CPU überhaupt nichts zu tun. Und der Compiler stellt sicher, dass es zu keinen Traps kommt. Mann mann mann...
MaWin schrieb: > Gepackte Strukturen sind ein Compiler-/Sprachkonstrukt. Das hat mit der > CPU überhaupt nichts zu tun. Eben, genau das schrieb ich ja. Du hattest das Gegenteil behauptet. > Mann mann mann... Dito.
:
Bearbeitet durch User
Rolf M. schrieb: > Was passiert denn, wenn ich über einen Zeiger auf das Element zugreife? > Generell bin ich bei gepackten Strukturen vorsichtig. Die haben bei mir > in der Vergangenheit schon zu sehr merkwürdigen Verhaltensweisen > geführt. Das kommt auf den Zeiger an. Um genau zu sein, darfst du keinen "normalen" Zeiger auf ein Feld in einer gepackten Struktur setzen. Nehmen wir mal folgendes an:
1 | struct __attribute__((packed)) P { |
2 | uint8_t a; |
3 | uint16_t b; |
4 | uint32_t c; |
5 | };
|
Dann wäre die folgende Definition falsch
1 | struct P my_p; |
2 | uint32_t * my_ptr = &(my_p.c) |
my_p.c ist als 32-Bit Integer ohne Alignment definiert während my_ptr als Zeiger auf ein 32 Bit Integer mit Standardalignment deklariert ist. D.h. wenn man (*my_ptr) schreiben würde, dann geht der Compiler davon aus, das die Adresse in my_ptr auf 4-Byte Grenzen ausgerichtet ist. Das geht dann krachen, weil wir ja die Adresse von my_p.c reingeschrieben haben, welche hier nicht auf eine 4-Byte Grenze liegt. Man kann sich aber Zeiger auf solche Integer definieren:
1 | typedef uint32_t uint32_unaligned_t __attribute__((aligned(1))); |
2 | |
3 | struct P my_p; |
4 | uint32_unaligned_t *proper_ptr = &(my_p.c); |
Ich habe gerade mal ein bischen mit den Compilern herumgespielt. Aktuelle GCC Versionen geben jetzt sogar eine Warning aus. War früher nicht so. Allerdings scheint der Compiler in manchen Fällen trotzdem lauffähigen Code zu produzieren. Für mein Beispiel von oben in "-O2" gibt er das korrekte aus, bei -O0 jedoch nicht. https://godbolt.org/z/4x4KfYvz1
Das klingt alles recht murksig. Gerade deshalb meide ich gepackte Strukturen doch lieber. Ich gehöre da eher zu dieser Kategorie: Andreas M. schrieb: > Manche bevorzugen Bytegymnastik. Das bekommt man dann deutlich weniger abhängig vom Compiler und der CPU hin.
(prx) A. K. schrieb: > Wenn der Compiler es unterstützt, die CPU > aber nicht, baut er sich die Zugriffe eben umständlich zurecht. Moin, korrekt und der erzeugte Code kann da teilweise recht ineffizient werden. Hier mal ein Beispiel für eine "normale" und gepackte Struktur für den MSP430 & GCC: https://godbolt.org/z/TKvr5MTEK Gruß, Michael
Rolf M. schrieb: > Das bekommt man dann deutlich weniger abhängig vom Compiler und der CPU > hin. Ja, die "__attribute__" Syntax ist eben GCC specifisch. Wenn die Strukturen gepackt, aber z.b. immer auf einer z.B. 4-Byte grenze im RAM liegen (habe ich hier ganz oft) dann kann man das ebenfalls per attribute syntax definieren. Das bietet Optimierungspotential, weil der GCC dann selbsttändig entscheidet, ob er ein Feld Byte-Weise lesen muss, oder ob er einen normalen Zugriff verwenden kann. Das kann sehr viel Code sparen. Ein byteweises zusammensetzen von 32-Bit sind immerhin 7 Instruktionen, bei einem normalen Zugriff ist das 1 Instruktion + evtl. 1 Instruktion (Byte-Order Anpassung). Das konsequent umgesetzt hat in unseren Firmwaren zwei bis dreistellig kByte Code reduziert. Mit C++ lässt ein Bytegymnastik Parser ja auch schön kapseln. Aber eine wirklich eleganten Weg solche Datenstrukturen für den Transport zu definieren/dekodieren/kodieren hab ich noch nicht gefunden. Alle Ansätze die ich bisher gesehen habe, haben immer irgendwelche "Kanten".
Andreas M. schrieb: > Ich habe gerade mal ein bischen mit den Compilern herumgespielt. > Aktuelle GCC Versionen geben jetzt sogar eine Warning aus. War früher > nicht so. Allerdings scheint der Compiler in manchen Fällen trotzdem > lauffähigen Code zu produzieren. Das ist ein sehr wichtiger Hinweis, Andreas. Ich möchte noch hinzufügen: Man sollte sich nicht darauf ausruhen, das die CPU, die man einsetzt, mit unaligned Zugriffen klar kommt. Gerade beim Cortex-M kann man sich böse Hardfaults eintreten. Ja, die CPU kann unaligned Zugriffe, aber nicht beim LDRD Befehl. Wenn der Compiler meint zwei Speicherzugriffe auf ein Befehl zu optimieren, dann knallt es trotzdem.
Michael F. schrieb: > Hier mal ein Beispiel für eine "normale" und gepackte Struktur für den > MSP430 & GCC: Da hast du allerdings auch vergessen, Optimierungen einzuschalten. Mit -O1 sieht der Code schon erheblich kürzer aus.
:
Bearbeitet durch User
Rolf M. schrieb: > Da hast du allerdings auch vergessen, Optimierungen einzuschalten. Jein... ;-) Mit abgeschalteter Optimierung bekommt man zugegebenermaßen den worst-case, aber das Beispiel zeigt (meiner Meinung nach) ganz gut, dass man sich nicht immer blind darauf verlassen sollte, dass der Compiler den geschriebenen C/C++ Code schon irgendwie sinnvoll implementiert. Ein paar gesparte Bytes im RAM durch eine gepackte Struktur können je nach MCU Architektur zu langsameren & größeren Code führen und man sollte sich dieser möglichen Auswirkungen bewusst sein, bzw. im Zweifelsfall den generierten Code anschauen :-) Gruß, Michael
Rolf M. schrieb: > Eben, genau das schrieb ich ja. Du hattest das Gegenteil behauptet. Nein, das habe ich nicht. Lies bitte meine Texte und versuche sie auch zu verstehen.
MaWin schrieb: > Rolf M. schrieb: >> Eben, genau das schrieb ich ja. Du hattest das Gegenteil behauptet. > > Nein, das habe ich nicht. Lies bitte meine Texte und versuche sie auch > zu verstehen. Vielleicht solltest du dann schreiben, was du meinst und nicht was anderes. Zur Kommunikation gehören immer zwei Seiten. Hier steht es schwarz auf weiß: MaWin schrieb: > Rolf M. schrieb: >> Allerdings kann nicht jede CPU problemlos auf Datentypen mit falschem >> Alignment zugreifen. > > Bei gepackten Strukturen schon. Wenn eine CPU nicht mit falschem Alignment zugreifen kann, dann kann sie das eben nicht, auch in gepackten Strukturen nicht. Der Compiler kann den Code ggf. so umbauen, dass eben keine falsch alignten Zugriffe auf den Speicher passieren, aber das ist eine andere Geschichte. … wie du dann im Anschluss ja auch ganz richtig schreibst: MaWin schrieb: > Gepackte Strukturen sind ein Compiler-/Sprachkonstrukt. Das hat mit der > CPU überhaupt nichts zu tun.
:
Bearbeitet durch User
MaWin schrieb: > Rolf M. schrieb: >> Eben, genau das schrieb ich ja. Du hattest das Gegenteil behauptet. > > Nein, das habe ich nicht. Lies bitte meine Texte und versuche sie auch > zu verstehen. Ich habe deine Texte nochmal gelesen und verstehe sie wohl genauso wie Rolf. Wenn das nicht das ist, was du sagen wolltest musst du vielleicht ein paar mehr Worte schreiben. MaWin schrieb: > Rolf M. schrieb: >> Allerdings kann nicht jede CPU problemlos auf Datentypen mit falschem >> Alignment zugreifen. > > Bei gepackten Strukturen schon. Wie soll man das anders interpretieren als "Bei gepackten Strukturen kann die CPU problemlos auf Datentypen mit falschem Alignment zugreifen." Wenn du sagen wolltest "Der Compiler regelt das für gepackte Strukturen", dann solltest du das auch sagen ...
Rolf M. schrieb: > Hier steht es schwarz auf weiß: Ja. Dann zitiere es bitte auch vollständig. Rolf M. schrieb: > Andreas M. schrieb: >> Wenn man dann die entsprechenden uint<n>_t Typen zusammen mit gepackten >> Strukturen verwendet dann funktioniert das ganz gut und bleibt lesbar. > > Allerdings kann nicht jede CPU problemlos auf Datentypen mit falschem > Alignment zugreifen. Darauf antwortete ich: MaWin schrieb: > Bei gepackten Strukturen schon. DU bist derjenige, der fälschlicherweise die CPU ins Spiel brachte. Die CPU hat überhaupt nichts mit gepackten Strukturen zu tun und es kommt auch nicht zu Traps, wie von DIR behauptet.
mh schrieb: > Wie soll man das anders interpretieren Indem man das gesamte Gespräch liest, und nicht nur den Teil, der extra so herausgepickt wurde, um mich dumm dastehen zu lassen. Antworten bedingen immer, dass man die beantworteten Texte auch liest. Und zwar alle relevanten, rekursiv.
MaWin schrieb: > DU bist derjenige, der fälschlicherweise die CPU ins Spiel brachte. Mit anderen Worten: Du hast mein Posting nicht richtig gelesen, beschwerst dich aber, dass ich deins angeblich nicht richtig gelesen hätte. Und was sollte daran falsch sein? Stimmt es etwa nicht, dass nicht jede CPU problemlos auf Datentypen mit falschem Alignment zugreifen kann? Klar mögen Compiler das vielleicht geradeziehen, aber das hättest du ja schreiben können. Und wie auch schon erwähnt wurde, ist das nichts standardisiertes, sondern vollständig compilerspezifisch. Also ist die pauschale Aussage, dass "der Compiler" das macht, auch so nicht richtig. > Die CPU hat überhaupt nichts mit gepackten Strukturen zu tun und es > kommt auch nicht zu Traps, wie von DIR behauptet. Zunächst einmal habe ich Traps überhaupt nicht erwähnt, also erzähl nicht, ich hätte da irgendwas behauptet. Und zweitens habe ich tatsächlich schon das Problem gehabt, dass die CPU in eine Exception gelaufen ist wegen der Nutzung von "packed"-Struckturen, die mir über eine Library mit reingekommen sind. Also offenbar kann es "der Compiler" wohl nicht (um es klar zu machen: Ich meine damit, dass die Menge der Compiler, die das nicht können, >0 ist).
:
Bearbeitet durch User
Rolf M. schrieb: > Stimmt es etwa nicht, dass > nicht jede CPU problemlos auf Datentypen mit falschem Alignment > zugreifen kann? Ja. Für eine gepackte Strukturen stimmt das nicht. In einer gepackten Struktur kann die CPU problemlos auf Elemente mit falschem Alignment zugreifen, weil der Compiler das garantiert und entsprechenden Code generiert. Du behauptest weiterhin, dass das anders ist. Du liegst leider falsch.
MaWin schrieb: > mh schrieb: >> Wie soll man das anders interpretieren > > Indem man das gesamte Gespräch liest, und nicht nur den Teil, der extra > so herausgepickt wurde, um mich dumm dastehen zu lassen. Ich habe deinen Vollständigen Beitrag zitiert. Wenn sich deine Antwort in diesem Beitrag auf mehr als den einen dort von dir zitierten Satz bezieht, solltest du mehr zitieren. > Antworten bedingen immer, dass man die beantworteten Texte auch liest. > Und zwar alle relevanten, rekursiv. Wenn das auf deine Beiträge zutrifft, solltest du das dazu schreiben. Vor allem, was du für RELEVANT hälst. Nur so als Tipp: Du könntest z.B. zitieren, was du für relevant hälst.
mh schrieb: > solltest du mehr zitieren. Nur keinen Fehler zugeben wollen, gell? Du könntest Rolf sein.
MaWin schrieb: > Rolf M. schrieb: >> Stimmt es etwa nicht, dass >> nicht jede CPU problemlos auf Datentypen mit falschem Alignment >> zugreifen kann? > > Ja. Für eine gepackte Strukturen stimmt das nicht. > In einer gepackten Struktur kann die CPU problemlos auf Elemente mit > falschem Alignment zugreifen, weil der Compiler das garantiert und > entsprechenden Code generiert. Der Compiler baut in diesem Fall den Code so um, dass die CPU keine Zugriffe mit falschem Alignment mehr ausführt. > Du behauptest weiterhin, dass das anders ist. Du liegst leider falsch. Ich denke, wir haben da einfach eine unterschiedliche Betrachtungsweise. Du betrachtest es von der abstrakten Sprachebene aus (wo das aber nicht standardisiert ist, sondern eben gänzlich vom verwendeten Compiler abhängt), ich aus Sicht der CPU selbst und welche Speicherzugriffe sie tatsächlich ausführt. Um ersteres kann sich der Compiler in gewissen Grenzen und mit entsprechenden Nachteilen bei Codegröße und Laufzeit kümmern, letzteres ist einfach eine Gegebenheit. Aber von mir aus können wir uns einigen auf ein: "Die CPU kann es von sich aus nicht, aber der Compiler kann eine entsprechende Krücke einbauen, um diese Einschränkung zum umgehen". MaWin schrieb: > Nur keinen Fehler zugeben wollen, gell? Du könntest Rolf sein. Ich kann Fehler durchaus zugeben, wenn ich denn welche gemacht hab und habe das hier im Forum auch schon öfters getan.
Rolf M. schrieb: > Aber von mir aus können wir uns einigen auf ein: "Die CPU kann es von > sich aus nicht, aber der Compiler kann eine entsprechende Krücke > einbauen, um diese Einschränkung zum umgehen". Genau. Und der Mechanismus "entsprechende Krücke" nennt sich packed struct. Somit kann bei einer packed struct kein "Problem beim Zugriff", oder wie du es auch immer formulieren willst, auftreten. Darum ging es ja nur. Es gibt kein "Problem beim Zugriff".
MaWin schrieb: > Somit kann bei einer packed struct kein "Problem beim Zugriff", oder wie > du es auch immer formulieren willst, auftreten. Doch, natürlich kann ein Problem auftreten. Das Herumwirtschaften um die Gegebenheiten der Hardware löst zwar das Problem der Möglichkeit des Zugriffs, dies aber natürlich auf Kosten der Zugriffszeit. Und auch das kann natürlich ein Problem sein bzw. werden.
c-hater schrieb: > Das Herumwirtschaften um die > Gegebenheiten der Hardware löst zwar das Problem der Möglichkeit des > Zugriffs, dies aber natürlich auf Kosten der Zugriffszeit. Dann bin ich ja mal gespannt, wie du das besser machen willst. Zeig doch mal konkret.
MaWin schrieb: > Dann bin ich ja mal gespannt, wie du das besser machen willst. Indem ich eine Sprache verwende, die eine feingranulare Eingriffsmöglichkeit auf die Speicherorganisation von Strukturen von Haus aus anbietet (oder zumindest ermöglicht) und nicht dieses unsägliche C. Der Witz ist: es gibt viele solche Sprachen. Deine idiotische Fixierung auf diesen unsäglich schlechten Macroassembler namens C nimmt dir die Sicht darauf. Am C-Tellerrand ist für dich halt Ende Gelände.
Hast wieder Freigang heute? Dein Gesabbel wird immer schlimmer.
c-hater schrieb: > Indem ich eine Sprache verwende, die eine feingranulare > Eingriffsmöglichkeit auf die Speicherorganisation von Strukturen von > Haus aus anbietet Dann kannst du ja einmal ein Beispiel machen.
c-hater schrieb: > Indem ich eine Sprache verwende, die eine feingranulare > Eingriffsmöglichkeit auf die Speicherorganisation von Strukturen von > Haus aus anbietet Also Bitfelder in C. Feingranularer geht nicht. ;-)
MaWin schrieb: > Dann kannst du ja einmal ein Beispiel machen. Dot.Net z.B. stellt erschöpfende Möglichkeiten dafür bereit (natürlich in jeder Dot.Net-Sprache, nur halt mit leicht variierender Syntax). Das gibt es als Hauptsache ein StructLayout-Attribut mit den Hauptparametern LayoutKind und Pack und und im Extremfall Attribute für die Steuerung der Ausrichtung jedes einzelnen Feldes (für Strukturen mit LayoutKind.Explizit).
c-hater schrieb: > Dot.Net z.B. stellt erschöpfende Möglichkeiten dafür bereit Das ist ja nett. Und dann kannst du uns auch bestimmt zeigen, wo das den enormen Vorteil bei der Ausführungsgeschwindigkeit gegenüber C bietet. Kleiner Tipp: Auch damit kann die CPU ein unaligned-Feld nicht als Ganzes lesen. Gegen angebliche Performanceprobleme bei C dann DotNet vorzuschlagen, hat schon etwas ulkiges. Im Gegensatz zu deiner Annahme bin ich kein C-Jünger. Ich bin aber auch kein C-Hater.
Oliver S. schrieb: > Das die stdint.h-Datentypen keine systemübergreifende Kompatibilität > beim Datenaustausch bieten, und auch nie dafür gedacht waren, dürfte so > ziemlich jedem klar sein. Nur dir nicht. O danke für die Blumen, du Kurzsichtiger. Der eigentliche Anlaß war, daß es in C keine wirklich sauberen Definitionen für Integer-Größen gibt. Lediglich einige gummiweiche Ansagen, daß z.B. int mindestens 16 Bit breit sein soll. Ich kann ja verstehen, daß man in den allerersten Anfangszeiten der ganzen Mikrorechnerei sich diverse Optionen offenhalten wollte, aber sowas wird nach rund 40 Jahren immer mehr zur Bürde. Also stand eigentlich ein ganz kleines Renovieren von C an, dahingehend, daß die C-Programmierer Integer-Datenbreiten haben wollen, auf die sie sich verlassen können. Also (wieder zum Beispiel) "int ist GENAU 16 Bit breit" oder so. Aber zu so einer weltbewegenden Neuerung konnte man sich nicht durchringen, weswegen der Berg zwar gekreißt hat, aber nur einen Vorschlag zu einer Headerdatei dabei gebar, die bittesehr ein jeder Hersteller auf seine Produkte anpassen möge, damit (wieder z.B.) bei int auch wirklich 16 Bit herauskommen möge. Es ist zwar lächerlich und bietet keinerlei Fortschritt, aber es war ohne jegliche Änderung an den Compilern machbar. Das Ärgerliche daran ist, daß es derart in Mode gekommen ist, daß viele Programmierer meinen, in C jetzt echte neue Integertypen zu haben und die Gedankenlosigkeit geht so weit, daß selbst für Textzeichen uint8_t verwendet werden. Als ob ein String "Ottokar" zum Integer-Rechnen gedacht sei. Und eben dieselben Leute meinen, daß dieses zum Sicherstellen der Kompatibilität da sei. Also wenn jemand besagten 'Ottokar' in einige char packt, gibt es Kompatibilitätsprobleme, bei uint8_t jedoch nicht. Ich hab da so den Eindruck, daß die Menschheit zu allen Zeiten ein gewisses Maß an Aberglauben für ihr Seelenheil braucht. Und um zu zeigen, daß man bereits bei einer Plattform mit anderer Endianess eine Bauchlandung macht, auch wenn man alle seine Variablen brav nach der neuesten Mode deklariert hat, ist die Diskussion über Endianess aufgekommen. Dem Mathematiker ist es klar: sobald man eine Theorie auch nur in einem einzigen Fall widerlegt hat, ist sie insgesamt widerlegt. So auch eben das ganze Geschwurbel über all diese xintyy_t und die besagte Headerdatei. Richtig ist, daß man sich als Programmierer SELBST Gedanken machen muß, was für Integerbreiten für eine bestimmte Aufgabe am ehensten geeignet sind, was ja eben auch das Thema dieses Threads ist: "Möglichst viel uint8_t verwenden?". Da sucht jemand, der offenbar orientierungslos ist, nach einem Geländer, das ihn auf den rechten Weg geleiten soll. Und zwar möglichst ohne daß er selbst drüber nachdenken muß W.S.
W.S. schrieb: > Der eigentliche Anlaß war Du musst weniger, oder mehr nehmen. Egal was. Die Menge stimmt nicht.
Erwin schrieb: > Franz schrieb: > >> Bei einem 32-Bit-Mikrocontroller, wie z.B. dem >> ESP32, ist ja nach wie vor der RAM oft das Problem, wenn Projekte >> umfangreich werden. > > Programmieren lernen hilft wohl am besten ... Micropython ist doch so grossartig und die Zukunft. /s
W.S. schrieb: > weswegen der Berg zwar gekreißt hat, aber nur einen > Vorschlag zu einer Headerdatei dabei gebar NB: Das ist kein Vorschlag, sondern Teil des Sprachstandards. Ob man etwas im Code des Compilers festlegt, oder in einem Headerfile, ist höchstens für Sprachpuristen von Bedeutung, denen der Compiler nicht gross genug sein kann. Man hätte eine völlig neue Sprache definieren können, die alle bekannten Fehler rauswirft. Dann hätten einige Dutzend Leute die neue Sprache verwendet, die zu bestehendem Code inkompatibel ist, und der grosse Rest hätte weiterhin das alte hässliche C verwendet. Und weil dieser Effekt schon vorher bekannt war, hat das Gremium sich entschlossen, das alte hässliche C ohne Verlust von Kompatibilität weiterzuentwickeln. Das war deren Zuständigkeit. Und man überliess es anderen Sprachschöpfern und Gremien, neue Sprachen zu definieren, die alles richtig machen. Der Rest ist die Freiheit des Anwenders, sich das Genehme auszusuchen. So lange die Leute aber an C hängen, wie die Alkis an der Flasche, so lange wird ein Gremium benötigt, dass C behandelt. Und so lange werden diese Alkis sich in Foren lauthals darüber beklagen, dass sie von der Welt dazu gezwungen werden.
:
Bearbeitet durch User
(prx) A. K. schrieb: > Und so lange werden > diese Alkis sich in Foren lauthals darüber beklagen, dass sie von der > Welt dazu gezwungen werden. Ist das jetzt die Schlange , die sich selber in den Schwanz beißt? ---GRINS....
W.S. schrieb: > Der eigentliche Anlaß war, daß es in C keine wirklich sauberen > Definitionen für Integer-Größen gibt. Lediglich einige gummiweiche > Ansagen, daß z.B. int mindestens 16 Bit breit sein soll. Ja. Ist seit Erfindung der Sprache so, und wird sich auch nie ändern. Lebe damit, oder nimm eine andere Programmiersprache. Sich in jedem zweiten Beitrag zum Thema drüber aufzuregen ist kindisch. Nicht akzeptieren zu wollen, daß die stdint-Datentypen auch Teil des C-Standards sind, ist genauso kindisch. Aber das alles ist ja nun schon hundertfach mit dir durchgekaut worden. Oliver
(prx) A. K. schrieb: > NB: Das ist kein Vorschlag, sondern Teil des Sprachstandards. Ob man > etwas im Code des Compilers festlegt, oder in einem Headerfile, ist > höchstens für Sprachpuristen von Bedeutung, Das ist in sich widersinnig - allerdings kommt es der Überschrift dieses Threads durchaus nahe: Laßt uns alles Rechnen in unsigned char erledigen, dann kann man den Compiler vereinfachen und all die Überträge, Vorzeichenbehandlung usw. in einer in C geschriebenen Bibliothek erledigen. Das würde sogar die Sache mit der Endianess erledigen und wozu braucht man eigentlich 32 Bit Controller? Nein, das wäre das Tollhaus. Unterschiedliche Datenbreiten gebrauchen auch unterschiedliche Maschinenbefehle und das richtig zu übersetzen ist Obliegenkeit des Compilers. Es ist also der Compiler UND SONST KEINER, der einen bestimmten Datentyp kennen und in passende Maschinenoperationen übersetzen muß. Punkt. Dabei ist es vollständig schnurz, ob jemand eine Headerdatei zum Sprachstandard hinzuzählt oder nicht. W.S.
W.S. schrieb: > Es ist also der Compiler UND SONST KEINER, der einen > bestimmten Datentyp kennen und in passende Maschinenoperationen > übersetzen muß. Punkt. Genau das tut er auch dann, wenn der Autor des Compilers es vorzog, einen Typedef wie uint8_t in ein Headerfile zu schreiben, statt direkt in den Quellcode des Compilers. Das ist gleichwertig. > ob jemand eine Headerdatei zum > Sprachstandard hinzuzählt oder nicht. Wenn für dich nur das Verhalten des EXE-Files zählt, nicht aber der Sprachstandard, ist das kein Problem der Sprache, sondern eines von dir persönlich. Solche Header sind fester Teil der Compiler-Suite und der Inhalt ist per ISO-Dokument vorgeschrieben.
:
Bearbeitet durch User
W.S. schrieb: > Es ist also der Compiler UND SONST KEINER, der einen > bestimmten Datentyp kennen und in passende Maschinenoperationen > übersetzen muß. Punkt. Die Forderung ist so selbstverständlich, daß man das nicht groß erwähnen muß. Und, auch wenn es dich vielleicht überrascht: Jeder C-Compiler kennt alle seine ihm bekannten Datentypen und übersetzt die in passende Maschinenoperationen. Dein "UND SONST KEINER" ist trotzdem grundfalsch. Derjenige, der den Compiler benutzt, muß die Datentypen genauso gut kennen. Was einen dann über ein paar weitere und durchaus sinnvolle Gedankenschritte zur stdint.h bringt. Oliver
:
Bearbeitet durch User
Oliver S. schrieb: > Jeder C-Compiler > kennt alle seine ihm bekannten Datentypen und übersetzt die in passende > Maschinenoperationen. Tja und das sind char, int und float mit diversen Attributen wie signed, long, double usw. Siehste. W.S.
W.S. schrieb: > Nein, das wäre das Tollhaus. Es ist sehr lustig zu lesen, wie du deine kruden Theorien immer weiterentwickelst, nur um Recht behalten zu können. Inhaltlich diskutiere ich mit dir natürlich nicht mehr, weil das Thema bereits erschöpfend behandelt wurde. Aber mache du bitte weiter. Es amüsiert mich sehr.
W.S. schrieb: > Tja und das sind char, int und float mit diversen Attributen wie signed, > long, double usw. > Siehste. Wenn Du als einzig gültige Sprachdefinition von C die von C89 akzeptierst, dann ja. In neueren C-Standards (ab C99) gehört aber stdint.h und das, was da drinsteht, zum Sprachumfang. Siehste.
W.S. schrieb: > Tja und das sind char, int und float mit diversen Attributen wie signed, > long, double usw. > Siehste. Und wenn du jetzt einen 16 bit signed int brauchst, welche nimmst du nun davon? Ich nehme da einen int16_t. Das "typedeft" dann die implementationsspezifische stdint.h auf den passenden Typ des Compilers (short, int, ...), und alle haben, was sie brauchen. Siehste. So einfach kann das Leben sein. Oliver
Oliver S. schrieb: > und alle haben, was sie brauchen. Fast alle. Klar ist, dass ihm nicht reicht, wenn es funktioniert, sondern es muss auch auf genau seine Art funktionieren. Auch wenn wir bisher nicht wissen, wie die aussieht. Ich erinnere mich vage, dass Ada in dieser Richtung einige Möglichkeiten hat. Ada wurde ja u.A. für eine einheitliche Entwicklung von Steuerungssystemen definiert. PS: Bis hin zur Bitorder: "For record subtypes, GNAT permits the specification of the Bit_Order attribute." https://docs.adacore.com/gnat_rm-docs/html/gnat_rm/gnat_rm/representation_clauses_and_pragmas.html
:
Bearbeitet durch User
PPS: Kennt hier jemand jemanden, der jemanden kennt, der Ada nutzt?
Oliver S. schrieb: > Und wenn du jetzt einen 16 bit signed int brauchst, welche nimmst du nun > davon? Bei einem 32 Bit µC? Da nehme ich für was Lokales einen long. Kommt ja auf den Stack. Fertig. Ansonsten einfach einen int - und zwar ohne Umweg über irgendwelche Headerfiles. Ich schrieb ja schon, daß diese letzte Marotte nichts Neues gebracht hat. Bei dem typischen Wildwuchs a la U32, U16, U8 und so war klar zu erkennen, daß dies der Bequemlichkeit der Schreiber diente, schließlich ist U32 viel kürzer als unsigned long int und den typischen C Programmierern ist jedes Textzeichen eigentlich schon zuviel. W.S.
Dann werf ich noch mal folgendes in den Raum:
1 | uint_fast8_t |
1 | uint_least8_t |
Mit den *_fast*_t typen bekommst du immer den schnellsten Datentyp der dir mindestens 8,16,32 oder 64bit liefert. Auf einer typischen 32bit architektur wären das dann 32bit für den uint_fast8_t. Achja, du kannst ggf. gewaltig code sparen und Geschwindigkeit gewinnen, wenn du Globale in strukturen/klassen legst. Wenn deine Zielarchitektur indirekte Addressierung kann (z.B. der Arm Corex), dann braucht der dann nur 1x die Basisadresse laden und holt sich dann die Daten passend von dort. Das funktioniert dann ähnlich wie vom Stack. 8bit Architekturen sind etwas eigen, da C 16bit integer erwartet. Aber ab einer 16bit Architektur macht es absolut keinen Sinn über Datentypen zu sinnieren. Solange "dein" int groß genug ist (also je nach Architektur) ist das normalerweise das effektivste, da üblicherweise Datenbusse, Register, Befehlsatz, ... auf diese Größe ausgelegt sind. 73
MaWin schrieb: > Kleiner Tipp: Auch damit kann die CPU ein unaligned-Feld nicht als > Ganzes lesen. Natürlich nicht. Aber ich HABE DIE MACHT, es auch in einer weitgehend "packed" organisierten Struktur korrekt zu alignen. Wenn es halt wichtig genug ist, um die Abweichung vom allgemeinen Ziel der Speicherersparnis für diese Struktur in genau diesem einen Punkt aufzuweichen. Dem Programmierer gehört die Macht, nicht irgendeinem verschissenen Compiler. Und schon garnicht den strukturell (durch die Sprache selber) massiv behinderten C-Compilern.
c-hater schrieb: > Aber ich HABE DIE MACHT Diese C-Hasserei ist schon ziemlich krankhaft bei dir, meinst du nicht?
MaWin schrieb: > Diese C-Hasserei ist schon ziemlich krankhaft bei dir, meinst du nicht? Aha, rein garnix zu den unbestreitbaren Fakten... Kann es sein, dass vielmehr deine Verteidigung von C einigermaßen krankhaft ist? Hast du da mal drüber nachgedacht?
W.S. schrieb: > Ich schrieb ja schon, daß diese letzte Marotte nichts Neues gebracht > hat. Bei dem typischen Wildwuchs a la U32, U16, U8 und so war klar zu > erkennen, daß dies der Bequemlichkeit der Schreiber diente, schließlich > ist U32 viel kürzer als unsigned long int und den typischen C > Programmierern ist jedes Textzeichen eigentlich schon zuviel. > > W.S. Du hast absolut keine Ahnung von Datentypen. Das wird immer offensichtlicher. int, long usw. sind Architekturabhängig. uint8_t, uint16, uint32_t usw. sind immer so breit wie der Bezeichner aussagt. Das könntest du mit sizeof() selbst auf verschiedenen Plattformen ausprobieren. Mach es einfach bevor du dich weiter blamierst.
c-hater schrieb: > Aha, rein garnix zu den unbestreitbaren Fakten... Warum auch. Du hast Recht. Ich hingegen meine nur, dass es keine Rolle spielt, wie C packed structs genau implementiert. > Kann es sein, dass vielmehr deine Verteidigung von C einigermaßen > krankhaft ist? Wie ich bereits schrieb: Ich verteidige C keineswegs. Wozu auch. C ist eine ziemlich schlechte Sprache. Trotzdem nutze ich sie, wo es Sinn ergibt. Und dort nutze ich auch packed structs. Die angeblichen Performanceprobleme, die es deiner Meinung nach dabei geben soll, sind mir noch nie untergekommen. Die angeblichen Performanceverluste sind in realen Programmen kaum vorhanden und praktisch nicht relevant.
MaWin schrieb: > Trotzdem nutze ich sie, wo es Sinn ergibt. Und dort nutze ich auch > packed structs. Die angeblichen Performanceprobleme, die es deiner > Meinung nach dabei geben soll, sind mir noch nie untergekommen. Das hängt einerseits von der Plattform ab, inwieweit misaligned access erlaubt ist, oder zum Absturz führt und daher vom Compiler ersetzt werden müssen. Wenn deine Szenarien freilich mehr im Controller-Sektor liegen, treten manche Gründe für Performance-Einbrüche nicht in Erscheinung. So können bei Highend-Prozessoren (sowas wie in PC und Smartphone) und einem Mix aus Lesen- und Schreiboperationen auf der Ebene der Mikroarchitektur böse Einbrüche auftreten. Und zwar auch dann, wenn die CPU das kann.
:
Bearbeitet durch User
MaWin schrieb: > C ist > eine ziemlich schlechte Sprache. Nicht mehr und nicht weniger sage ich immer und immer wieder. Will nur niemand hören. Wird sogar als "krankhaft" eingestuft, das zu sagen. So auch von dir, nur wenige Postings höher... Wer ist hier krank?
c-hater schrieb: > sage ich immer und immer wieder. Es gibt neben dem, was man zu sagen meint, auch das, was andere dabei wahrnehmen, und wie man es sagt. Und ganz besonders fällt dabei eine gewisse Penetranz auf.
(prx) A. K. schrieb: > c-hater schrieb: >> sage ich immer und immer wieder. > > Es gibt neben dem, was man zu sagen meint, auch das, was andere dabei > wahrnehmen, und wie man es sagt. Und ganz besonders fällt dabei eine > gewisse Penetranz auf. Vor allem dann, wenn jedes mal "*c-hater* schrieb" drüber steht ;-)
(prx) A. K. schrieb: > Und ganz besonders fällt dabei eine > gewisse Penetranz auf. Hihi. Genau so wie mir die Penetranz der C-Apologeten auffällt,, die halt mit unglaublicher Zähigkeit immer und immer wieder behaupten, dass C alle Probleme löst und die C-Compiler so tierisch gut sind, dass sie immer besseren Code liefern als der beste Mensch das könnte. Diese überaus penetranten Lügen waren der Anlass zur Geburt des "c-hater" und bis heute haben diese Lügen nicht nachgelassen, und erhalten somit auch den "c-hater" unendlich am Leben... Usache->Wirkung. So einfach ist das.
c-hater schrieb: > Diese überaus penetranten Lügen waren der Anlass zur Geburt des > "c-hater" und bis heute haben diese Lügen nicht nachgelassen, und > erhalten somit auch den "c-hater" unendlich am Leben... Das klingt in der Tat etwas krankhaft.
c-hater schrieb: > Hihi. Genau so wie mir die Penetranz der C-Apologeten auffällt, Es gibt neben mir eine Menge Leute, die zu C jene Ambivalenz pflegen, wie sie auch in diesem Beitrag ausgedrückt wird: Beitrag "Re: 32-Bit-µC: Möglichst viel uint8_t verwenden?" Man kann C verwenden und damit zurecht kommen, ohne es zu mögen. Deinen Apologeten begegne ich weit seltener als Pragmatikern. Jenen, die es kennen und nutzen, ohne davon so begeistert zu sein, wie du suggerierst. > die C-Compiler so tierisch gut sind Hier beziehst du dich auf Assembler vs Compiler Diskussionen, die vor einigen Jahren recht heftig abliefen und einen Teilnehmer so in Rage brachten, dass er aus dem Forum flog. Allerdings war darin zwar C jene Sprache, die in der Diskussion gegen Assembler stand. Grundsätzlich waren C Compiler darin aber nur Platzhalter für Compiler allgemein. Es ging um die Alternative zwischen Assembler und jedweder Art von Compiler, nicht speziell um C. In diesem Thread geht es jedoch nicht um Assembler gegen Compiler allgemein, sondern um bestimmte Eigenschaften der Sprache C.
:
Bearbeitet durch User
Die Kritik an der schwachen Definition der C Typen begleitete C schon früh, und führte auch zu einem jener seltenen Fälle, in denen bestehender Code gebrochen wurde. Nämlich beim Thema sign- vs value-preserving, was aus K&R nicht eindeutig hervorging und deshalb verschieden implementiert wurde. ANSI C legte das fest. C wurde nie als 1000-jährige Sprache erfunden, sondern für einfache Compiler auf ressourcenarmen Minicomputern der Entstehungszeit. Wer von 12-Pass Compilern damaliger Mainframes gehört hat, kann sich vielleicht vorstellen, was ich meine. Aufgrund des Erfolgs von Unix im Kontext von Universitäten nahm die Verbreitung und Kenntnis zu. Die relativ geringe Komplexität von C Compilern führte dann auch dazu, dass sie für Mikrocontroller genutzt wurden. Es war kein Lebenswerk, einen Compiler zu bauen. Zum Vergleich: Der erste Ada Compiler galt als abschreckender Moloch, der schon allein dadurch die Verbreitung der Sprache behinderte. Das war von Anfang bis heute kein ideologischer Ansatz, sondern ein rein pragmatischer. Man brauchte was, man hatte oder kannte was, man nutzte es. Die meisten Leute schaffen das allerdings, ohne sich täglich darüber öffentlich zu ergiessen, wie schlecht ihre Werkzeuge doch wären.
:
Bearbeitet durch User
c-hater schrieb: > dass C alle Probleme löst und die C-Compiler so tierisch gut sind Dir wird sicher auffallen, dass niemand ebensolche Behauptungen in diesem Thread getroffen hat. Ich kenne nur einen, der (alleine schon mit seinem Nick) herumhetzt. Es ist der C-Hater.
(prx) A. K. schrieb: > MaWin schrieb: >> Trotzdem nutze ich sie, wo es Sinn ergibt. Und dort nutze ich auch >> packed structs. Die angeblichen Performanceprobleme, die es deiner >> Meinung nach dabei geben soll, sind mir noch nie untergekommen. > > Das hängt einerseits von der Plattform ab, inwieweit misaligned access > erlaubt ist, oder zum Absturz führt und daher vom Compiler ersetzt > werden müssen. Ich sage nur SIMD Befehle auf x86... da willst du sogar deine daten auf cachelines ausrichten. Oder unaligned access auf ARM7, ARM9, Cortex-M0... die schmieren einfach ab, wenn du das machen würdes. Am Cortex-m3 geht unaligned-access grundsätzlich - es gibt aber einen "penalty-cycle". 73
Hans W. schrieb: > Am Cortex-m3 geht unaligned-access grundsätzlich - es gibt aber einen > "penalty-cycle". Ja, ein einzelner Straftakt ist aber der harmlose Teil. Übler wird es bei Out-Of-Order Cores, wenn ein Load auf einen Store folgt, der nicht vom Store Buffer bedient werden kann (=> store to load forwarding). Sowas gibts beispielsweise bereits bei einem Byte-Store und nachfolgendem Word-Load von der gleichen Adresse. Das kann zweistellige Takte kosten und kann bei Misalignment leicht auftreten.
:
Bearbeitet durch User
Hans W. schrieb: > Ich sage nur SIMD Befehle auf x86... da willst du sogar deine daten auf > cachelines ausrichten. SSE hat eigens ein paar Befehle, um bei Misalignment gepflegt auf die Schnauze fliegen zu dürfen. Sowas wie MOVDQA (aligned) vs MOVDQU (unaligned). Und bei AVX-512 ist der Unterschied zwischen der Grösse der Daten und der Grösse der Cachelines ohnehin nicht mehr arg gross.
:
Bearbeitet durch User
Hans W. schrieb: > Am Cortex-m3 geht unaligned-access grundsätzlich - es gibt aber einen > "penalty-cycle". Nein. Nicht in Strictly Ordered oder Device Memory getaggten Speicherbereichen. Z.b. im I/O Speicher der Peripherien.
W.S. schrieb: > Der eigentliche Anlaß war, daß es in C keine wirklich sauberen > Definitionen für Integer-Größen gibt. Genau das ist eine der wesentlichen Stärken von C. Nur so war und ist es möglich, eine C-Standard-Lib bereitzustellen, die auf allen Architekturen - angefangen vom Z80 bis zum Supercomputer in den Top-10 - portabel und mit der für den Host optimalen Performance läuft. Es wurde nämlich (meist) darauf geachtet, dass ein Integer der "natürlichen Bitbreite" der verwendeten CPU entspricht. Das ist ein Vorteil sondergleichen. Hätte man damals den Integer als 16-Bit festgetackert, würden C-Programme heute auf 32- oder 64-Bit Systemen grottenlahm laufen, weil sie mit einer solchen Altlast nicht optimal arbeiten können. Die meisten C-Standard-Funktionen arbeiten mit Integern und Pointern, die eben nicht in der Bitbreite festgelegt sind. Und das ist auch gut so. denn sie arbeiten mit denjenigen Typen, mit denen sie optimal umgehen können. So laufen auch heute noch Programme aus den 80ern ohne Code-Änderung, welche auf 64-Bit-Systemen ganz normal mit mehreren GB großen Dateien umgehen können. Wäre damals ein Integer auf 16 Bit festgetackert worden, hätte man die alle umschreiben müssen. Du hast Probleme mit der C-Standard-Lib, weil Du diese als nicht zum C-Sprachumpfang gehörend betrachtest. Deshalb ignorierst Du auch stdint.h und die damit verbundenen Typen uint8_t, uint16_t usw. Doch das ist ein Irrtum. Ohne C-Standard-Lib hast Du gar nichts, noch nichtmals stdio.h. Wenn Du stdint.h ablehnst, dann sei wenigstens so konsequent und mache auch um die stdio.h, string.h und alle weiteren einen großen Bogen. Schon mal putchar() oder strlen() verwendet? Warum? Schreibs Dir doch selber, so wie Du das auch mit U8, U16 usw. machst und auch noch predigst! Lächerlich ist dabei, dass Du für jeden Host (Target) Deine eigens definierten Typen immer wieder anpassen musst. Damit führst Du Deine Argumentation doch ad absurdum. Ein include von stdint.h reicht. Dann hast Du Deine festgetackerten Typen. Und ja, ohne die Includes der C-Standard-Lib kann man nicht arbeiten - auch Du nicht. P.S. Wo Du doch so ein Pascal-Fan bist: Auszug aus https://wiki.freepascal.org/Integer : The size of an integer is dependent upon the bit size of the target machine to which the compiler is to generate code (32 bit or 64 bit), the type of compiler (16-bit, 32-bit or 64-bit), and upon compiler switches in some cases. Ohne die flexible Definition hätte Pascal (und deren Nachfolger Modula-2/Oberon) nicht überleben können, sondern wären allesamt vorzeitig gestorben.
:
Bearbeitet durch Moderator
Frank M. schrieb: > Es wurde nämlich (meist) darauf geachtet, dass ein Integer der > "natürlichen Bitbreite" der verwendeten CPU entspricht. Das ist ein > Vorteil sondergleichen. Hätte man damals den Integer als 16-Bit > festgetackert, würden C-Programme heute auf 32- oder 64-Bit Systemen > grottenlahm laufen, weil sie mit einer solchen Altlast nicht optimal > arbeiten können. Die meisten C-Standard-Funktionen arbeiten mit Integern > und Pointern, die eben nicht in der Bitbreite festgelegt sind. Und das > ist auch gut so. Stell dir vor, jemand hätte size_t auf 16 Bit festgenagelt :-)
Wissender schrieb: > int, long usw. sind Architekturabhängig. Ja eben. Genau DAS war es, was ich genannt habe. Und alle Alias, die in irgendeiner Headerdatei erzeugt werden, wären per se eben genauso abhängig. Eben deshalb wird es den Toolherstellern oder sonstigen Zulieferern anheimgestellt, besagte Headerdateien so zu schreiben, daß das Gewünschte dabei herauskommt. > uint8_t, uint16, uint32_t usw. sind immer so breit wie der Bezeichner > aussagt. Und das kommt daher, daß sie nur ein Alias des jeweils passenden Grundtyps sind, hingeschrieben von einem Zulieferer. W.S.
Frank M. schrieb: > Genau das ist eine der wesentlichen Stärken von C. Andere haben sowas bereits treffend formuliert: "It's not a bug, it's a feature" Frank M. schrieb: > Hätte man damals den Integer als 16-Bit > festgetackert, würden C-Programme heute auf 32- oder 64-Bit Systemen > grottenlahm laufen, weil sie mit einer solchen Altlast nicht optimal > arbeiten können. Erzähle nicht solchen Unsinn. Das ist etwas, was du dir selbst aus den Fingern gesaugt hast. Hätte man damals den int auf 16 Bit festgetackert und den long int auf 32 Bit und den (später dazugekommenen) long long int auf 64 Bit, dann würden heutige C Programme auch nicht langsamer laufen. Was du vermutlich sagen willst: "Wenn man alte Quellcodes ohne jegliche Änderung für Maschinen übersetzen will, die Probleme mit 16 Bit Integers haben, dann wird's langsam." Aber das hängt nicht daran, daß man nicht Rechenoperationen, für die 16 Bit ausreichend sind, auf Maschinen mit 32 oder 64 Bit Rechenbreite nicht ausführen könnte - oh nein, sowas hängt eigentlich immer an irgendwelchen Tricks mit Überläufen, Typecasts usw., die ein besonders schlauer C-Programmierer benutzt hat. So ist (und war) das. Frank M. schrieb: > Wenn Du stdint.h ablehnst, dann sei wenigstens so konsequent und mache > auch um die stdio.h, string.h und alle weiteren einen großen Bogen. > Schon mal putchar() oder strlen() verwendet? Warum? Schreibs Dir doch > selber, so wie Du das auch mit U8, U16 usw. machst und auch noch > predigst! Um das Reizwort auch hier mal wieder zu nennen: Du hast offensichtlich nicht in die Quellen zur Lernbetty geschaut. Und was solche Bezeichner wie U8, U16 usw. betrifft: Das haben Andere benutzt. Tu also nicht so, als ob das auf meinem Mist gewachsen wäre. Jetzt hast du nun eine ellenlange Epistel mit ganz offensichtlich falschen Unterstellungen geschrieben. Warum? Bloß um zu stänkern? So, zurück zum Thema. Die Überlegung, auf 32 Bit Maschinen "Möglichst viel uint8_t verwenden" zu wollen um nicht so viel RAM zu verbrauchen ist verkehrt. Besser ist allemal, sich Gedanken um bessere Algorithmen zu machen und die Datenbreite von Integers nicht gedankenlos zu wählen. W.S.
Frank M. schrieb: > Nur so war und ist es möglich, eine C-Standard-Lib bereitzustellen, die > auf allen Architekturen - angefangen vom Z80 bis zum Supercomputer in > den Top-10 - portabel und mit der für den Host optimalen Performance > läuft. Dafür braucht man aber nicht das ganze Typsystem zu verkrüppeln, sondern es reicht, wenn man einen Typ anbietet, der der nativen Breite entspricht. Das als "Genau das ist eine der wesentlichen Stärken von C." ist schon etwas überzogen. Rust löst das wesentlich besser. > So laufen auch heute noch Programme aus den 80ern ohne Code-Änderung, > welche auf 64-Bit-Systemen ganz normal mit mehreren GB großen Dateien > umgehen können. Für "mehrere" = "zwei oder vier".
W.S. schrieb: >> uint8_t, uint16, uint32_t usw. sind immer so breit wie der Bezeichner >> aussagt. > > Und das kommt daher, daß sie nur ein Alias des jeweils passenden > Grundtyps sind, hingeschrieben von einem Zulieferer. Genauso, wie derselbe oder auch andere Zulieferer den Compiler, Linker, und alle weiteren toolchain-Tools an die Architektur anpassen, dazu die Stdlib, die Linkerscripts, und alles andere, was eine konkrete Implementierung des abstrakten Konzepts „Programmiersprache C“ erfordert. Das gilt nicht nur für C, sondern für alle Programmiersprachen. Oliver
Rust verwendet andere Namen. i32 statt int32_t, und isize statt int.
:
Bearbeitet durch User
Mombert H. schrieb: > Wie löst rust das denn besser? Rust hat integer-Typen mit fester Breite (u32, i32, u16, i16, usw), plus einen Typ für native Breite (usize / isize).
MaWin schrieb: > Mombert H. schrieb: >> Wie löst rust das denn besser? > > Rust hat integer-Typen mit fester Breite (u32, i32, u16, i16, usw), plus > einen Typ für native Breite (usize / isize). Also weniger ausdrucksstark als C oder C++.
(prx) A. K. schrieb: > Hier beziehst du dich auf Assembler vs Compiler Diskussionen, die vor > einigen Jahren recht heftig abliefen und einen Teilnehmer so in Rage > brachten, dass er aus dem Forum flog. Da mach Dir mal keine Sorgen. Derjenige ist im Forum so aktiv wie eh und je und verfolgt Diskussionen wie diese hier höchst belustigt. Weil es die Vorteile simplen Assemblers gegenüber den immer komplexeren Hochsprachen immer wieder aufs Herrlichste bestätigt ;-)
W.S. schrieb: > Wissender schrieb: >> int, long usw. sind Architekturabhängig. > > Ja eben. Genau DAS war es, was ich genannt habe. Und alle Alias, die > in irgendeiner Headerdatei erzeugt werden, wären per se eben genauso > abhängig. Ja, klar. Bei Standardheadern ist es doch ganz normal, dass diese genau für den Compiler, mit dem sie ausgeliefert werden, gemacht und daher auch davon abhängig sind. Da ist stdint.h natürlich keine Ausnahme. > Eben deshalb wird es den Toolherstellern oder sonstigen Zulieferern > anheimgestellt, besagte Headerdateien so zu schreiben, daß das Gewünschte > dabei herauskommt. Selbstverständlich ist es die Aufgabe des Herstellers der Compiler-Toolchain, alle Komponenten so zusammenzustellen, dass sie auch zusammenpassen. Auch hier gibt's keinen Grund, warum stdint.h eine Sonderrolle haben sollte. >> uint8_t, uint16, uint32_t usw. sind immer so breit wie der Bezeichner >> aussagt. > > Und das kommt daher, daß sie nur ein Alias des jeweils passenden > Grundtyps sind, hingeschrieben von einem Zulieferer. Wenn die Typen fest im Compiler eingebaut wären, dann wären sie halt im Quellcode des Compiler "hingeschrieben von einem Zulieferer". Macht also auch in der Hinsicht keinen Unterschied.
W.S. schrieb: > Wissender schrieb: >> int, long usw. sind Architekturabhängig. > > Ja eben. Genau DAS war es, was ich genannt habe. Und alle Alias, die > in irgendeiner Headerdatei erzeugt werden, wären per se eben genauso > abhängig. Eben deshalb wird es den Toolherstellern oder sonstigen > Zulieferern anheimgestellt, besagte Headerdateien so zu schreiben, daß > das Gewünschte dabei herauskommt. nun auch wenn du gerne Pascal lobst: Dort ist es ganz genauso TP war ein 16Bit Compiler integer somit 16 bit breit. Heute ist das eben 32bit.
Vielleicht ist ja PL/I genehm: FIXED BINARY (VK,NK) - i.A. zu BIN(...) abgekürzt VK = Anzahl Vorkommastellen NK = Anzahl Nachkommastellen Vorzeichen geht extra int16_t entspricht also BIN(15,0) int entspricht BIN ohne (...) Sowas ging dann natürlich auch zwanglos auf Maschinen mit 24, 36 oder 48 Bit, oder was immer die Kisten früher hatten. Man schreibt hin, was man braucht, nicht was die Kiste hat. Ein brauchbarer Subset von PL/I passte tatsächlich in ein normales 64 KB CP/M System auf 8080. War vor Turbo Pascal gar nicht mal so übel.
:
Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.