Forum: Compiler & IDEs Ineffiziente Berechnung von AVR-GCC


von Roland .. (rowland)


Lesenswert?

Hallo Compilerexperten,

ich bin im Zuge der Optimierung des Codes eines AVR-GCC-Projekts auf ein 
mir nicht erklärliches Verhalten des Compilers gestoßen, zu welchem ich 
gerne Euren Rat einholen möchte. Um das Problem einfach beschreiben zu 
können, habe ich die entsprechende Codepassage so weit wie möglich 
simplifiziert, in eine eigene Funktion ausgelagert und ein Wegoptimieren 
durch das Schreiben auf ein SF-Register verhindert:
1
__attribute__((noinline)) void Mul16(uint8_t B8, uint16_t B16)
2
{
3
  TCNT1 = (uint8_t)(B16 >> 8) * B8;
4
}

Wie ersichtlich, soll einfach das High-Byte einer 16-Bit-Variable mit 
einer 8-Bit-Variable multipliziert werden, was mit einer normalen 
8-Bit-Multiplikation möglich sein sollte. Obwohl ich mit der 
Typenumwandlung „(uint8_t)“ einer 8-Bit-Multiplikation Nachdruck 
verleihe, produziert der Compiler eine 16-Bit-Multiplikation, behandelt 
also „(uint8_t)(B16 >> 8)“ weiter als 16-Bit-Variable.
1
0000004e <_Z5Mul16hj>:
2
  4e:  27 2f         mov  r18, r23
3
  50:  30 e0         ldi  r19, 0x00  ; 0
4
  52:  48 2f         mov  r20, r24
5
  54:  42 9f         mul  r20, r18
6
  56:  c0 01         movw  r24, r0
7
  58:  43 9f         mul  r20, r19
8
  5a:  90 0d         add  r25, r0
9
  5c:  11 24         eor  r1, r1
10
  5e:  9d bd         out  0x2d, r25  ; 45
11
  60:  8c bd         out  0x2c, r24  ; 44
12
  62:  08 95         ret

Ändere ich die Berechnung geringfügig, indem zum High-Byte zusätzlich 
etwas addiert wird, so behandelt der Compiler den Multiplikator als 
8-Bit-Variable und produziert eine erwartete 8-Bit-Multiplikation:
1
__attribute__((noinline)) void Mul8(uint8_t B8, uint16_t B16)
2
{
3
  TCNT1 = (uint8_t)((B16 >> 8) + 1) * B8;
4
}
1
00000064 <_Z4Mul8hj>:
2
  64:  7f 5f         subi  r23, 0xFF  ; 255
3
  66:  78 9f         mul  r23, r24
4
  68:  c0 01         movw  r24, r0
5
  6a:  11 24         eor  r1, r1
6
  6c:  9d bd         out  0x2d, r25  ; 45
7
  6e:  8c bd         out  0x2c, r24  ; 44
8
  70:  08 95         ret

Ebenso wird eine 8-Bit-Multiplikation generiert, wenn die 
16-Bit-Variable „B16“ weniger als 8 Mal nach rechts geschoben wird, 
freilich nur mit der Typenumwandlung „(uint8_t)“.

Ich möchte Euch nun gerne Frage, ob Ihr für dieses Verhalten eine 
Erklärung habt und wie eine schlanke 8-Bit-Multiplikation erreicht 
werden kann ohne selbst mit Inline-Assembler die Berechnung 
überschreiben zu müssen.

Das Projekt wurde im Übrigen für einen ATMega8 mit der 
AVR8-Toolchain-Version 3.4.5.1522 und der GCC-Version 4.8.1 erstellt.

Ich bedanke mich für das Lesen meiner Frage und schon im Voraus für Eure 
Antworten.

Beste Grüße,
Roland.

: Gesperrt durch Moderator
von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Einfach Klammern setzen
1
((uint8_t)(B16 >> 8)) * B8

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Das ändert nichts, der Cast hat sowieso schon Präzedenz gegenüber der 
Multiplikation.

von Rick (rick)


Lesenswert?

1
 __attribute__((noinline)) void Mul8(uint8_t B8, uint16_t B16)
2
 {
3
   TCNT1 = (uint8_t)((B16 >> 8) + 0) * B8;
4
 }
Was passiert hier?

von Oliver S. (oliverso)


Lesenswert?

Roland .. schrieb:
> Obwohl ich mit der
> Typenumwandlung „(uint8_t)“ einer 8-Bit-Multiplikation Nachdruck
> verleihe, produziert der Compiler eine 16-Bit-Multiplikation, behandelt
> also „(uint8_t)(B16 >> 8)“ weiter als 16-Bit-Variable.

Das steht nunmal so in den Integer-Promotion-Rules von C. Vor der 
Multiplikation werden die Operanden, deren Integertypen kleiner ist als 
ein int, auf int gecastet.

Ansonsten fällt deine Frage, warum der Compiler irgend etwas so macht, 
wie er es macht, in die große Kategorie „Ist halt so“.

Oliver

: Bearbeitet durch User
von Roland .. (rowland)


Lesenswert?

Hallo,

vielen Dank für Eure Antworten.

@Kaj G.,  Niklas G.
Das Umklammern des gecasteten Ausdrucks hatte ich bereits versucht und 
erzeugt wie von Niklas geschrieben dasselbe Ergebnis.

@Rick
Ändert man den Summanden auf null, erhält man wieder eine 
16-Bit-Multiplikation und das gleiche Ergebnis wie bei „Mul16“. Dasselbe 
Ergebnis erhält man auch bei einer Addition von 256. Eine 
8-Bit-Multiplikation wird jedoch auch bei einem Summanden von 257 
produziert, der Compiler berücksichtigt scheinbar die Typenumwandlung im 
Bezug auf die Addition.
1
__attribute__((noinline)) void Mul8(uint8_t B8, uint16_t B16)
2
{
3
  TCNT1 = (uint8_t)((B16 >> 8) + 0) * B8;
4
}
1
00000064 <_Z4Mul8hj>:
2
  64:  27 2f         mov  r18, r23
3
  66:  30 e0         ldi  r19, 0x00  ; 0
4
  68:  48 2f         mov  r20, r24
5
  6a:  42 9f         mul  r20, r18
6
  6c:  c0 01         movw  r24, r0
7
  6e:  43 9f         mul  r20, r19
8
  70:  90 0d         add  r25, r0
9
  72:  11 24         eor  r1, r1
10
  74:  9d bd         out  0x2d, r25  ; 45
11
  76:  8c bd         out  0x2c, r24  ; 44
12
  78:  08 95         ret

@Oliver S.
Ja, das stimmt schon, merkt man auch bei 8-Bit-Vergleiche, die werden 
auf 16-Bit erweitert. Allerding kann man mit der Typenumwandlung auf 
8-Bit einen 8-Bit-Vergleich erzwingen.
1
if((uint8_t)a > 1);

Es scheint ja auch im ursprünglichen Beispiel von „Mul8“ bei 
Multiplikationen zu funktionieren, allerdings eben nur, wenn der 
Multiplikation eine zusätzliche Berechnung vorausgeht.

Tja, die Kategorie „Ist halt so“ scheint es wirklich gut zu treffen.

Beste Grüße,
Roland.

von MaWin O. (mawin_original)


Lesenswert?

Roland .. schrieb:
> Allerding kann man mit der Typenumwandlung auf
> 8-Bit einen 8-Bit-Vergleich erzwingen.

Nein.
In C findet immer eine Typpromotion nach int statt.
Daraus folgt, dass Berechnungen und Vergleiche immer mit mindestens 
int-Breite stattfinden.

Dass der AVR-GCC dann trotzdem oft Code in 8-Bit ausgibt liegt daran, 
dass er beweisen kann, dass das gleiche Ergebnis rauskommt wie bei der 
Berechnung mit int.
Da das aber eine Optimierung ist, liegt es in der Natur solcher 
Optimierungen, dass sie nicht immer 100% in allen Fällen greifen.

Du bist hier einfach nur auf eine solche fehlende Optimierung getroffen.
Da bleibt eigentlich nur:
- Code umstellen, dass hoffentlich eine Optimierung greift. Das ist oft 
schwierig, wie wir hier ja nun sehen.
- Compilerupdate und hoffen, dass der neue Compiler das nun optimiert.
- Damit leben.
- Es in Inline-Assembly selbst schreiben.

von Klaus R. (klausro)


Lesenswert?

Was macht -mint8?

von MaWin O. (mawin_original)


Lesenswert?

Klaus R. schrieb:
> Was macht -mint8?

Das ändert die ABI auf int = 8 Bit breit.
Ist meistens nicht zu empfehlen, wenn du irgendwelche Fremd-Libs 
(Inklusive libc!) verwendest. Die avr-libc unterstützt das nicht.

von Klaus R. (klausro)


Lesenswert?

Das ist mir klar, evtl. hätte ich es dazu schreiben sollen, dass diese 
Option erhebliche Nebeneffekte hat. Habe nur grad keinen avr-gcc zur 
Hand zum testen.

von Roland .. (rowland)


Lesenswert?

@MaWin O.
Vielen Dank für die Erklärung. Es war mir so nicht bekannt, dass der 
Compiler nur dann auf eine 8-Bit-Berechnung umstellen kann, wenn die 
Optimierung das ergibt. Ich war da der Auffassung, dass Operationen 
immer in jener Breite durchgeführt werden, die von jener 
Eingangsvariablen mit der höchsten Breite bestimmt wird. Bei Operationen 
mit Konstanten ohne Längenangabe dann eben mit 16-Bit, wenn die 
Variablenbreite kleiner ist.
Da dem scheinbar nicht so ist, wird das Beispiel hier dann wohl 
tatsächlich einfach eine (triviale) nicht gefundene Optimierung.

@Klaus R.
Die Option „-mint8“ Löst das Problem tatsächlich, es wird in beiden 
Fällen eine 8-Bit-Multiplikation generiert. Wie MaWin O. jedoch schon 
geschrieben hat, gibt es dann diverse Warnungen und Probleme an anderen 
Stellen, wenn damit generell mit 8-Bit gerechnet wird.

Gibt es vielleicht auch eine einfache Möglichkeit (eventuell ein 
Attribut) „mint8“ nur auf einen bestimmten Codebereich oder einzelne 
Sourcedateien anzuwenden?

Beste Grüße,
Roland.

: Bearbeitet durch User
von MaWin O. (mawin_original)


Lesenswert?

Roland .. schrieb:
> Gibt es vielleicht auch eine einfache Möglichkeit (eventuell ein
> Attribut) „mint8“ nur auf einen bestimmten Codebereich oder einzelne
> Sourcedateien anzuwenden?

Natürlich kannst du einzelne Sources mit und ohne mint8 übersetzen.
Aber da ist Vorsicht geboten, weil sich das ABI ändert und die 
zusammengelinkten Module von anderen Voraussetzungen was int in 
gemeinsam genutzten Symbolen und Funktionssignaturen angeht ausgehen.
Außerdem kannst du wie gesagt in einem mit mint8 übersetzen source 
praktisch kein libc nutzen.

Ich halte das für eine exzellente Idee, wenn man es darauf anlegt in 
Zukunft extrem schwer zu debuggende Bugs zu erzeugen.

von Roland .. (rowland)


Lesenswert?

Ja, das klingt im Großen und Ganzen nicht nach einer wirklich 
praktikablen Lösung. Da ist wohl ein Ausweichen im Zweifel auf 
Inline-Assembler eher eine Option, wenn auch dergleichen nicht ganz so 
elegant anmutet.

von Oliver S. (oliverso)


Lesenswert?

Nun ja, wenn’s da wirklich auf jeden einzelnen Prozessorzyklus ankommt, 
ist Assembler tatsächlich näher dran. Nur, wann muß das tatsächlich so 
sein?

Oliver

von Rolf M. (rmagnus)


Lesenswert?

Roland .. schrieb:
> Ich war da der Auffassung, dass Operationen immer in jener Breite
> durchgeführt werden, die von jener Eingangsvariablen mit der höchsten
> Breite bestimmt wird.

Das stimmt im Prinzip auch, aber Mindestgröße ist dabei immer int. Das 
ist quasi wie im Bierzelt mit dem Mindestverzehr. Wenn also ein Operand 
kleiner ist als int, wird er erst mal auf int "angehoben" (das nennt 
sich "integer promotion"). Damit ist natürlich auch das Ergebnis einer 
Operation immer mindestens vom Typ int.

