Der Zustand einiger voneinander unabhängiger Bits eines Bytes (uint8_t)
wird überwacht und auswertet.
Ein vereinfachtes und extrem verständliches Beispiel dazu wäre:
Bit 3 == 1 ---> LED on
Bit 3 == 0 ---> LED off
1
uint8_tb1;
2
.....
3
.....
4
if((b1&0x08)==0x08)// if b1 == xxxx1xxx
5
{
6
PORTA.OUT|=0x08;// set PA3 (LED on)
7
}
8
else// if b1 == xxxx0xxx
9
{
10
PORTA.OUT&=0xF7;// clear PA3 (LED off)
11
}
12
.....
Kann man dieses Beispiel irgendwie ohne if() umsetzen?
(ist nicht sehr wichtig, nur eine theoretische Frage)
Diese if-Anweisung kann der Compiler gut erkennen und entsprechend
optimieren.
Z.B. mit Bit-Setz oder Lösch-Befehlen, sofern auf der Architektur
vorhanden
Schützt die OPAMP-Tiere! schrieb:> Falls der Port auch mal kurz auf 0 gehen darf:> immer mit &= clearen, dann geshiftetes ergebnis der if bedingung wieder> "rein-Odern"...
Es wäre schön. Leider dürfen unbeteiligte Bits nicht angefasst werden.
Georg M. schrieb:> wird auswertet
Naja...
Das scheint mir gute Lösung zu sein.
Ein Compiler in wirklich hoher Optimierungsstufe sollte allerdings die
Vorgabe-Lösung des TE im Idealfall exakt genauso hinkriegen! Vielleicht
sogar codeidentisch.
Gruss
Mich würde interessieren, was der TO gegen if / else hat.
Klar, damit kann man unübersichtlich verschachteln, aber bei dem
genannten Beispiel ist das ja nicht so.
Und andere Konstrukte, wie ?: wird der Compiler zum ebenso effizienten
Code auflösen, ich vermute, dass auch die Variante von Gerald K.
annähernd den gleichen Assemblercode ergibt.
Also, warum nicht if und else?
HildeK schrieb:> Also, warum nicht if und else?
War nur eine theoretische Frage des TO's ohne praktischem Nutzen.
Georg M. schrieb:> Kann man dieses Beispiel irgendwie ohne if() umsetzen?> (ist nicht sehr wichtig, nur eine theoretische Frage)
Man kann jedes if() durch ein while() mit einer Hilfsvariable ersetzen.
Aufgabe formal erfüllt. Man kann sich auch ein Loch ins Knie bohren und
La Paloma drauf pfeifen lassen. Ähnlich sinnvoll.
Stefan ⛄ F. schrieb:> ((b1 & 0x08) == 0x08) && PORTA.OUT |= 0x08 || PORTA.OUT &= 0xF7;>> Ist nur geraten, nicht getestet.
Sieht korrekt aus, ob short-logic hier bekannt/beliebt ist ...?
ABER, die Sequenze ist (eigentlich) gleich dem if/else des TO.
Aus meiner Sicht, gibts keinen Weg an if Konstrukten vorbei.
Klar kann man in manchen Fällen einen alternativen Weg finden.
Nur ist es dann lesbarer?
Daran entscheidet sich der Sinn in erster Linie.
Natürlich macht es Sinn möglichst flache Programme zu schreiben.
Möglichst geringe Schachtelungstiefen, einfache Bedingungen.
Eben genau, wegen der Lesbarkeit.
Ein Weg:
Viele kleine gut benannte Funktionen, statt wenige aufgeblähte.
Wie wäre es mit einer Switch Case Anweisung?
Schaue hier :
https://www.c-howto.de/tutorial/verzweigungen/switch-case/
int a=2;
switch(a) {
case 1: printf("a ist eins\n"); break;
case 2: printf("a ist zwei\n"); break;
case 3: printf("a ist drei\n"); break;
default: printf("a ist irgendwas\n"); break;
}
Freizeit Coder schrieb:> Wie wäre es mit einer Switch Case Anweisung?> Schaue hier :> https://www.c-howto.de/tutorial/verzweigungen/switch-case/>> int a=2;>> switch(a) {> case 1: printf("a ist eins\n"); break;> case 2: printf("a ist zwei\n"); break;> case 3: printf("a ist drei\n"); break;> default: printf("a ist irgendwas\n"); break;> }
Hmm,
und was hat jetzt diese deine Erläuterung
zu tun mit
der Fragestellung (= erster Beitrag).
Erich schrieb:> Hmm,> und was hat jetzt diese deine Erläuterung> zu tun mit> der Fragestellung (= erster Beitrag).
Das er das Programmieren erfunden hat. Alle anderen sind in seinen Augen
Idioten. Er ist der Macker. Nur er. Ohne Helm und ohne Gurt.
Kot Er.
Also: ein ?:, Ggf mit Komma statt {} kann viele ifs ersetzen.
Eine binäre Zuweisung aus einer binären Frage geht meist ohne jedes If
oder dessen äquivalent
&& Und || erlauben viele if-äquivalente Konstrukt
(Kurzschluss-Auswertung)
Switch/Case, for und while können immer als If missbraucht werden, als
Else nur teilweise.
Weitere Möglichkeiten über funktionsptr-Arrays und und und.
Wenn Du einen der genannten Ansätze nicht Kennst, einfach fragen
Wenn AVR:
> von Docureader (Gast) 10.06.2022 16:28
PORTA.OUT = __builtin_avr_insert_bits(0xfffff2ff, b1, PORTA.OUT);
Wird in 4 Maschinen-Befehlen (in, lds, bst, out) ausgeführt.
Kürzer, schneller, eleganter und edler geht es nicht.
Man muss nur wissen wie.
Eggsbert schrieb:> Kürzer, schneller, eleganter und edler geht es nicht.
Von anderen AVR Modellen bin ich gewohnt, dass das Setzen/Löschen eines
einzelnen Bits in jeweils eine einzige Instruktion übersetzt wird.
Auszug aus einem Assembler Listing:
1
PORTB |= (1<<PB4);
2
e4: c4 9a sbi 0x18, 4 ; 24
3
4
PORTB &= ~(1<<PB5);
5
e6: c5 98 cbi 0x18, 5 ; 24
Das hat allerdings nichts mit der Frage des TO zu tun.
Ich schrieb:
>> ((b1 & 0x08) == 0x08) && PORTA.OUT |= 0x08 || PORTA.OUT &= 0xF7;Apollo M. schrieb:> Sieht korrekt aus, ob short-logic hier bekannt/beliebt ist ...?
Bei mir ist es jedenfalls nicht beliebt. Aber wenn er schon so fragt ...
Ich kenne dieses Konstrukt eher von Shell Scripten, zum Beispiel:
In diesem Fall bricht das Script bei jedem Fehler sauber ab, ohne
Folgefehler zu produzieren. Insbesondere das Umbenennen von summary.csv
nach summary.old soll nur stattfinden, wenn der Download und das
Auspacken der ZIP Datei erfolgreich war.
> Bei mir ist es jedenfalls nicht beliebt.
Short-Circuit evaluation ist elementarer Bestandteil von C und davon
abgeleiteten Sprachen.
Um die zu umgehen, muss man reichlich sinnlosen und aufgeblähten Code
schreiben.
A. S. schrieb:> Weitere Möglichkeiten über funktionsptr-Arrays und und und.
Oder ganz allgemein: reine Daten-Zweige sind auf zig weitere Arten
möglich (Rückgabewert abhängig vom Argument). Die Wege für den
kontrollfluas sind begrenzter und größtenteils schon gelistet (mir fällt
so schnell kein weiterer ein, vielleicht über mehr experimentelle Sachen
wie Exit, ljump, typeof in Makros, ...
Gerald K. schrieb:> HildeK schrieb:>> Also, warum nicht if und else?>> War nur eine theoretische Frage des TO's ohne praktischem Nutzen.
Aber der Hintergrund der Frage wäre schon interessant zu wissen, auch im
Bezug auf die Antworten. Geht es eher darum, zu vermeiden, dass der
Computer eine Verzweigung ausführt? Dann wird die Antwort sehr
kompliziert (oder sehr einfach, je nach Sichtweise). Oder ist es eher
eine sprachliche Frage, also welche Sprachkonstrukte es gibt, die im
Prinzip das gleich leisten können?
Oder gibt es keinen Hintergrund, und die Frage ist wirklich komplett aus
der Luft gegriffen? Dann ist sie irgendwie sinnlos.
> if((b1 & 0x08) == 0x08) // if b1 == xxxx1xxx
kann man kürzer schreiben:
if(b1 & 8)
Ein Ausdruck ist dann wahr, wenn er ungleich 0 ist, man muss also nicht
explizit mit 8 vergleichen, es reicht, wenn !=0.
Korrektur:
> Wird in 4 Maschinen-Befehlen (in, lds, bst, out) ausgeführt.
Es sind 5 Befehle: in, lds, bst, bld, out
> Von anderen AVR Modellen bin ich gewohnt, dass das Setzen/Löschen> eines einzelnen Bits in jeweils eine einzige Instruktion übersetzt wird.
Schon, aber bei bst (Bit Store from Register to T) wird das Bit im
Status-Flag T zwischengespeichert und mit bld (Bit Load from T to
Register) ins Zielregister kopiert (eigentlich würde man es von store
und load genau andersherum erwarten).
Rolf M. schrieb:> Aber der Hintergrund der Frage wäre schon interessant zu wissen, auch im> Bezug auf die Antworten.Georg M. schrieb:> Der Zustand einiger voneinander unabhängiger Bits eines Bytes (uint8_t)> wird überwacht und auswertet.
Da habe ich jetzt rausgelesen, normalerweise sind es mehr Bits als in
dem Beispiel. Deshalb:
Ralf G. schrieb:> 'switch/case'> Sieht manchmal schöner aus...
Jedes mögliche Bitmuster bekommt seinen 'case-Zweig'. Das sieht für mich
dann aufgeräumter aus als immer 'if-else-if-if-else'...
Eggsbert schrieb:> PORTA.OUT = __builtin_avr_insert_bits(0xfffff2ff, b1, PORTA.OUT);> Wird in 4 Maschinen-Befehlen (in, lds, bst, out) ausgeführt.> Kürzer, schneller, eleganter und edler geht es nicht.> Man muss nur wissen wie.
Verstehe nur Bahnhof und blicke es nicht.
Kann das mal erklärt werden hins. 0xfffff2ff, ...
Wo kommt der Wert her? Warum ist der so lang?
Der Zweck wird von gnu genannt, aber wie der damit erfüllt wird kapiert
ich auch nicht. Wie kann ich da über mehrere Ports an den Bits einfach
rumfummeln?
Apollo M. schrieb:> Eggsbert schrieb:>> PORTA.OUT = __builtin_avr_insert_bits(0xfffff2ff, b1, PORTA.OUT);>> Wird in 4 Maschinen-Befehlen (in, lds, bst, out) ausgeführt.>> Kürzer, schneller, eleganter und edler geht es nicht.>> Man muss nur wissen wie.>> Verstehe nur Bahnhof und blicke es nicht.> Kann das mal erklärt werden hins. 0xfffff2ff, ...> Wo kommt der Wert her? Warum ist der so lang?>> Der Zweck wird von gnu genannt, aber wie der damit erfüllt wird kapiert> ich auch nicht. Wie kann ich da über mehrere Ports an den Bits einfach> rumfummeln?
Mal abgesehen davon, daß das mit "..2FFF" falsch ist (und vom Beitrag
Schreiber inzwischen korrigiert),
braucht das NIEMAND verstehen, der nicht optimierten Assemblercode für
genau diese Prozessor-Architektur erstellen wollen.
Alle anderen schreiben vernünftig lesbaren und kommentierten C-Code, den
sie selbst verstehen und auch in 1/2 Jahr noch kampieren (dank der
eigenen Kommentare).
Den Rest besorgt der C-Compiler mit Optimierungslevel 3 von 4 oder der
höchsten.
Gruss
EAF schrieb:> Aus meiner Sicht, gibts keinen Weg an if Konstrukten vorbei
Schade eigentlich.
Damit bist du ungeeignet um hochoptimierte Assemblerprogramme zu
erstellen.
Denn ein Sprung unterbricht auf vielen Prozessorarchitekturen die
Pipeline und kostet mehr Takte als z.B. das hier angemessene
Mario M. schrieb:> tmp = PORTA.OUT & 0xf7;> PORTA.OUT = tmp | b1 & 8;
Komplette Programme ohne einen einzigen Sprung sind natürlich arg
limitiert: sie laufen nur ein Mal ab und können nicht viele Dinge
treiben. Aber es gibt sie, so auf dem Level der AWL einer SPS.
HildeK schrieb:> Mich würde interessieren, was der TO gegen if / else hat.
Abgesehen von "Code beliebig komplex schreiben" fällt mir auf die
schnelle tatsächlich eine Prozessorplattform ein, die einerseits eine
extrem schlechte Branch-Performance aufweist, gleichzeitig vom
Hersteller aber jahrelang einen Compiler spendiert bekam der nicht fähig
war, beispielsweise ein "if/else" zu einer (wesentlich performanteren)
bedingten Zuweisung zu optimieren, wenn mans nicht extra als
Ternäroperation hinschrieb.
vn nn schrieb:> der nicht fähig> war, beispielsweise ein "if/else" zu einer (wesentlich performanteren)> bedingten Zuweisung zu optimieren, wenn mans nicht extra als> Ternäroperation hinschrieb.
Naja...
Ist es doch so, dass das if() Statement für den Kontrollfluss zuständig
ist, und der ?: Operator für Daten.
Optimierungen werden nicht vom Standard eingefordert.
Also eigentlich alles richtig.
Deiner Aussage nach sind die betreffenden Compiler "besser" geworden.
Also auch da alles richtig und gut so.
EAF schrieb:> Ist es doch so, dass das if() Statement für den Kontrollfluss zuständig> ist, und der ?: Operator für Daten.
Was natürlich in sehr vielen Alltagssituationen aufs gleiche hinaus
läuft, insbesondere bei komplexer Regelungstechnik statt Pins toggeln.
EAF schrieb:> Optimierungen werden nicht vom Standard eingefordert.
Hat auch keiner behauptet, man geht im 21. Jahrhundert halt davon aus
dass ein Compiler zumindest ansatzweise sinnvoll optimieren kann.
EAF schrieb:> Also eigentlich alles richtig.
Natürlich. Es fordert ja auch kein Standard dass ich mir nicht von
hinten durchs Knie ins Auge schießen kann, somit alles richtig.
EAF schrieb:> Deiner Aussage nach sind die betreffenden Compiler "besser" geworden.
Ich wüsste nicht wo ich das geschrieben haben soll, ich kenn lediglich
die aktuelle Situation nicht, nachdem ich schon vor einigen Jahren den
Arbeitsgeber gewechselt habe.
Nachdem der Prozessorhersteller aber bis heute immer noch auf seine
mistigen proprietären Compiler statt auf GCC oder LLVM setzt und immer
noch damit kämpft, überhaupt mal den Standard einzuhalten, gehe ich
nicht davon aus dass er übers simpelste hinausgehende Optimierungen
hinbekommt.
EAF schrieb:> Also auch da alles richtig und gut so.
Ja, alles gut, es gibt nichts zu sehen, Mindestmaß ist ja erfüllt, mehr
Ansprüche hat ja eh keiner.
vn nn schrieb:> Abgesehen von "Code beliebig komplex schreiben" fällt mir auf die> schnelle tatsächlich eine Prozessorplattform ein, die einerseits eine> extrem schlechte Branch-Performance aufweist, gleichzeitig vom> Hersteller aber jahrelang einen Compiler spendiert bekam der nicht fähig> war, beispielsweise ein "if/else" zu einer (wesentlich performanteren)> bedingten Zuweisung zu optimieren, wenn mans nicht extra als> Ternäroperation hinschrieb.
Sowas kann im konkreten Fall sicher nicht das Problem sein. Denn: es
handelt sich ganz offensichtlich ja nicht um eine simple Zuweisung mit
Bedingung. Allerhöchstens dann, wenn die Zielarchitektur
Bitinstruktionen kennt.
Damit dein Geschwalle also überhaupt irgendeinen praktischen Nutzen
entfalten kann, braucht es eine Architektur sowohl mit Codecache als
auch mit der der Fähigkeit zu Bitinstruktionen auf jedes beliebige Ziel.
Da wird die Luft doch schon arg dünne...
Und wenn es so eine Architektur gibt, der Compiler das aber nur für die
ternäre Schreibweise korrekt umsetzt, dann bedeutet das nicht mehr und
nicht weniger als: der Compiler taugt nix.
Eggsbert schrieb:> Wenn AVR:>> von Docureader (Gast) 10.06.2022 16:28>> PORTA.OUT = __builtin_avr_insert_bits(0xfffff2ff, b1, PORTA.OUT);> Wird in 4 Maschinen-Befehlen (in, lds, bst, out) ausgeführt.> Kürzer, schneller, eleganter und edler geht es nicht.> Man muss nur wissen wie.
Naja, so RMW-Sequenzen müssen ggf. auch noch gegen Interrupts gesperrt
werden.
Da es ja um AVR zu gehen scheint, könnte man auch vermuten, dass es sich
dabei um die moderneren AVRs haben, bei denen man gar kein RMW braucht,
sondern das über die CLR / SET-Register kürzer und atomar machen kann.
Georg M. schrieb:> Kann man dieses Beispiel irgendwie ohne if() umsetzen?> (ist nicht sehr wichtig, nur eine theoretische Frage)
Ja, z.B. mit for(...) oder while(...).
Ein extremes Beispiel findet sich in den Quelltexten des Apple 2
Betriebssystems.
In dem Assemblercode finden sich abstruse and/or/xor Verknüpfungen, bei
denen vollkommen undurchschaubare Ergebnisse übrig bleiben. Und auf
dieses Kuddelmuddel werden dann weiter Tests gemacht - bei denen niemand
mehr durchblickt.
Lieber vollkommen undurchschaubare Verknüpfungen als ein Sprungbefehl zu
viel.
Der praktische Nutzen? BS und Anwendungsprogramm passen in 64 Kilobyte.
Wilhelm M. schrieb:> Da es ja um AVR zu gehen scheint, könnte man auch vermuten, dass es sich> dabei um die moderneren AVRs haben, bei denen man gar kein RMW braucht,> sondern das über die CLR / SET-Register kürzer und atomar machen kann.
Wenn nur ein toggle/xor gewünscht ist, könnte man das selbst bei älteren
AVR (z.B. ATmega644) sehr effizient machen: Bei denen kann man durch
Schreiben des PINx Registers die 1-bits im PORTx Register toggeln.
1
PINB=(1<<PB1);
2
// ldi r18, 2
3
// out PINB, r18
4
5
// Equivalent to:
6
// PORTB ^= (1<<PB1)
7
// in r18, PORTB
8
// ori r18, 2 ; sbr r18, 1
9
// out PORTB, r18
Als kleine Spezialität wird sbi nicht als RMW des Registers
ausgeführt, sondern ist ein echter Bit-Befehl und führt direkt zu
bit-xor im PORTx Register. Das ist vielleicht nicht jedem auf Anhieb
offensichtlich...