> @MaWin O.
> Vielen Dank für die Erklärung. Es war mir so nicht bekannt, dass der
> Compiler nur dann auf eine 8-Bit-Berechnung umstellen kann, wenn die
> Optimierung das ergibt.

Im Prinzip sagt der Standard, dass Berechnungen nie kleiner als mit int 
durchgeführt werden. Allerdings gibt es auch die "as-if"-Regel, die 
besagt, dass der Compiler nicht zwingend alles exakt so machen muss, wie 
angegeben. Er kann Dinge auch weglassen oder anders machen, solange er 
sicherstellen kann, dass sich das zu beobachtende Verhalten ("observable 
behavior") dadurch nicht ändert.

: Bearbeitet durch User
von Harald K. (kirnbichler)


Lesenswert?

Möglicherweise lässt sich der Optimierer des Compilers durch Verwendung 
der _fast-Typen erweichen, d.h. statt uint8_t uint_fast8_t verwenden.

Das widerspricht zwar den "promotion"-Regeln, aber ... wer weiß.

https://en.cppreference.com/w/c/types/integer

von MaWin O. (mawin_original)


Lesenswert?

Harald K. schrieb:
> Möglicherweise lässt sich der Optimierer des Compilers durch Verwendung
> der _fast-Typen erweichen, d.h. statt uint8_t uint_fast8_t verwenden.

Nein, das funktioniert mit Sicherheit nicht. Das ist nur ein Typalias, 
der zum Zeitpunkt der Optimierung schon lange aufgelöst ist.

Es ist generell überhaupt gar nicht möglich mit irgendeiner bestimmten 
Codefolge in C eine ganz bestimmte Asm-Codefolge mit Sicherheit zu 
produzieren. Das ist alles hochgradig compilerabhängig.

Das einzige worauf man sich verlassen kann ist, dass der generierte 
Asm-Code sich unter den Bedingungen des C-Maschinenmodells (nicht der 
realen Hardware!) so verhält als würde der geschriebene C-Code direkt 
ausgeführt werden ("as-if").
Das beinhaltet aber nicht die Ausführungszeit des Codes und das 
beinhaltet auch sonst keinerlei Hardwareeigenschaften (keine Interrupts, 
keine Mehrkernsysteme).
Das C-Maschinenmodell ist abstrakt und vollkommen unabhängig von der 
Zielhardware.

Damit aber im Fall der AVR-Architektur trotzdem noch halbwegs 
vernünftiger Code herauskommt, gibt es im AVR-Backend noch einen 
besonderen nachgeschalteten Optimierungsschritt, der Kenntnis über die 
Zielhardware hat. Verlassen kann man sich darauf aber auch nicht.

Wenn man eine bestimmte Asm-Codefolge braucht, dann muss man sie in Asm 
schreiben. Da bleibt keine andere Möglichkeit das sicher und stabil 
(Compilerupdate) zu erreichen.

von Harald K. (kirnbichler)


Lesenswert?

Nun, wenn ich mich recht irre, hieß es auch mal, daß der gcc eh' nie für 
8-Bit-Architekturen entworfen wurde; in Anbetracht dieser Tatsche 
liefert der schon verdammt guten AVR-Code.

Man müsste vergleichen, wie sich ein explizit für 8-Bit-Architekturen 
geschriebener Compiler schlägt, also z.B. der IAR für AVR, oder eben der 
XC8, den Microchip aber nur in kastrierter Ausführung dem Microchip 
Studio beilegt. Oder der sdcc, aber der hat AVRs nie so recht 
unterstützt.

von Oliver S. (oliverso)


Lesenswert?

Harald K. schrieb:
> Man müsste vergleichen

Wenn das muß, dann müsste man das. Wenn nicht, dann nicht.

Oliver

von Harald K. (kirnbichler)


Lesenswert?

Oliver S. schrieb:
> Wenn das muß, dann müsste man das.

Sonntag ist erst morgen. Und sonst so, alles frisch?

von Rolf M. (rmagnus)


Lesenswert?

Harald K. schrieb:
> Nun, wenn ich mich recht irre, hieß es auch mal, daß der gcc eh' nie für
> 8-Bit-Architekturen entworfen wurde;

Richtig.

> Man müsste vergleichen, wie sich ein explizit für 8-Bit-Architekturen
> geschriebener Compiler schlägt, also z.B. der IAR für AVR, oder eben der
> XC8, den Microchip aber nur in kastrierter Ausführung dem Microchip
> Studio beilegt. Oder der sdcc, aber der hat AVRs nie so recht
> unterstützt.

Es gäbe auch noch clang. Dessen daraus generierter Code sieht so aus:
1
Mul16:                                  ; @Mul16
2
; %bb.0:
3
        mul     r23, r24
4
        mov     r24, r1
5
        clr     r1
6
        mov     r18, r0
7
        clr     r19
8
        mov     r25, r24
9
        clr     r24
10
        or      r24, r18
11
        or      r25, r19
12
        sts     133, r25
13
        sts     132, r24
14
        ret

von MaWin O. (mawin_original)


Lesenswert?

Rolf M. schrieb:
> Es gäbe auch noch clang.

clang ist aber kein explizit für 8-Bit-Architekturen entworfener 
Compiler.
Und das sieht man an diesem generierten Code ja auch ganz gut ;)

von Veit D. (devil-elec)


Lesenswert?

Hallo,

warum möchtest du eine 8Bit Rechnung erzwingen? TCNT1 ist 16Bit breit. 
Wenn das Ergebnis größer 255 sein sollte und er würde tatsächlich in 
8Bit rechnen wäre das Ergebnis verfälscht. Warum der Versuch nach einem 
Zwang in 8Bit? Außerdem rechnet er schon deswegen 16Bit weil B16 16Bit 
breit ist.

von C-hater (c-hater)


Lesenswert?

Veit D. schrieb:

> warum möchtest du eine 8Bit Rechnung erzwingen? TCNT1 ist 16Bit breit.

Wie er ausdrücklich geschrieben hat, diente die Zuweisung auf ein SFR 
einzig dazu, dafür zu sorgen, dass der Code nicht komplett wegoptimiert 
wird.

Capisce?

von C-hater (c-hater)


Lesenswert?

Veit D. schrieb:

> warum möchtest du eine 8Bit Rechnung erzwingen?

Ergänzend: außerdem will er ja tatsächlich ein 16Bit-Resultat. Das ist, 
was sich nach den Regeln der Mathematik (nicht denen der geistig extrem 
behinderten C-Comiler) aus der Multiplikation zweier 8Bit-Werte ergibt.

von Harald K. (kirnbichler)


Lesenswert?

Rolf M. schrieb:
> Es gäbe auch noch clang.

Wusste nicht, daß es davon einen AVR-Port gibt, aber man lernt ja nie 
aus.

von Wilhelm M. (wimalopaan)


Lesenswert?

Harald K. schrieb:
> Rolf M. schrieb:
>> Es gäbe auch noch clang.
>
> Wusste nicht, daß es davon einen AVR-Port gibt, aber man lernt ja nie
> aus.

Der ist aber sehr experimental.

von Wilhelm M. (wimalopaan)


Lesenswert?

Der avr-gcc ist leider ab >4.6.4 in manchen Belangen schlechter 
geworden. DAs haben wir hier schon öfter diskutiert. Der 13.0.1 ist 
wieder etwas besser, aber leider nicht in diesem Fall.

Für 4.6.4 ergibt sich die gewünschte Optimierung zu einer reiner 8x8 
Multiplikation.

von Rolf M. (rmagnus)


Lesenswert?

Harald K. schrieb:
> Rolf M. schrieb:
>> Es gäbe auch noch clang.
>
> Wusste nicht, daß es davon einen AVR-Port gibt, aber man lernt ja nie
> aus.

Es gibt keinen "AVR-Port" in dem Sinne. Das ist nicht wie bei gcc, wo du 
für jede Zielarchitektur einen eigenen brauchst. Du benötigst nur einen 
aktuellen clang, dann kann der automatisch alle Zielarchitekturen, 
inklusive AVR. Allerdings braucht man halt noch die avr-libc dazu.

von Roland .. (rowland)


Lesenswert?

Hallo,

vielen Dank für die zahlreichen weiteren Antworten, Erklärungen und 
Informationen.

@Oliver S.
Ich bin ja eigentlich der Meinung, dass man für ein Projekt irgendwie 
den falschen Mikrocontroller gewählt hat, wenn man mit Assembler 
versuchen muss, einzelne Zyklen zu optimieren. Es ist bei mir jedoch 
schon vorgekommen, dass sich im Laufe des Projekts die (selbst 
gesteckten) Anforderungen ändern und irgendwann stellt man fest, dass 
die Rechenleistung nicht mehr so recht ausreicht. Weil man aber schon 
viel Zeit investiert hat, möchte man auch nicht unbedingt alles wieder 
ändern, weil die Rechenleistung ja „nur ganz knapp“ nicht reicht. Das 
ist dann so ein Punkt, wo man sich gerne einen effizienten Code wünscht 
und am Ende Teile in Assembler integrieren muss.
Bei diesem Projekt geht es um die Berechnung der Mischfarbe von Pixeln. 
Im Prinzip ist es nicht so kritisch wie lange die Berechnung dauert, da 
die Framerate sehr niedrig ist. Da jedoch in Summe sehr viele Pixel zu 
berechnen sind, habe ich mir den entsprechenden erzeugten Maschinencode 
angesehen und eben festgestellt, dass er nicht ganz so effizient ist.

@Rolf M., MaWin O.
Danke für die weitere Erklärung der Compilertechniken. Legt der 
C-Standard dann auch die tatsächliche Breite der Größe „int“ fest? 
Soweit ich das einmal gelernt habe, kann die Breite von „int“ anhängig 
vom System variieren. Auf PC-Ebene – zumindest in Visual Studio – ist 
ein „int“ 32-Bit breit. Eine Bitbreite von 16-Bit für den Typ „int“ 
scheint auch weit verbreitet zu sein. Wenn der C-Standard nun 
vorschreibt, dass die Mindestgröße vom Typ „int“ sein muss, wird ja 
eigentlich nicht die konkrete Bitbreite vorgeschrieben. Oder legt der 
C-Standard auch fest, dass „int“ auch mindestens 16-Bit breit sein muss?
---

Interessantes Ergebnis des clang-Compilers. Immerhin merkt der Compiler 
dass nur eine 8-Bit-Multiplikation nötig ist, verspielt es jedoch wieder 
mit den Null-Veroderungen.

@Harald K.
Danke für diese Idee, wie MaWin O. schon prophezeit hat, erzeugt auch 
der Fast-Typ denselben 16-Bit-Multiplikationscode.

@Veit D., c-hater
Wie c-hater schon geschrieben hat, dient die Zuweisung an „TCNT1“ 
lediglich dem Verhinder des Optimierens. Die eigentliche Berechnung 
(Mischfarbenberechnung) ist umfangreicher, weshalb ich das Problem auf 
das Nötigste verkürzt habe.

@Wilhelm M.
Das ist eine interessante Erkenntnis, dass die ältere Compilerversion 
diese Optimierung findet. Sind dir auch Fälle bekannt, wo der ältere 
Compiler ineffizienteres Ergebnis liefert als eine neuere Version?

Beste Grüße,
Roland.

von Oliver S. (oliverso)


Lesenswert?

Roland .. schrieb:
> Legt der
> C-Standard dann auch die tatsächliche Breite der Größe „int“ fest?

Nein. Das ist implementation defined, und beim AVR halt 16 Bit.

Oliver

von MaWin O. (mawin_original)


Lesenswert?

Roland .. schrieb:
> Legt der
> C-Standard dann auch die tatsächliche Breite der Größe „int“ fest?

Nicht vollständig. Es sind relative Beziehungen zwischen den Typen 
festgelegt.

> Soweit ich das einmal gelernt habe, kann die Breite von „int“ anhängig
> vom System variieren.

Richtig.

> Auf PC-Ebene – zumindest in Visual Studio – ist
> ein „int“ 32-Bit breit.

Das hat erst einmal nichts mit "PC" zu tun.
Das wird in der ABI definiert. (Application Binary Interface).
Die ist grob gesagt systemabhängig, aber ein System kann gleichzeitig 
mehrere ABIs unterstützen.
Linux kann z.B. gleichzeitig die x64, i386 und x32 ABIs auf der PC 
Plattform unterstützen.

> Oder legt der
> C-Standard auch fest, dass „int“ auch mindestens 16-Bit breit sein muss?

Glaube schon. Bin mir aber nicht mehr ganz sicher.
Ist aber bestimmt nachzulesen in diesem Internet. :)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Roland .. schrieb:
> produziert der Compiler eine 16-Bit-Multiplikation

Welcher denn eigentlich?

Da hat sich zwischen den einzelnen Versionen viel geändert.

von Rolf M. (rmagnus)


Lesenswert?

Roland .. schrieb:
> @Rolf M., MaWin O.
> Danke für die weitere Erklärung der Compilertechniken. Legt der
> C-Standard dann auch die tatsächliche Breite der Größe „int“ fest?

Er legt fest, dass int mindestens den Bereich -32767 bis +32767 
unterstützen muss, was sich mit weniger als 16 Bit natürlich nicht 
erreichen lässt. Außerdem muss seine Größe ein ganzzahliges Vielfaches 
der Größe von char sein. Und dann gilt noch 
sizeof(char)<=sizeof(short)<=sizeof(int)<=sizeof(long)<=sizeof(long 
long).
Ein Compiler dürfte also laut C-Standard theoretisch also auch so 
aufgebaut sein, dass char, short, int, long und long long alle die 
gleiche Größe von z.B. 71 Bit haben. Allerdings wäre das eine eher 
ungewöhnliche Wahl 😀.

> Soweit ich das einmal gelernt habe, kann die Breite von „int“ anhängig
> vom System variieren.

Das ist richtig. Im Prinzip war es so gedacht, dass int nach Möglichkeit 
die native Größe der jeweiligen CPU haben sollte (was aber vom Standard 
nicht vorgeschrieben ist). Daher ist bei 16-Bit-Architekturen int 16 Bit 
breit, bei 32-Bit-Architekturen entsprechend 32 Bit. Für 8-Bitter geht 
es aber nicht, auf Grund der Mindestanforderung von int. Bei 64-Bittern 
ist int in der Regel (bis auf ein paar Ausnahmen) immer noch 32 und 
nicht 64 Bit, einfach weil einem sonst die Datentypen nach unten hin 
ausgehen. Denn in C dürfen ja wie oben geschrieben nur char und short 
kleiner sein als int, aber man möchte ja auch Datentypen für 8, 16, und 
32 Bit unterstützen, bräuchte also drei Typen, die kleiner sind.

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Hallo,

int wird im ANSI C mit Mindestgröße von 2 Byte festgelegt, was 16 Bit 
signed sind. Minimum! Auf einem 8 Bit AVR sind int 16 Bit signed. Auf 
deinem PC werden es sicherlich 32bit signed sein. Im Zweifel mit 
sizeof() Datentypgröße anzeigen lassen. Möchtest du konkrete 
Datentypengrößen verwenden, dann nimm int16_t oder int32_t usw., dann 
sagt dessen Name was Programm ist.

von MaWin O. (mawin_original)


Lesenswert?

Veit D. schrieb:
> Möchtest du konkrete
> Datentypengrößen verwenden, dann nimm int16_t oder int32_t usw., dann
> sagt dessen Name was Programm ist.

Wobei das nicht von der Integer-Promotion entbindet, wie hier im Thread 
schon erklärt.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

korrekt. War mehr gedacht das er sich überlegen kann ob er int, long 
oder int16_t, int32_t schreibt und weniger rätseln muss. Nur für den 
Fall der Fälle.

von Rolf M. (rmagnus)


Lesenswert?

Allerdings verwendet er diese ja bereits:

Roland .. schrieb:
> __attribute__((noinline)) void Mul16(uint8_t B8, uint16_t B16)

von Roland .. (rowland)


Lesenswert?

@Oliver S., MaWin O., Rolf M., Veit D.
Danke für die Erläuterung. Das erklärt dann natürlich, warum sich für 
8-Bit-Systeme die Breite von „int“ nicht an die Systembreite anpassen 
und nur die Optimierung hier zu schlankerem Maschinencode führen kann.

@Jörg W.
Wie im Eingangsbeitrag am Ende angeführt habe ich das Projekt für einen 
ATMega8 mit der AVR8-Toolchain-Version 3.4.5.1522 und der GCC-Version 
4.8.1 erstellt. Beides mitgeliefert mit der IDE „Atmel Studio 6“ in 
Version 6.2.1502 SP2.
Das ist wohl eine nicht mehr ganz taufrische IDE dessen Editor auch 
teilweise mühsam ist, aber es läuft nun einmal soweit, man kennt das ja 
vielleicht.

Was ich bisher vergessen habe zu erwähnen – wenn es denn relevant ist – 
das Beispiel wurde als C++-Projekt angelegt und als Release mit der 
Optimierungsstufe „Os“ übersetzt.

@Veit D.
Ich verwende bei Code für Mikrocontroller eigentlich ausschließlich 
Integertypen mit festgelegter Breiter in der benötigen Größe in der 
Hoffnung, so die beschränkte Rechenleistung Effizient nutzen zu können. 
Dennoch Danke für den Ratschlag.

Beste Grüße,
Roland.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Roland .. schrieb:
> GCC-Version 4.8.1

Die ist hornalt :-), aber interessanterweise zeigen auch aktuellere 
Versionen (bei denen das Backend stark geändert wurde) ähnliches 
Verhalten.

MaWin O. schrieb:
> Wobei das nicht von der Integer-Promotion entbindet

Da gilt aber immer noch die "as if"-Regel. Da die zweite Multiplikation 
mit 0 multipliziert, könnte der Compiler das durchaus wegoptimieren, 
ohne die Promotion-Regeln zu verletzen.

von Wilhelm M. (wimalopaan)


Lesenswert?

Jörg W. schrieb:
> Roland .. schrieb:
>> GCC-Version 4.8.1
>
> Die ist hornalt :-), aber interessanterweise zeigen auch aktuellere
> Versionen (bei denen das Backend stark geändert wurde) ähnliches
> Verhalten.
>

Wie oben schon geschrieben, ist diese missed-optimization seit 4.6.4 
leider da.

Zu ähnlichen Fällen gibt es PR im bugzilla. Einige sind in 13.0.1 
behoben, teilweise unvollständig.

Am besten ein PR eintragen, sofern noch keins existiert.

Johann kann bestimmt mehr dazu sagen.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Roland .. schrieb:
> Was ich bisher vergessen habe zu erwähnen – wenn es denn relevant ist –
> das Beispiel wurde als C++-Projekt angelegt und als Release mit der
> Optimierungsstufe „Os“ übersetzt.

Prima.
Wenn es C++ ist, dann kannst Du es so schreiben:
1
#include <avr/io.h>
2
#include <stdint.h>
3
4
uint16_t b;
5
uint8_t a;
6
7
volatile uint8_t r;
8
9
template<typename A, typename B>
10
A Mul(const A a, const B b) {
11
    constexpr uint8_t shift = (sizeof(B) - sizeof(A)) * 8;
12
    return static_cast<A>(b >> shift) * a;
13
}
14
15
int main() {
16
    r = Mul(a, b);
17
    return r;
18
}

oder
1
    r = Mul<uint8_t>(a, b);

und bekommst das (fast) optimale Resultat:
1
        lds r25,b+1
2
        lds r24,a
3
        mul r24,r25
4
        mov r24,r0
5
        clr r1
6
        sts r,r24
7
        lds r24,r
8
        ldi r25,0

Dies ist etwas, was ich manchmal beobachte: wenn man generischen Code 
schreibt, produziert der gcc besseren Code.

von Oliver S. (oliverso)


Lesenswert?

Wilhelm M. schrieb:
> Wenn es C++ ist

Das war doch eigentlich schon seit:
> Z5Mul16hj

klar.

Oliver

von Wilhelm M. (wimalopaan)


Lesenswert?

Oliver S. schrieb:
> Wilhelm M. schrieb:
>> Wenn es C++ ist
>
> Das war doch eigentlich schon seit:
>> Z5Mul16hj
>
> klar.

Das stimmt. Hatte ich vergessen ...

von Wilhelm M. (wimalopaan)


Lesenswert?

Es ist natürlich totaler Blödsinn, was ich oben geschrieben haben: ich 
bitte um Entschuldigung.

Richtig (das generische Äquivalent zur Funktion des TO) bzgl. des Typs 
der Funktion muss es heißen:
1
template<typename A, typename B>
2
B Mul(const A a, const B b) {
3
    const uint8_t shift = (sizeof(B) - sizeof(A)) * 8;
4
    return static_cast<A>(b >> shift) * a ;
5
}

Und hier produzieren leider alle gcc > 4.6.4 denselben Murks.
Ich werde auch dazu ein PR erstellen, falls der TO das nicht schon 
gemacht hat.

Sorry for the noise!

von Wilhelm M. (wimalopaan)


Lesenswert?


von Oliver S. (oliverso)


Lesenswert?

Wird es wohl, aber der stört halt kaum jemanden.

> Reported:  2012-10-04 18:27 UTC by Georg-Johann Lay
> Status:  UNCONFIRMED
> Assignee:  Not yet assigned to anyone

usw.

Die Hoffnung stirbt zwar zuletzt, aber in dem Fall ist die wohl doch 
schon lange tot.

Oliver

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> Es scheint dieser Bug zu sein:
>
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54816

Nicht ganz.

Im Beispiel von Johann wird der Shift als MULS implementiert, was einen
Zyklus länger dauert als die Variante mit drei LSL und ein zusätzliches
Register benötigt. Die Codegröße ist in beiden Fällen gleich. Beide
Varianten enthalten einen überflüssigen Befehl (CLR bzw. MOVW) und sind
deswegen sowieso nicht perfekt. Die Differenz von einem Taktzyklus
reicht offensichtlich nicht aus, das Problem als dringlich einzustufen.

Im Beispiel hier im Thread wird eine 16x16->16-Multiplikation nicht als
8x8->16-, sondern nur als 16x8->16-Multiplikation optimiert. Der Shift
(hier um 8 Bits) wird – anders als bei Johann – gar nicht explizit
ausgeführt. Deswegen haben die beiden Probleme nur wenig miteinander zu
tun.

Das Problem hier im Thread würde ich als schwerwiegender ansehen, da
hier die unvollständige Optimierung nicht nur mehr Zyklen (4), sondern
auch mehr Flash-Bytes (8) kostet,

Von daher gesehen hätte ein neuer Report zu diesem Problem  vielleicht
eine höhere Chance auf Berücksichtigung.

@Wilhelm:

Falls du das Problem noch einmal einstellen möchtest, solltest du das
Template weglassen. Codebeispiele in PRs sollten sich auf das
Wesentliche konzentrieren, und da das Template nicht die Ursache des
Problems ist, verschleiert es das eigentlich Problem nur unnötig.

von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:
> Falls du das Problem noch einmal einstellen möchtest, solltest du das
> Template weglassen. Codebeispiele in PRs sollten sich auf das
> Wesentliche konzentrieren, und da das Template nicht die Ursache des
> Problems ist, verschleiert es das eigentlich Problem nur unnötig.

Das stimmt. Werde das korrigieren bzw. ein neuen PR aufmachen.

von Roland .. (rowland)


Lesenswert?

@Jörg W.
Stimmt, ich bin richtig erstaunt, welche hohe Versionsnummer der neueste 
Compiler hat. Ich sollte wohl die gesamte Programmiersoftware erneuern 
und gleich auf einen Open Source Editor umsteigen. Das Konfigurieren 
schreckt mich da jedoch ein wenig ab.

@Wilhelm M.
Vielen Dank für Dein Engagement in dieser Sache und dem Erstellen eines 
Fehlerreports.
Mich wundert es ja ein wenig, dass die fehlende Optimierung über sehr 
viele Versionen hinweg noch keinem Aufgefallen ist oder zumindest 
gestört hat, da eine derartige Berechnung meines Erachtens nicht so 
Exotisch erscheint.
---

Gibt es eigentlich in der von der AVR8-Toolchain verwendeten 
Bibliotheken eine Funktion, mit der sich das High-Byte extrahieren 
lässt, oder wäre das dann ohnedies nur ein einfaches Shift-Makro?

Beste Grüße,
Roland.

von Wilhelm M. (wimalopaan)


Lesenswert?

Roland .. schrieb:
> @Wilhelm M.
> Vielen Dank für Dein Engagement in dieser Sache und dem Erstellen eines
> Fehlerreports.
> Mich wundert es ja ein wenig, dass die fehlende Optimierung über sehr
> viele Versionen hinweg noch keinem Aufgefallen ist oder zumindest
> gestört hat, da eine derartige Berechnung meines Erachtens nicht so
> Exotisch erscheint.

Es war schon längst aufgefallen, ich war nur zu blöd zum Suchen:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66933

Wie schon gesagt, war Johann schon dran ;-)
Jedoch fehlte der Bug in der Liste hier: 
https://www.mikrocontroller.net/articles/Avr-gcc_Bugs


(ich teste gerade den Patch dafür)

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Roland .. schrieb:
> Gibt es eigentlich in der von der AVR8-Toolchain verwendeten
> Bibliotheken eine Funktion, mit der sich das High-Byte extrahieren
> lässt, oder wäre das dann ohnedies nur ein einfaches Shift-Makro?

Aus C-Sicht wäre das schon nur Shift oder Division durch 256.

von Wilhelm M. (wimalopaan)


Lesenswert?

Roland .. schrieb:
> Gibt es eigentlich in der von der AVR8-Toolchain verwendeten
> Bibliotheken eine Funktion, mit der sich das High-Byte extrahieren
> lässt, oder wäre das dann ohnedies nur ein einfaches Shift-Makro?

Hast Du doch verwendet: shift

von Wilhelm M. (wimalopaan)


Lesenswert?

Wilhelm M. schrieb:
> Es war schon längst aufgefallen, ich war nur zu blöd zum Suchen:
>
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66933

Da war ich zu vorschnell, deswegen neuer PR:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109476

Leider bin ich zu blöd, um selbst einen Patch zu schreiben.
Vielleicht hat Johann ja Erbarmen ;-)

von Yalu X. (yalu) (Moderator)


Lesenswert?

Roland .. schrieb:
> Mich wundert es ja ein wenig, dass die fehlende Optimierung über sehr
> viele Versionen hinweg noch keinem Aufgefallen ist oder zumindest
> gestört hat, da eine derartige Berechnung meines Erachtens nicht so
> Exotisch erscheint.

Es gibt recht viele Fälle, wo der GCC Code mit 16-Bit-Operationen
erzeugt, wo auch 8-Bit-Operationen genügten. Das hängt u.a. damit
zusammen, dass der GCC ursprünglich für die auf unixoiden System
eingesetzten Prozessoren, also nicht für 8-Bit-Prozessoren vorgesehen
war.

Die Optimierung für 8-Bit-Prozessoren erfordert deswegen sehr viel
spezifische Detailarbeit, um alle denkbaren Fälle abzudecken. Johann hat
da schon viel Arbeit hineingesteckt mit deutlich sichtbarem Erfolg.
Trotzdem gibt es immer noch Kombinationen von Operationen, wo seine
Optimierungen nicht greifen.

Solange die Beschränkungen in der Optimierung nur wenige Taktzyklen oder
Programmbytes kosten, werden entsprechende PRs wohl als wenig dringlich
eingestuft oder gar nicht erst erstellt.

von Sheeva P. (sheevaplug)


Lesenswert?

Roland .. schrieb:
> Gibt es vielleicht auch eine einfache Möglichkeit (eventuell ein
> Attribut) „mint8“ nur auf einen bestimmten Codebereich oder einzelne
> Sourcedateien anzuwenden?

Ich wüßte nicht, wie... aber stelle mir auch die Frage, ob es wirklich 
nötig ist, solche Mikrooptimierungen zu betreiben. Damit sich so etwas 
lohnt, muß das Timing schon extrem kritisch und der betreffende Code 
vergleichsweise oft aufgerufen werden, und in solchen Fällen fährt man 
sicherlich mit Inline-Assembler besser. In allen anderen Fällen gelten 
die Leitsätze von Donald E. Knuth ("premature optimization is the root 
of all evil"), Kirk Pepperdine ("measure, don't guess"), und Kent Beck 
("make it work, make it right, make it fast"). Oder, anders gesagt: in 
den meisten Fällen sind solche Mikrooptimierungen kontraproduktiv, weil 
sie die Les- und Wartbarkeit Deines Code verschlechtern, und nutzlos, 
weil es in einem fertigen und vom Compiler optimierten Programm meist 
ganz andere Stellen gibt, an denen eine Optimierung wesentlich 
lohnenswerter ist.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Roland .. schrieb:
> „mint8“ nur auf einen bestimmten Codebereich

Nein, denn letztlich ändert -mint8 das ABI. Wie schon geschrieben wurde, 
bist du damit nicht nur inkompatibel zum C-Standard, sondern auch zur 
avr-libc.

von Roland .. (rowland)


Lesenswert?

@Jörg W., Wilhelm M.
Ja, das ist schon klar, dass achtmal Rechtsschieben oder eine Division 
durch 256 rechnerisch das High-Byte einer 16-Bit-Variale extrahiert. Auf 
8-Bit-Maschinenebene wäre das dann logischer weise einfach das direkte 
Auswählen des High-Bytes eines Registerpaares für weitere Berechnungen. 
Ich dachte mir nur, es gibt vielleicht eine Inline-Assembler-Funktion, 
die von der übergebenen Variable das höherwertige Register (wohl ein 
ungerades Register) auf ein niederwertiges Rückgaberegister (wohl das 
gerade Register 24) kopiert. Somit würde der Multiplikator dann schon in 
einem geraden Register stehen, der Compiler dies als 8-Bit Multiplikator 
erkennen und auf die, von den Integer-Promotion-Regeln vorgegebene, 
Erweiterung auf 16-Bit verzichten.
Also klar, das macht so natürlich keinen Sinn, da der Compiler ja 
einfach selbst zur weiteren Berechnung das obere Register des 
Registerpaares direkt wählen kann. Es war im Prinzip nur eine Überlegung 
der Optimierung etwas nachzuhelfen, da kleine Änderungen (wie eben das 
zusätzliche Addieren einer Konstanten größer null) scheinbar die 
Berechnungslogik so ändern, dass die Optimierung bei der Multiplikation 
greift.
Aber ja, würde der Funktionsaufruf tatsächlich durchgeführt werden und 
nicht als Inline ausgeführt werden, wäre die Zyklusersparnis nicht mehr 
gegeben. Auch würden wohl unnötig viele Kopieroperationen hinzukommen 
wodurch die Effizienzsteigerung kaum vorhanden wäre.

@Yalu X.
Ja, ich kann mir schon vorstellen, dass es nicht sehr einfach ist einen 
Optimierungsfall zu erkennen. Oft wird zwar die Optimierung dieselbe 
sein, aber die Ausgangssituation eine andere was wohl die Erkennung 
erschwert.

@Sheeva P.
Das stimmt wohl. Die Lesbarkeit leidet unter so mancher Konstruktion in 
der Hoffnung auf Optimierung schon sehr deutlich. Auch habe ich schon 
festgestellt, dass die Kompaktheit des erzeugten Codes nicht wirklich 
mit einer ausgeklügelten Optimierung der C-Formulierung steigt. 
Beispielsweise diverse Schiebeoperation um möglichst effizient 
Dividieren zu können werden vom Compiler meist auch selbst gefunden.
---
Im Zuge des Verfassens dieser Antwort habe ich mich dran Erinnert, dass 
gelegentlich das Konstrukt der Union für das Auslesen einzelner Bytes 
eines größeren Datentyps zweckentfremdet wird und mir die Frage 
gestellt, ob das in diesem Fall den Compiler dazu bringen würde, die 
Multiplikation mit 8-Bit durchzuführen:
1
__attribute__((noinline)) void Mul16(uint8_t I8, uint16_t I16)
2
{
3
  union
4
  {
5
    uint16_t I16;
6
    uint8_t A8[2];
7
  } U;
8
  
9
  U.I16 = I16;
10
  
11
  TCNT1 = U.A8[1] * I8;
12
}

Zu meiner Überraschung generiert der Compiler die erwartete Effiziente 
8-Bit-Multiplikation:
1
0000004e <_Z5Mul16hj>:
2
4e:  78 9f         mul  r23, r24
3
50:  c0 01         movw  r24, r0
4
52:  11 24         eor  r1, r1
5
54:  9d bd         out  0x2d, r25  ; 45
6
56:  8c bd         out  0x2c, r24  ; 44
7
58:  08 95         ret

Soweit ich das in Erinnerung habe, ist das Zweckentfremden einer Union 
für das Manipulieren von Bytes von Variablen keine sonderlich gute Idee, 
da wohl die Zuordnung der Speicherbereiche nicht garantiert werden kann. 
In diesem Beispiel scheinen sich in der Union „U“ „I16“ und „A8[2]“ 
jedoch ein Registerpaar (R22/R23) zu teilen und mit „U.A8[1]“ der 
Zugriff auf das High-Byte von „U.I16“ respektive „I16“ der 
Eingangsvariable möglich.  Durch die direkte Übergabe einer nun 
8-Bit-Variable scheint die Optimierung des Compilers eine 
8-Bit-Multipliktion für ausreichend zu erachten.

Beste Grüße,
Roland.

von Wilhelm M. (wimalopaan)


Lesenswert?

Roland .. schrieb:
> Im Zuge des Verfassens dieser Antwort habe ich mich dran Erinnert, dass
> gelegentlich das Konstrukt der Union für das Auslesen einzelner Bytes
> eines größeren Datentyps zweckentfremdet wird und mir die Frage
> gestellt, ob das in diesem Fall den Compiler dazu bringen würde, die
> Multiplikation mit 8-Bit durchzuführen:

Das ist in C++ leider UB (in C wäre es legal). Du liest vom 
nicht-aktiven Element der union.
Bevor nun alle in Entrüstung fallen: ja, ich weiß, dass der avr-g++ dies 
wie gewünscht übersetzt ;-)

von Wilhelm M. (wimalopaan)


Lesenswert?

Roland .. schrieb:
> @Jörg W., Wilhelm M.
> Ja, das ist schon klar, dass achtmal Rechtsschieben oder eine Division
> durch 256 rechnerisch das High-Byte einer 16-Bit-Variale extrahiert. Auf
> 8-Bit-Maschinenebene wäre das dann logischer weise einfach das direkte
> Auswählen des High-Bytes eines Registerpaares für weitere Berechnungen.

Das erkennt der Optimizer auch so, keine Sorge.

von Wilhelm M. (wimalopaan)


Lesenswert?

Roland .. schrieb:
> Im Zuge des Verfassens dieser Antwort habe ich mich dran Erinnert, dass
> gelegentlich das Konstrukt der Union für das Auslesen einzelner Bytes
> eines größeren Datentyps zweckentfremdet wird und mir die Frage
> gestellt, ob das in diesem Fall den Compiler dazu bringen würde, die
> Multiplikation mit 8-Bit durchzuführen

Man kann aber Deine Idee aufgreifen und folgendes schreiben:
1
constexpr uint16_t mul(const uint8_t a, const uint16_t b) {
2
    const auto aa = std::bit_cast<std::array<uint8_t, 2>>(b);
3
    return aa[1] * a;
4
}

(Man könnte sagen: std::bit_cast ist der Ersatz für illegales 
type-punning via union in C++. std::memcpy geht auch, aber nur zur 
Laufzeit)

Das produziert ebenfalls:
1
mul(unsigned char, unsigned int):
2
mul r23,r24      ;  tmp59, tmp58
3
movw r24,r0      ;
4
clr __zero_reg__
5
ret

Wer kein std::bit_cast hat, kann
1
uint16_t mul(const uint8_t a, const uint16_t b) {
2
    uint8_t aa[2];
3
    std::memcpy(aa, &b, 2);
4
    return aa[1] * a;
5
}

Der Unterschied hier ist, dass std::bit_cast auch constexpr ist.

Aber bitte: das ist von hinten durch die Brust ins Auge und sollte 
niemals ernsthaft irgendwo verwendet werden.

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

Roland .. schrieb:
> Zu meiner Überraschung generiert der Compiler die erwartete Effiziente
> 8-Bit-Multiplikation

Oliver S. schrieb:
> Ansonsten fällt deine Frage, warum der Compiler irgend etwas so macht,
> wie er es macht, in die große Kategorie „Ist halt so“.

Oliver

von Wilhelm M. (wimalopaan)


Lesenswert?

Ich habe gerade den avr-gcc 13.0.1 mit einem Patch versorgt, und nun 
produziert er nun wieder das optimale Ergebnis, wie auch schon vor 
langer Zeit avr-gcc 4.6.4.

Der Patch ist hier zu finden:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109476

von Roland .. (rowland)


Lesenswert?

@Wilhelm M.
Vielen Dank für Deinen weiteren Einsatz sowie der Beispiele für einen 
C++-Konformen Zugriff auf einzelne Bytes eines Datentyps . Wie ich 
gesehen habe, hast Du in einem Beitrag „std:bit_cast“ hier vorgestellt. 
In der auf meinem System installierten Compilerversion ist dies zwar 
nicht verfügbar, aber der Alternative Umweg über „memcpy“ funktioniert.
In Anlehnung an diese Methode habe ich versucht, „memcpy“ in einfacher 
Weise direkt mit wildem Pointer-Casting nachzuahmen:
1
__attribute__((noinline)) void Mul16(uint8_t I8, uint16_t I16)
2
{
3
  TCNT1 = ((uint8_t*)&I16)[1] * I8;
4
}

Der Compiler erzeugt zwar damit auch eine einfache 8-Bit-Multiplikation, 
allerdings ist der zusätzliche Overhead besonders umfangreich. Im 
Gegensatz zu „memcpy“ wird das Array nicht in Registern gehalten, 
sondern in das RAM ausgelagert, was natürlich enorm viel Effizienz 
kostet:
1
0000004e <_Z5Mul16hj>:
2
4e:  cf 93         push  r28
3
50:  df 93         push  r29
4
52:  00 d0         rcall  .+0        ; 0x54 <_Z5Mul16hj+0x6>
5
54:  cd b7         in  r28, 0x3d  ; 61
6
56:  de b7         in  r29, 0x3e  ; 62
7
58:  7a 83         std  Y+2, r23  ; 0x02
8
5a:  69 83         std  Y+1, r22  ; 0x01
9
5c:  9a 81         ldd  r25, Y+2  ; 0x02
10
5e:  98 9f         mul  r25, r24
11
60:  c0 01         movw  r24, r0
12
62:  11 24         eor  r1, r1
13
64:  9d bd         out  0x2d, r25  ; 45
14
66:  8c bd         out  0x2c, r24  ; 44
15
68:  0f 90         pop  r0
16
6a:  0f 90         pop  r0
17
6c:  df 91         pop  r29
18
6e:  cf 91         pop  r28
19
70:  08 95         ret

Erstaunlich finde ich im Übrigen die beiden Befehle „pop r0“, wo doch 
„push r0“ zuvor nie ausgeführt wird, sowieso den mir nicht schlüssigen 
„rcall“-Befehl.

Interessant, was sich im Beitrag Deines Bug-Reports tut, danke für das 
Erstellen desselbigen. Hier scheint es doch ein Interesse zu geben und 
womöglich wird der Patch in der nächsten Version integriert.

@Oliver S.
Exakt! :-)

Beste Grüße,
Roland.

von Oliver S. (oliverso)


Lesenswert?

Roland .. schrieb:
> Erstaunlich finde ich im Übrigen die beiden Befehle „pop r0“, wo doch
> „push r0“ zuvor nie ausgeführt wird, sowieso den mir nicht schlüssigen
> „rcall“-Befehl.

rcall .0 schafft einen 2-Byte  großen Stackframe, der dann im weiteren 
benutzt wird. Die pops am Ende räumen den wieder ab.

gcc-Assembler-Magie ;)

Oliver

: Bearbeitet durch User
von C-hater (c-hater)


Lesenswert?

Oliver S. schrieb:

> gcc-Assembler-Magie ;)

Der war gut.

von Roland .. (rowland)


Lesenswert?

Ah, okay, also ein Funktionsaufruf an dieselbe Programmstelle, womit die 
im Stack gespeicherte Rücksprungadresse als Datenspeicher 
zweckentfremdet werden kann. Weil die Rücksprungadresse also geändert 
wird, kann nicht mit „ret“ der Aufrufbefehl abgeschlossen werden, 
sondern es wird mit „pop r0“ beendet.

Danke für die Erklärung. Ja, das ist wahrlich gcc-Assembler-Magie, sehr 
gefinkelt ;-).

Beste Grüße,
Roland.

von Wilhelm M. (wimalopaan)


Lesenswert?

Roland .. schrieb:
> In Anlehnung an diese Methode habe ich versucht, „memcpy“ in einfacher
> Weise direkt mit wildem Pointer-Casting nachzuahmen:

In diesem Fall hast Du Glück und es kein UB: Du machst einen 
pointer-cast und dereferenzierst. Dies ist normalerweise UB, sofern der 
Zieltyp nicht (unsigned) char ist. Dies ist bei Dir der Fall (uint8_t) 
und deswegen kein UB.

Heute macht man das aber per std::bit_cast. Dann ist es sogar constexpr 
und auf jedenfall expressiver. Die Notlösung ist std::memcpy, was ja gar 
kein Copy macht wie Du siehst, weil hier std::memcpy ein Spezialfall 
ist, der von allen Compilern entsprechend behandelt wird.

von Peter D. (peda)


Lesenswert?

Man sollte sich bei solchen Aktionen aber erstmal fragen, ob man nicht 
versehentlich Mikro-Optimierung betreibt.
D.h. bringt die Optimierung an dieser Stelle wirklich den Meilenstein in 
viel schnellerer Ausführung des Gesamtprogramms bzw. besonders 
zeitkritischer Codeabschnitte oder eine bedeutende Senkung des 
Flashverbrauchs.

Beitrag #7392627 wurde von einem Moderator gelöscht.
von Roland .. (rowland)


Lesenswert?

@Wilhelm M.
Ein Pointer-Cast von „long“ auf „int“ wäre dann ein undefiniertes 
Verhalten, obwohl ein „int“ weniger oder zumindest gleichviele Bytes 
groß ist als der Datentyp „long“? Wird dann sozusagen nur garantiert, 
dass an der gecasteten Adresse zumindest ein Byte Gültigkeit hat?

@Peter D.
Zweifelsohne ist das so. Wie schon geschrieben, spielt es in meinem Fall 
keine allzu große Rolle. Ich habe mir eben jenen Teil des kompilierten 
Programms angesehen, der in einer Schleife sehr oft aufgerufen wird 
(Pixelberechnung) um zu sehen ob ich das Optimieren kann und 
festgestellt, dass nicht nötiger Weise mit 16-Bit multipliziert wird. 
Nachdem ich einen Fehler im Programmcode ausgeschlossen habe, wollte ich 
einfach wissen wie es dazu kommt, was mir hier ja ausführlich erklärt 
wurde.
Dank des Erstellens eines Fehlerberichts von Wilhelm wurde ja sogar eine 
Optimierung des Optimierers angestoßen. Wenn dies in künftige 
Compilerversionen einfließt ist das doch eine kleine Verbesserung.

Beste Grüße,
Roland.

von Wilhelm M. (wimalopaan)


Lesenswert?

Peter D. schrieb:
> Man sollte sich bei solchen Aktionen aber erstmal fragen, ob man nicht
> versehentlich Mikro-Optimierung betreibt.

Natürlich sollte man in der oben geschilderten Art seinen Quelltext 
nicht modifizieren, das wäre ja Quatsch. Das waren auch nur Testcases, 
um ggf. dem missed-optimization-bug auf die Spur zu kommen.

> D.h. bringt die Optimierung an dieser Stelle wirklich den Meilenstein in
> viel schnellerer Ausführung des Gesamtprogramms bzw. besonders
> zeitkritischer Codeabschnitte oder eine bedeutende Senkung des
> Flashverbrauchs.

Der endgültige Patch für den avr-gcc, den ich jetzt teste, produziert in 
Code mit einigen solcher 8x8 Multiplikationen ca. 10% kleineren Code und 
natürlich auch schnelleren Code (wie stark sich das auswirkt, habe ich 
noch nicht vermessen, doch das kannst Du Dir auch selbst ausrechnen 
anhand des besseren Assembler).

Daher halte ich das nicht für eine Mikro-Optimierung. Ob und wann die 
jetzt mainline geht, weiß ich nicht. Ist mir aber auch egal, da ich 
meinen avr-gcc eh selbst erzeuge.

von Wilhelm M. (wimalopaan)


Lesenswert?

Roland .. schrieb:
> @Wilhelm M.
> Ein Pointer-Cast von „long“ auf „int“ wäre dann ein undefiniertes
> Verhalten, obwohl ein „int“ weniger oder zumindest gleichviele Bytes
> groß ist als der Datentyp „long“? Wird dann sozusagen nur garantiert,
> dass an der gecasteten Adresse zumindest ein Byte Gültigkeit hat?

Das spielt gar keine Rolle, weil es einfach formal UB ist, wenn der 
Zieltyp nicht char ist. Auch wenn es in der Praxis funktionieren wird 
(principle of least surprise).

Wie oben schon gezeigt, ist std::memcpy() bzw. std::bit_cast die einzige 
Möglichkeit, ein type-punning korrekt auszuführen. Allerdings gibt da 
prinzipiell natürlich auch die Möglichkeit von trap-representations, 
also nicht gültigen Bit-Kombinationen im Zieltyp. Kann man dies aber 
ausschließen im konkreten Fall, so liegt kein UB mehr vor.

> Dank des Erstellens eines Fehlerberichts von Wilhelm wurde ja sogar eine
> Optimierung des Optimierers angestoßen. Wenn dies in künftige
> Compilerversionen einfließt ist das doch eine kleine Verbesserung.

Aktuell testen wir noch einen Patch. Dieser zeigt aktuell das richtige 
Optimierungsverhalten und läuft auch in kompilizierten Fällen ohne ICE. 
Die Codegrößer schrumpft dabei um ca. 10% bei Code, der viel solche 
Sachen enthält. Von daher bestehen gute Chancen auf Integration in 
mainline, und damit ist der avr-gcc > 13.0.1 dann wieder so gut wie 
4.6.4. Na, wenn das mal nicht gut ist ;-)

von MaWin O. (mawin_original)


Lesenswert?

Roland .. schrieb:
> Ein Pointer-Cast von „long“ auf „int“ wäre dann ein undefiniertes
> Verhalten, obwohl ein „int“ weniger oder zumindest gleichviele Bytes
> groß ist als der Datentyp „long“? Wird dann sozusagen nur garantiert,
> dass an der gecasteten Adresse zumindest ein Byte Gültigkeit hat?

Es ist verboten auf eine Variable über einem falschen (zum Variablentyp 
unpassenden) Pointertyp zuzugreifen (Type Punning). Eine Ausnahme gibt 
es nur für char.
Das heißt, wenn man long-pointer -> int-pointer castet, dann muss man 
wieder zurück auf long-pointer casten bevor man den Zeiger 
dereferenziert.
Ansonsten erzeugt man UB.

von Peter D. (peda)


Lesenswert?

Man kann auch einen zeitkritischen Codeteil nach Assembler compilieren, 
das *.S selber optimieren und alles zusammen linken.
Das finde ich deutlich lesbarer als inline Assembler.
Aufpassen, daß man das Compilieren nach Assembler wieder aus dem Make 
rausnimmt, sonst wird das Optimierte wieder überschrieben.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Peter D. schrieb:
> Man kann auch einen zeitkritischen Codeteil nach Assembler compilieren,
> das *.S selber optimieren und alles zusammen linken.
> Das finde ich deutlich lesbarer als inline Assembler.
> Aufpassen, daß man das Compilieren nach Assembler wieder aus dem Make
> rausnimmt, sonst wird das Optimierte wieder überschrieben.

Dafür programmiere ich aber nicht in C / C++. Optimierung ist eine der 
vornehmsten Aufgaben eines Compilers. Und diese hier angesprochene 
Optimierung ist doch wohl sehr erwartbar. Sonst reden doch hier auch 
alle davon, wie toll die Compiler dies und das optimieren und das man 
Kraut- und Rübencode schreiben können, der Compiler wird's schon 
richten. Und dann bei so einem simplen Fall auf Assembler gehen: das ist 
doch wohl nicht Dein Ernst.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Peter D. schrieb:
> Aufpassen, daß man das Compilieren nach Assembler wieder aus dem Make
> rausnimmt, sonst wird das Optimierte wieder überschrieben.

Das kann eigentlich nur passieren, wenn man ein lbödes OS hat, das nicht 
zwischen *.s und *.S unterscheiden kann. GCC erzeugt von sich aus (etwa 
mit -S oder zusammen mit -save-temps) nämlich nur *.s, aber nie *.S.

Abhilfe schafft dann, Endung *.sx zu verwenden. Oder Endung wie *.asm 
nach gusto, aber dann mit -x assembler-with-cpp vor dem Modul.

von C-hater (c-hater)


Lesenswert?

Johann L. schrieb:

> Das kann eigentlich nur passieren, wenn man ein lbödes OS hat, das nicht
> zwischen *.s und *.S unterscheiden kann.

LOL

Es ist viel aufwendiger und komplizierter, eine 
"case-independend"-Benamsung korrrekt umzusetzen. Jeder, der was vom 
Programmieren versteht, kann mir da nur zustimmen.

Microsoft hat es geschafft (so ungefähr ab W2k dann endlich sogar 
wirklich fehlerfrei).

Nur die wirklich blöden OS' können's bis heute nicht. Hängen also 
entwicklungsmäßig in dieser Sache ungefähr ein Vierteljahrhundert 
hinterher. Und das Schlimme ist: es ist keine Besserung absehbar, weil 
es dann an allen Ecken und Enden heftigst knirschen würde im Gebälk...

von MaWin O. (mawin_original)


Lesenswert?

C-hater schrieb:
> Microsoft hat es geschafft (so ungefähr ab W2k dann endlich sogar
> wirklich fehlerfrei).

Nein. Nicht wirklich. Das kann man gar nicht fehlerfrei umsetzen, weil 
"Case" gar nicht global eindeutig definiert ist, außer vielleicht für 
die Buchstaben a-z. Schon gar nicht zu der Zeit, wo MS mit dem Unsinn 
angefangen hat.

Das ist ein absolut sinnloses Feature, was man gar nicht korrekt 
implementieren kann.
Man kann es höchstens für die eigene Definition von "Case" korrekt 
implementieren.

von Yalu X. (yalu) (Moderator)


Lesenswert?

C-hater schrieb:
> Microsoft hat es geschafft (so ungefähr ab W2k dann endlich sogar
> wirklich fehlerfrei).

Auch Windows 10 und vermutlich Windows 11 können sich immer noch nicht
entscheiden, ob sie Dateinamen case-sensitiv oder case-insensitiv
behandeln sollen, wie folgende Beispiele zeigen:

Ich habe gerade in ein und demselben Verzeichnis erfolgreich drei
verschiedene Dateien angelegt, deren Namen sich nur in ihrer
Groß-/Kleinschreibung unterscheiden:

- straße.txt
- STRASSE.TXT
- STRAẞE.TXT

Dann sollten doch erst recht die beiden folgenden Dateinamen, die sich
nicht nur in der Groß-/Kleinschreibung, sondern auch in der Bedeutung
ganz klar unterscheiden, parallel existieren können:

- ahnen.txt
- Ahnen.txt

Da hat sich Windows aber gewehrt.

Ich halte das für einen schweren Bug :)

von Harald K. (kirnbichler)


Lesenswert?

Yalu X. schrieb:
> Ich habe gerade in ein und demselben Verzeichnis erfolgreich drei
> verschiedene Dateien angelegt, deren Namen sich nur in ihrer
> Groß-/Kleinschreibung unterscheiden:
>
> - straße.txt
> - STRASSE.TXT
> - STRAẞE.TXT

Nee, der mittlere fällt raus. Daß MS das Versal-SZ nicht korrekt 
behandelt, das ist in der Tat ein Fehler, aber der Helvetizismus 
"Strasse" ist nicht das gleiche wie "Straße".

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Mit der ineffizienten Berechung eines AVR-GCC hat das aber jetzt nicht 
mehr viel zu tun …

von Rolf M. (rmagnus)


Lesenswert?

Harald K. schrieb:
> Nee, der mittlere fällt raus. Daß MS das Versal-SZ nicht korrekt
> behandelt, das ist in der Tat ein Fehler, aber der Helvetizismus
> "Strasse" ist nicht das gleiche wie "Straße".

STRASSE ist kein Helvetizismus, sondern die gängige Art in Deutschland, 
das Wort "Straße" zu versalieren. So steht's auch im amtlichen Regelwerk 
zur Rechtschreibung, wobei alternativ auch das ẞ erlaubt ist.
Das ẞ als "großes" ß hat sich bisher nicht wirklich durchsetzen können. 
Es passt vom Schriftbild her auch nicht sonderlich gut zu den anderen 
Großbuchstaben.

Jörg W. schrieb:
> Mit der ineffizienten Berechung eines AVR-GCC hat das aber jetzt nicht
> mehr viel zu tun …

Nun ja, ähm…

: Bearbeitet durch User
von C-hater (c-hater)


Lesenswert?

Harald K. schrieb:

> Nee, der mittlere fällt raus. Daß MS das Versal-SZ nicht korrekt
> behandelt, das ist in der Tat ein Fehler

Seit wann gibt's das in Unicode? Wenn ich mich richtig erinnere, noch 
nicht sehr lange. Sprich: BUG ja, aber vermutlich eher temporärer Natur. 
Wird irgendwann auch noch nachgepflegt werden. In manchen 
skandinavischen Sprachen gibt's übrigens auch inzwischen Nachholbedarf 
für MS. Da wurden ebenfalls einfach Unicode-Versalien ohne Sinn und 
Verstand eingeführt. Also für Zeichen, die in normaler Schreibung 
einfach niemals groß geschrieben werden (oder vielmehr wurden).

So ist das halt, wenn jemand anders die "Standards" setzt. Da kann man 
nur hinterherjapsen. Der Unterschied ist: Linsux bemüht sich ja 
nichtmal, sondern überläßt das einfach komplett den 
Anwendungsprogrammierern...

Klar also, was die bevorzugen...

von C-hater (c-hater)


Lesenswert?

MaWin O. schrieb:

> Das ist ein absolut sinnloses Feature

Nunja, mindestens eine Milliarde Windows-Benutzer weltweit sehen das 
definitiv anders. Und vorher DOS-Benutzer, allerdings viel weniger als 
eine Milliarde. Aber etliche Millionen werden es auch damals schon 
gewesen sein, denen es nicht vermittelbar war, dass "FuckYou.Txt" was 
anderes ein soll als "fuckyou.txt".

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ok, das dein Horizont mit unflätiger Sprache endet, wissen wir ja schon. 
Zur Erweiterung des Horizonts:

GuteRinder.txt
GuterInder.txt

Der_gefangene_Floh.jpg
Der_Gefangene_floh.jpg

StauBecken.jpg
StaubEcken.jpg

UrInstinkt.txt
UrinStinkt.txt

etc.

: Bearbeitet durch User
von Roland .. (rowland)


Lesenswert?

@Wilhelm M., MaWin O.
Ich verstehe, danke.
---

Also eine Reduktion der Codegröße von 10% bei Programmen mit vielen 
Berechnungen dieser Art finde ich dann schon beachtlich, danke für den 
Einsatz.

Beste Grüße,
Roland.

von C-hater (c-hater)


Lesenswert?

Johann L. schrieb:
> Ok, das dein Horizont mit unflätiger Sprache endet, wissen wir ja schon.

Und deiner endet offensichtlich bei einer weltweit derart irrelevanten 
Sprache wie deutsch. Wieviele Leute sprechen und schreiben deutsch? 
Wenn's hoch kommt, vielleicht 200 Millionen. Satte 2,5% der 
Weltbevölkerung.

Nunja, wenn man die paar Latin-Sprachen dazu nimmt, bei denen die 
Caseness eine ähnliche Rolle spielt wie im Deutschen, kommen wir schon 
auf ein paar mehr Prozent. Relevant sind da eigentlich nur französisch, 
spanisch und portugiesisch. Das bissel skandinavische Zeugs kann man 
eher in den Skat drücken. Wie auch immer: großzügig aufgerundet sind wir 
hier dann alles in allem bei vielleicht bei 10% der Weltbevölkerung.

Für die restlichen 90% ist das Windows/DOS-Konzept eine Erleicherung. 
Genau deswegen gibt's das auch. MS wollte und will die Benutzer 
glücklich machen, damit die MS kaufen. Allein der Erfolg des Konzepts 
zeigt, dass sie das richtig analysiert haben. Schon damals, in den 
frühen 1980ern. Da gab's Linux noch nicht mal. Nur die Unix-Vorgänger, 
deren Anbieter das halt offensichtlich nicht so gut analysiert hatten. 
Und die ein so beschissenes Filesystem-Konzept entwickelt haben, dass 
sich sowas auch nachträglich nicht mehr dranstricken ließ. Das haben 
dann heute noch relevante Sachen wie Linux oder Free/Open-BSD geerbt.

DAS ist der eigentliche Knackpunkt.

Und du kannst diese Tasachen nicht mit dem Verweis auf "unflätige 
Sprache" meinerseits entkräften. Viele Leute sind durchaus intelligent 
genug, um diese lächerliche Ausflucht als solche zu erkennen. Gerade im 
konkreten Fall ist das nun wirklich sehr leicht.

von Rolf M. (rmagnus)


Lesenswert?

C-hater schrieb:
> Für die restlichen 90% ist das Windows/DOS-Konzept eine Erleicherung.
> Genau deswegen gibt's das auch.

Ach Quatsch. Das gibt es, weil DOS sich das damals von CP/M so abgeguckt 
hat, und das hat es so gemacht, weil damals noch nicht jeder Computer 
überhaupt Kleinbuchstaben in seinem Zeichensatz hatte. Viele (inklusive 
DOS) haben Kleinbuchstaben in Dateinamen gar nicht unterstützt. Also im 
Prinzip der Gleiche Grund, aus dem es noch immer diese unsäglichen 
Laufwerksbuchstaben gibt: Überbleibsel aus der Computer-Steinzeit.

> MS wollte und will die Benutzer glücklich machen, damit die MS kaufen.
> Allein der Erfolg des Konzepts zeigt, dass sie das richtig analysiert
> haben.

Klar, die Benutzer haben alle Windows nur deshalb auf ihren Computern, 
weil das nicht zwischen Groß- und Kleinschreibung unterscheidet. Was 
hast du geraucht?

: Bearbeitet durch User
von C-hater (c-hater)


Lesenswert?

Rolf M. schrieb:

> Klar, die Benutzer haben alle Windows nur deshalb auf ihren Computern,
> weil das nicht zwischen Groß- und Kleinschreibung unterscheidet. Was
> hast du geraucht?

Aha, die idiotische Linux-Kloppertruppe auf Kriegspfad.

Nun denn: Was auch immer nun konkret die Windows (oder zuvor 
DOS-Benutzer) dazu gebracht hat, eben diese OS zu wählen, ist reine 
Spekulation.

Fakt ist hingegen, dass eben zu jeder Zeit seit den frühen 80ern des 
vorigen Jahrhunderts diese Wahl durch die weit überwiegende Mehrheit so 
getroffen wurde.

Zieht euch das einfach rein und denkt drüber nach, warum das so ist. 
Natürlich sind die Gründe vielfältig, darüber brauchen wir nicht 
diskutieren. Aber ganz sicher ist eins: Es gibt keine, wie auch immer 
geartete Überlegenheit des Unix-Konzepts. Ganz sicher jedenfalls und 
offensichtlich nicht aus Sicht der Nutzer...

Dazu kommt: MS hat das, was konzeptionell an Unix gut war, zumindest 
später in den NT-Kernel übernommen hat. D.h.: das ist seit Jahrzehnten 
auch Teil von Windows.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

C-hater schrieb:
> MS hat das, was konzeptionell an Unix gut war, zumindest später in den
> NT-Kernel übernommen hat.

Das mit dem UNIX hatten sie paar Jahre vorher versucht, mit wenig Erfolg 
(Xenix).

NT stammt von VMS-Entwicklern.

Dass sich Millionen von Nutzern nun ausgerechnet für case insensitive 
entschieden hätten, halte ich für eine gewagte These: die haben 
einfach genommen, was sie vorgesetzt bekommen haben. Hätte Microsoft 
ihnen ein System vorgesetzt, das in den Dateinamen Groß- und 
Kleinschreibung unterscheidet, hätten sie das genauso genommen.

Immerhin kann man in Windows inzwischen wenigstens ein (bspw. 
vertipptes) FooBAr.txt in FooBar.txt umbenennen – lange Zeit ging das 
nicht, denn schließlich gibt es die Zieldatei ja schon …

von Rolf M. (rmagnus)


Lesenswert?

C-hater schrieb:
> Aha, die idiotische Linux-Kloppertruppe auf Kriegspfad.

Beleidigungen, wie immer…

> Nun denn: Was auch immer nun konkret die Windows (oder zuvor
> DOS-Benutzer) dazu gebracht hat, eben diese OS zu wählen, ist reine
> Spekulation.

Du hast damit angefangen.

> Fakt ist hingegen, dass eben zu jeder Zeit seit den frühen 80ern des
> vorigen Jahrhunderts diese Wahl durch die weit überwiegende Mehrheit so
> getroffen wurde.

Nein, das ist kein Fakt, sondern Unsinn. Die "weit überwiegende 
Mehrheit" hatte gar keine Wahl. Die musste das nehmen, was der 
Hersteller programmiert hat.

> Dazu kommt: MS hat das, was konzeptionell an Unix gut war, zumindest
> später in den NT-Kernel übernommen hat. D.h.: das ist seit Jahrzehnten
> auch Teil von Windows.

Das meiste davon hat Microsoft allerdings nicht besonders gut umgesetzt. 
Symlinks gibt es seit NT, sind aber bis heute unter Windows völlig 
kaputt.

von Wilhelm M. (wimalopaan)


Lesenswert?

Der Patch dazu ist nun upstream, d.h. in avr-gcc / gcc 14.0.0 enthalten.

von C-hater (c-hater)


Lesenswert?

Rolf M. schrieb:

>> Fakt ist hingegen, dass eben zu jeder Zeit seit den frühen 80ern des
>> vorigen Jahrhunderts diese Wahl durch die weit überwiegende Mehrheit so
>> getroffen wurde.
>
> Nein, das ist kein Fakt, sondern Unsinn. Die "weit überwiegende
> Mehrheit" hatte gar keine Wahl. Die musste das nehmen, was der
> Hersteller programmiert hat.

Ähemm.. Nö. Ich jedenfalls wurde zu keiner Zeit daran gehindert, z.B. 
ein Linux parallel zu einem Windows oder auch als alleiniges OS auf 
einem PC zu installieren (und habe das auch in all den Jahrzehnten immer 
wieder getan). De facto nutze ich derzeit privat 2x Linux/Windows 
Dual-Boot und 1x pure Linux, dienstlich (als Arbeitsrechner) 2x 
Linux/Windows Dual-Boot. Die Server-Landschaft ist vielfältiger. 
Dual-Boot gibt's da natürlich nicht, aber sowohl Windows als auch Linux 
in jeweils etlichen Instanzen und sogar ein einsames BSD.

Deine Aussage ist also ganz offensichtlich ein freche und obendrein 
überaus plumpe Lüge. Das kann man nicht beschönigen. Es gab zu keiner 
Zeit einen objektiven Zwang, Windows zu benutzen. Die User hatten immer 
die freie Wahl (Nunja, zuindest seitdem Linux überhaupt brauchbar 
wurde). Das würde ich mal so ab ungefährt 1994 verorten (bzw: 
eigentlich: "verzeitlichen").

von Yalu X. (yalu) (Moderator)


Lesenswert?

C-hater schrieb:
> Es gab zu keiner Zeit einen objektiven Zwang, Windows zu benutzen. Die
> User hatten immer die freie Wahl (Nunja, zuindest seitdem Linux
> überhaupt brauchbar wurde). Das würde ich mal so ab ungefährt 1994
> verorten (bzw: eigentlich: "verzeitlichen").

C-hater schrieb:
> Fakt ist hingegen, dass eben zu jeder Zeit seit den frühen 80ern des
> vorigen Jahrhunderts diese Wahl durch die weit überwiegende Mehrheit so
> getroffen wurde.

Zwischen den frühen 80ern und 1994 liegt IMHO mindestens ein Jahrzehnt.
In den frühen 80ern hatte man auf PCs im Wesentlichen die Wahl zwischen
PC-DOS, MS-DOS und CP/M-86, viel mehr war da nicht.

Was die Groß-/Kleinschreibung von Dateinamen betrifft, hat Rolf also
völlig recht:

Rolf M. schrieb:
> Die "weit überwiegende Mehrheit" hatte gar keine Wahl. Die musste das
> nehmen, was der Hersteller programmiert hat.

von Harald K. (kirnbichler)


Lesenswert?

Yalu X. schrieb:
> Was die Groß-/Kleinschreibung von Dateinamen betrifft, hat Rolf also
> völlig recht:

Nun, weder CP/M noch DOS kannten überhaupt das Konzept von Dateinamen. 
Die hatten nur 8.3-Kürzel. Windows kannte vor 1993 (Windows NT) auch 
keine Dateinamen, aber Windows NT brauchte noch gute acht Jahre, bis es 
unter dem Namen "Windows XP" im Mainstream ankan. Bis dahin konnte man 
ab etwa 1995 mit dem frickeligen Windows 95 immerhin auch schon in den 
Genuss von Dateinamen kommen, die aber nur dann funktionierten, wenn man 
auch 32-Bit-Programme verwendete.

Der Rest der Diskussion ist müßig, sich daran aufzugeilen, daß das 
selbst verwendete Betriebssystem es natürlich richtig macht, während die 
anderen es falsch machen hat was von praktiziertem Autismus, wenn nicht 
schon von religiöser Intoleranz.

Wer aus der kindlich-analen Phase raus ist, schreibt seine Software so, 
daß sie nicht darauf angewiesen ist, daß das Dateisystem bestimmte 
Eigenschaften hat, sondern mit verschiedenen Gegebenheiten zurechtkommt. 
Zum Beispiel auch Leerzeichen in Dateinamen oder Pfaden; es gibt 
Softwareentwickler, die das können, und es gibt welche, die davon 
gründlich überfordert sind. Die schreien dann natürlich laut rum, daß 
das Fehler des dummen Anwenders ist, statt einzusehen, daß es nur ihre 
eigene Unfähigkeit ist.

Und so bleiben uns weiterhin die schönen Flamewars erhalten, die wir 
schon in den 80ern (Spectrum vs. C64, Atari vs. Amiga vs. Mac etc.pp.) 
erleben durften.

Könnte man bloß Flamewars als Energiequelle nutzen ...

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Harald K. schrieb:
> Die hatten nur 8.3-Kürzel.

Auch das waren trotzdem Dateinamen. RSX11/M hatte 6+3 Zeichen, noch dazu 
Radix-50 codiert, da konnte man gar keine Kleinbuchstaben überhaupt 
reinschreiben,  (bei CP/M konnte man es, am OS vorbei). Waren trotzdem 
auch Dateinamen.

Frühe UNIX-Dateisysteme konnten auch bloß 14 Zeichen, erst seit BSD UFS 
wurden es 255.

von Rolf M. (rmagnus)


Lesenswert?

Harald K. schrieb:
> Zum Beispiel auch Leerzeichen in Dateinamen oder Pfaden; es gibt
> Softwareentwickler, die das können, und es gibt welche, die davon
> gründlich überfordert sind.

Es gibt aber auch diejenigen, die es bewusst vermeiden, weil sie wissen, 
dass es andere Software gibt, die damit Probleme hat. Und dann gibt es 
die, denen das egal ist und die es deshalb einfach den Anwender ausbaden 
lassen, weil sie eben in ihrer "kindlichen" Eigenschaft lieber stolz 
präsentieren wollen, dass sie mit Leerzeichen zurechtkommen.

: Bearbeitet durch User
von Harald K. (kirnbichler)


Lesenswert?

Rolf M. schrieb:
> Und dann gibt es
> die, denen das egal ist und die es deshalb einfach den Anwender ausbaden
> lassen, weil sie eben in ihrer "kindlichen" Eigenschaft lieber stolz
> präsentieren wollen, dass sie mit Leerzeichen zurechtkommen.

Welches Problem verursacht eine Software, die mit Leerzeichen in 
Pfaden/Dateinamen zurechtkommt, bei den Anwendern?

von Wilhelm M. (wimalopaan)


Lesenswert?

Von fehlender Optimierung zu unportablen Dateinamen: so liebe ich die 
Threads hier ;-)

von Roland .. (rowland)


Lesenswert?

Vielen Dank Dir Wilhem M. für das Anstoßen der Fehleranalyse und dem 
Mitarbeiten am Patch, sowie natürlich an Roger Sayle, Richard Biener und 
Segher Boessenkool, die sich des Problems gleich angenommen haben.

Ich finde es schon großartig, dass diese Modifikation nun in so kurzer 
Zeit Einzug in die nächste Compilerversion hält, zumal das Problem ja 
nur eine fehlende Optimierung betrifft. Eine wirklich großartige 
Open-Source-Community.

Beste Grüße,
Roland.

: Bearbeitet durch User
von C-hater (c-hater)


Lesenswert?

Roland .. schrieb:
> Vielen Dank Dir Wilhem M. für das Anstoßen der Fehleranalyse und dem
> Mitarbeiten am Patch, sowie natürlich an Roger Sayle, Richard Biener und
> Segher Boessenkool, die sich des Problems gleich angenommen haben.
>
> Ich finde es schon großartig, dass diese Modifikation nun in so kurzer
> Zeit Einzug in die nächste Compilerversion hält, zumal das Problem ja
> nur eine fehlende Optimierung betrifft. Eine wirklich großartige
> Open-Source-Community.

Nunja, bei den AVR8 als Target bin ich sehr froh, dass ich keine 
derartigen Abhängigkeiten von einem wohlwollenden, weltweit verteilten 
Entwicklerkollektiv habe...

Asm rules...

Beitrag #7406955 wurde von einem Moderator gelöscht.
von Roland .. (rowland)


Lesenswert?

Zweifelsohne hat es schon einen Reiz, wenn man in Assembler programmiert 
und so das Maximum an Effizienz aus dem Controller herausholt, was oft 
auch mit sportlichem Ehrgeiz einhergeht. Je nach Umfang des Projekts 
kann es mitunter aber auch Mühsam und Unübersichtlich sein.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Roland .. schrieb:

> Je nach Umfang des Projekts
> kann es mitunter aber auch Mühsam und Unübersichtlich sein.

Unser C-Hasser kann sich ja mal daran versuchen:

https://github.com/threeme3/usdx

Mal sehen, wie viele Jahre es dauert, bis er das in Assembler 
geschrieben hat …

von Harald K. (kirnbichler)


Lesenswert?

Roland .. schrieb:
> Zweifelsohne hat es schon einen Reiz, wenn man in Assembler programmiert
> und so das Maximum an Effizienz aus dem Controller herausholt

Du schreibst das so, als würde das reine Programmieren in Assembler das 
bereits implizieren. Tut es nicht, man kann natürlich auch in Assembler 
problemlos ineffizienten Code schreiben. Und die meisten werden, auch 
wenn das, was sie schreiben, nicht direkt ineffizient ist, noch weit 
entfernt sein von dem, was wirklich das mögliche Maximum ist, viel mehr, 
die meisten werden noch nicht mal gegen einen anständigen Compiler 
anstinken können.

von Rolf M. (rmagnus)


Lesenswert?

Harald K. schrieb:
> Welches Problem verursacht eine Software, die mit Leerzeichen in
> Pfaden/Dateinamen zurechtkommt, bei den Anwendern?

Lies meinen Beitrag nochmal. Ich sprach von Problemen, die durch 
Software ausgelöst wird, die damit nicht zurechtkommt.

von Harald K. (kirnbichler)


Lesenswert?

Du hast das hier geschrieben:

Rolf M. schrieb:
> Und dann gibt es die, denen das egal ist und die es deshalb
> einfach den Anwender ausbaden lassen, weil sie eben in ihrer
> "kindlichen" Eigenschaft lieber stolz präsentieren wollen,
> dass sie mit Leerzeichen zurechtkommen.

Das bedeutet, daß diejenigen, die "stolz präsentieren wollen, daß sie 
mit Leerzeichen zurechtkommen", es "den Anwender ausbaden lassen".

Beitrag #7407824 wurde von einem Moderator gelöscht.
von C-hater (c-hater)


Lesenswert?

Jörg W. schrieb:

> https://github.com/threeme3/usdx
>
> Mal sehen, wie viele Jahre es dauert, bis er das in Assembler
> geschrieben hat …

Monate ja, ohne Zweifel, aber sicher keine Jahre. Und nach diesen 
Monaten bin ich ziemlich sicher, dass ich etwas geschaffen hätte, was 
diesen C-Kram bezüglich der Performance sehr deutlich in den Schatten 
stellt.

Ich brauche nur kurz in das Kompilat zu schauen, um die erheblichen 
Redundanzen selbst bei -O3 zu sehen. Das könnte ich mit an Sicherheit 
grenzender Wahrscheinlichkeit deutlich schlagen.

Dazu kommt noch: du schummelst! Der Code enthält unzählige in (inline-) 
Asm programmierte Schnipsel und umschifft nur allein damit die größten 
Schwachen des Compilers. Wäre auch dieser ganze Kram in Plain-C 
programmiert, wäre das Ergebnis noch viel, viel verheerender.

Aber: Ich habe wirklich kein Interesse an Amateurfunk. Und für 
Anwendungen derselben Algorithmen in Bereichen, die mein Interesse 
erwecken, ist ein AVR8 schlicht nicht schnell genug.

: Wiederhergestellt durch Moderator
von Rolf M. (rmagnus)


Lesenswert?

Harald K. schrieb:
> Das bedeutet, daß diejenigen, die "stolz präsentieren wollen, daß sie
> mit Leerzeichen zurechtkommen", es "den Anwender ausbaden lassen".

Du hast leider vergessen, den entscheidenden Teil mit zu zitieren:

Rolf M. schrieb:
> Es gibt aber auch diejenigen, die es bewusst vermeiden, weil sie wissen,
> dass es andere Software gibt, die damit Probleme hat.

: Bearbeitet durch User
von Harald K. (kirnbichler)


Lesenswert?

Rolf M. schrieb:
> Du hast leider vergessen, den entscheidenden Teil mit zu zitieren:

Das machts nicht besser. "Es gibt auch diejenigen" ... "und dann gibt es 
die" ... ist eine klassische Gegenüberstellung von Gegensätzen.

Du hast Dich da missverständlich ausgedrückt, aber es ist ja schön, daß 
wir die Sache inhaltlich auf die gleiche Weise sehen.

Damit können wir die Leerzeichen jetzt wohl ruhen lassen.

Beitrag #7407991 wurde von einem Moderator gelöscht.
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

C-hater schrieb:
> erheblichen Redundanzen selbst bei -O3

Klar – weil du -O3 schlicht nicht verstanden hast.

Musst du auch nicht (da du ja keine Compiler benutzen willst), aber über 
Sachen, die man nicht verstanden hat, sollte man nicht herum unken.

Es hat einen Grund, dass -Os bei MCUs die beliebteste 
Optimierungseinstellung ist.

: Bearbeitet durch Moderator
von C-hater (c-hater)


Lesenswert?

Jörg W. schrieb:

> Klar – weil du -O3 schlicht nicht verstanden hast.

Na dann: Klär mich auf!

Was macht diese "Optimierung" genau, was ist ihr Ziel? Und warum muß man 
sich den Code des Compilers anschauen, um das heraus zu finden? Sowas 
sollte klar und eindeutig dokumentiert sein.

Ein Werkzeug ohne brauchbare Dokumentation ist lebensgefährlich. Darauf 
sollte sich niemand verlassen müssen. Ich brauch' das auch nicht, weil 
dieses "Werkzeug" halt nur als Quelle der Erheiterung verwende 
(zumindest was AVR8 betrifft). Allerdings: die bezüglich AVR8 gewonnenen 
Erkenntnisse haben mich auch vieles gelehrt bezüglich anderer 
Zielarchitekturen, wo ich diesen Scheiß aus praktischen Gründen 
tatsächlich oft verwenden muss.

Das Fazit ist: Brauchbar gut sind Compiler allenfalls im Mainstream. Und 
selbst da hinken sie der Entwicklung der Hardware immer deutlich 
hinterher und sind auch ansonsten suboptimal, weil sie immer ihr eigenes 
ABI einhalten müssen. Genau dieses konstante ABI ist aber der Pitfall, 
der ihnen viele der in Asm mögliche Optimierungen unmöglich macht. Und 
dies ist wiederum beileibe nicht auf AVR8 beschränkt. Hat man z.B. bei 
ARM genauso.

Diese zwei Architekturen sind mein primäres Interesse im kleinen 
µC-Bereich. Der PC-Kram ist hingegen heute so schnell, dass die 
(gegenüber dem Optimum) schlechte Compiler-Performance kaum noch 
relevant ist. Hier ist das Problem eher die immer weiter ausuferende 
Verwendung immer fetterer Frameworks mit noch höherer Abstraktionslevel 
(betrifft allerdings auch die "dicken" ARMs).

: Wiederhergestellt durch Moderator
Beitrag #7408022 wurde von einem Moderator gelöscht.
von C-hater (c-hater)


Lesenswert?

Jörg W. schrieb:

> Es hat einen Grund, dass -Os bei MCUs die beliebteste
> Optimierungseinstellung ist.

Diesen Satz hatte ich komplett überlesen. Das soll aber wohl ein Witz 
sein? Klar kann es in wichtig sein, ob eine Anwendung überhaupt noch in 
in den Flash eines µC passt. Wenn man wenigstens 10000er Stückzahlen der 
Lösung produziert und der (bezüglich Flash-Size) nächstgrößere µC der 
Produktlinie exorbitant mehr kostet oder es gar keinen in der Linie mit 
größerem Flash mehr gibt. Ansonsten:

Viel wichtiger ist aber im Allgemeinen doch wohl, ob die Anwendung ihren 
Job überhaupt erledigen kann. Und das von dir gewählte Beispiel ist so 
eine Sache, bei der wohl eher die Rechenzeit der begrenzende Faktor ist. 
Das ist bei Signalverarbeitung meist so. Und der übliche Ausweg ist 
halt: Tabellen. Macht der Compiler bei Speed-Optimierung so und 
natürlich auch der kompetente Asm-Programmierer. Der kann es nur viel 
besser (wenn er hinreichend gut ist).

: Wiederhergestellt durch Moderator
von Oliver S. (oliverso)


Lesenswert?

C-hater schrieb:
> Was macht diese "Optimierung" genau, was ist ihr Ziel? Und warum muß man
> sich den Code des Compilers anschauen, um das heraus zu finden? Sowas
> sollte klar und eindeutig dokumentiert sein.

Das ist es. -O3 opfert Programmgröße für Speed. Loop-unrolling, massives 
inlining, usw. Nur ist das alles auf einem AVR ziemlich wirkungslos, da 
bei dem durch den (fast) echten RISC-Befehlssatz und wegen fehlenden 
Späßchen wie Sprungvorhersagen, mehrstufigen Caches, und all den anderen 
Hardwarespirenzchen größerer Prozessoren kürzerer Code halt doch 
schneller ist als längerer.

Daher nimmt man da -Os, und alles wird so gut, wie es halt geht.

Oliver

Beitrag #7408039 wurde von einem Moderator gelöscht.
von C-hater (c-hater)


Lesenswert?

Oliver S. schrieb:

> Das ist es. -O3 opfert Programmgröße für Speed.

Das war auch, was mir bekannt ist. Umso überraschender war für mich der 
Einwurf von Jörg W. Seines Zeichens Moderations-Macht-Missbraucher 
(nicht in diesem Thread (zumindest noch nicht, ich warte aber förmlich 
darauf), aber in anderen, das kann ich detailliert nachweisen!).

-O3 sollte also theoretisch das Maximum an Speed aus dem Compiler 
herausholen. Damit ist es das, mit dem sich mein Asm-Code vergleichen 
müsste. Genau deswegen habe ich auch das mit eben dieser Option 
übersetzte Kompilat als Referenz benutzt.

Und da kann ich eben ganz klar sagen: das könnte ich ganz sicher 
deutlich besser als es der Compiler kann.

Wie eigentlich jede AVR8-Sache. Der Compiler ist für AVR8 als Target 
halt einfach ziemliche Scheiße. Da spielen mehrere Sachen hinein. Zum 
einen ist jeder C-Compiler bei AVR8 als Zielarchitektur gleich doppelt 
behindert. Die mögen weder Harvard-Architektur noch native 
8Bit-Verarbeitung. Was die wollen, um einigermaßen effizient zu sein 
ist: Die Verabeitung passiert mit (mindestestens) 16Bit. Und es gibt nur 
einen Addressraum. Und: Es gibt keine Nebenläufigket.

All das ist halt bei typischen AVR8-Anwendungen nicht gegeben. Deswegen 
ist es gerade bei dieser Architektur so überaus einfach, als 
Asm-Programmierer sehr deutlich besser zu sein als der Compiler...

: Wiederhergestellt durch Moderator
von Oliver S. (oliverso)


Lesenswert?

C-hater schrieb:
> Der Compiler ist für AVR8 als Target
> halt einfach ziemliche Scheiße.

Wenn man das aus deiner Ausdrucksweise ins normale Deutsch übersetzt, 
stimmt das in etwa. gcc ist für AVR8 nicht perfekt, aber zumindest 
brauchbar.

Oliver

Beitrag #7408065 wurde von einem Moderator gelöscht.
von C-hater (c-hater)


Lesenswert?

Oliver S. schrieb:

> Wenn man das aus deiner Ausdrucksweise ins normale Deutsch übersetzt,
> stimmt das in etwa. gcc ist für AVR8 nicht perfekt, aber zumindest
> brauchbar.

Far away from to be perfect.

Klar: Um ein paar LEDs blinkenzu lassen oder primitive state-machines 
mit ein wnig Input und Output zu implementieren reicht es. Und mehr hat 
der Herr Jörg W. auch niemals gezeigt...

Das von ihm selber gewählte Beispiel für mehr ist ja bezeichnenderweise 
auch nicht von ihm...

: Wiederhergestellt durch Moderator
Beitrag #7408118 wurde von einem Moderator gelöscht.
Beitrag #7408124 wurde von einem Moderator gelöscht.
Beitrag #7408153 wurde von einem Moderator gelöscht.
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

C-hater schrieb:
> Damit ist es das, mit dem sich mein Asm-Code vergleichen müsste.

Du hast den Vergleich aber mit der Größe gemacht, nicht der 
Geschwindigkeit: "erheblichen Redundanzen selbst bei -O3".

Daher die Aussage: diese "Redundanzen" sind bei -O3 schlicht Absicht, 
nur bringen sie beim AVR aufgrund seiner Architektur kaum was. Deshalb 
nimmt man diese Stufe dort schlicht nicht. Wenn du auf Redundanzen 
vergleichen willst, dann nimm -Os: "Zusätzlich (zu Stufe 1) noch alle 
die Optimierungen von Stufe 2, die (normalerweise) den Code nicht größer 
werden lassen", "optimized for size" eben, nichts anderes heißt es.

Dass du sowieso alles viel besser kannst, wussten wir auch so schon. Nur 
ist halt der Aufwand dafür ein Mehrfaches. Wenn man sich das leisten 
kann: kein Problem, mach doch. Da ich für den nicht genutzten Flash von 
Microchip nichts zurück bekomme, leiste ich mir den Aufwand in der Regel 
eher nicht.

Beitrag #7408211 wurde von einem Moderator gelöscht.
Beitrag #7408214 wurde von einem Moderator gelöscht.
Beitrag #7408420 wurde von einem Moderator gelöscht.
von Roland F. (rhf)


Lesenswert?

Hallo,
C-hater schrieb:
> Das könnte ich mit an Sicherheit grenzender Wahrscheinlichkeit
> deutlich schlagen.

Ich lese hier immer wieder das die heutigen Compiler Maschinencode in 
einer Qualität erzeugen, der nur schwer von handgeschriebenen Assembler 
übertroffen werden kann. Jetzt behauptest du genau das Gegenteil.

Zeig doch mal an einem typischen, nicht zu simplen, Beispiel wo ein 
handassemblierter Maschinencode den C-Compiler signifikant schlägt.

rhf

von Harald K. (kirnbichler)


Lesenswert?

Selbstüberschätzung ist gerade bei Verfechtern der 
Assemblerprogrammierung gar nicht so selten. Im Gegensatz zu "moby" 
traue ich dem Programmiersprachenhasser aber wenigstens etwas mehr 
Kompetenz zu. Vor allem kann er besser fluchen.

Und was wäre ein guter Programmierer ohne anständiges Gefluche?

Beitrag #7408561 wurde vom Autor gelöscht.
Beitrag #7408562 wurde von einem Moderator gelöscht.
Beitrag #7408605 wurde von einem Moderator gelöscht.
Beitrag #7409263 wurde von einem Moderator gelöscht.
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Da Moby den Thread nur noch zum permanenten Wiederholen seiner 
Auslassungen missbraucht: hier ist eh alles geschrieben worden 
inzwischen, was zum Thema beiträgt.

Beitrag #7409378 wurde von einem Moderator gelöscht.
Beitrag #7409381 wurde von einem Moderator gelöscht.
Dieser Beitrag ist gesperrt und kann nicht beantwortet werden.