Noch eine Variante wäre
if (c && (c<=3)) {..}
Noch eine Variante wäre
if (c && (c<=3)) {..}
Ich persönlich halte das für Mikrooptimierung.
Ich für noch unsinniger. Schon gar, wenn es um UART geht. Und um dessen Fehlerbehandlung.
Ein if, das eine 16 Bit Zahl mit einer Konstanten vergleicht, dauert auf unseren 16 MHz Controllern weniger als eine µs, schätze ich mal. Inclusive Sprungbefehl. Bei 8Bit Werten und Test auf 0 noch weniger.
Da lohnt es nicht, sich das Disassembly anzusehen und Takte zu zählen, um diese Schätzung zu verifizieren.
Klar sind alle Tips hier ok, und dem Conpiler zu helfen, dass im Normalfall ohne Fehler möglichst wenig Code in der ISR durchlaufen wird, ist immer richtig.
Die interessante Gegenfrage wäre: "Wofür willstn das wissen? "
michael_x:
Ich für noch unsinniger.
Für dich habe ich noch einen Spruch dazu!
@ Doc_Arduino
Du hörtst jetzt besser weg!
Performancefrage
von wahsaga:
Forenposting mit Performancefrage Bewertungsfaustregel:
Wer Fragen nach der Performance solcher Kinkerlitzchen stellt, der programmiert vermutlich noch nicht mal ansatzweise performant. Andernfalls, wenn er wirklich und zu Recht an einer Applikation arbeiten würde, bei der dieses Quentchen entscheidend ist, sollte er diese Frage gar nicht mehr stellen müssen.
Hallo,
ihr könnt euch meinetwegen lustig machen. Ist mir egal. Was mich aber stört ist, dass man das Thema mit einer Handbewegung abschmettert als wenn das alles so dermaßen egal wäre, wie wenn in China ein Sack Reis umfällt. Dabei wisst ihr noch nicht einmal wofür das ist. Tja Micha, hättest du vorher gefragt wofür und dich dann lustig gemacht, wäre das okay gewesen. Leider haste die falsche Reihenfolge gewählt.
Ihr seit alles Leute von denen ich die Meinungen sehr schätze. Deswegen verate ich euch mein Motto. ![]()
"Alle sagen es funktioniert nicht. Dann kam jemand der wusste das nicht und hat es gemacht." So muss man rangehen!
Ich möchte ab jetzt nicht mehr hören wollen das ist alles sinnlos. Denn man lernt immer etwas dabei.
Also. Das wird eine Steuerung mit vielen Slaves. Die serielle Kommunikation läuft mit 250kBaud unter Zuhilfenahme von MAX487. Bisher absolut robuste Übertragung mit Klingeldraht. Wenn ich den aktuellen Code auf kurze 2 Byte Übertragung abändere, indem ich nur ein Byte sende und ein Byte empfange + je Steuerzeichen, dann habe ich eine Antwortverzögerung von 39,3µs. Gemessen zwischen Ende letztes Sendebit und Anfang erstes Empfangsbit. Vom Master aus gesehen. Das sind schon Größenordnungen wo man zumindestens daran denken kann nichts unüberlegtes zu programmieren. Finde ich.
In der Praxis werde ich mit 1 Byte Sende-Befehlen nicht auskommen. Aber ein Byte Empfang schon.
Jetzt mach ich mich mal ran um die if bzw. switch/case auszumessen.
hi, doc,
ich möchte mich hier sicher von niemandem distanzieren, weil ich die meisten sehr schätze, aber die richtung, die das genommen hat, ging mir auch ein wenig gegen den strich.
zu optimieren ist IMMER statthaft, auch wenn's "nichts ausmacht". und falls es einen unterschied macht, noch viel mehr.
gruß stefan
Tschuldigung, wenn es so rüberkam, dass ich mich lustig gemacht hätte.
Dass zwischen Senden-Ende und dem Empfang des ersten Antwort-Bit gar bald 40 µs Pause sind, macht das Ganze nur weniger kritisch.
250 kBd bedeutet zusätzliche 8 µs zwischen dem Senden eines Bytes und dem kompletten Empfang des Antwortbytes. Schade, dass in dieser Zeit (47 µs = viele hundert Takte) vermutlich gar nichts sinnvolles zu tun ist.
Wenn die Antwort erstmal da ist, sollte man natürlich nicht unnötig trödeln. Wie lange braucht denn der Master, bis er wieder was sendet? Wie lange darf er brauchen? Ein einzelnes if macht dabei aber nicht wirklich etwas aus. In den Arduino Funktionen ist noch viel Luft (rate ich mal), wenn es wirklich drauf ankäme.
Keiner sagt, etwas geht nicht. Du hast viele sinnvolle Tips gekriegt. Unter anderem den, dich lieber um wichtigeres zu kümmern.
Und danke für die erste Antwort auf meine Frage "Wofür willstn das wissen?"
"Optimieren ist IMMER statthaft" ok, das ja. Aber nicht immer wichtig.
Ich stimme auch zu, dass man sich generell hier bei den kleinen Controllern einen sparsamen Stil einfach angewöhnen sollte, und dazu gehört, sich Gedanken zu machen, wie man dem Compiler beim Optimieren helfen kann.
(Meist wird Speicherplatz-Optimierung übrigens für wichtiger gehalten, auch wenn im Endeffekt ungenutzter Speicher einfach so dumm rumliegt.)
Hallo,
ist schon okay Eisebaer und Michael, ich wollte es nur nicht unkommentiert stehen lassen. Ich habs verdaut. ![]()
Es mag sein das ich im fertigen "Projekt-Code", der noch wächst, viel Zeit mit anderen Dingen verdrödel und das alles keine Auswirkung haben wird. Dennoch möchte ich es auch in diesem Detail genau wissen was passiert. Ich nehme nicht alles immer als gegeben hin. Manche Dinge möchte ich nachprüfen. Wie schon gesagt, man lernt dabei. Der Code vom Master darf eine loop Zeit von 35ms nicht überschreiten. Das ist mein Hauptanliegen. Hängt mit der Reaktion auf Ereignisse zusammen.
Die Slaves sind übrigens ATtiny841 mit ihren internen 8MHz.
erster Test, alle Vergleiche werden als wahr erkannt.
mit if:
mit jeden zusätzlichen Vergleich der gemacht werden muss dauert es länger. Je eher einer wahr wird umso eher wird alles abgebrochen.
mit switch-case:
mit jedem zusätzlich geschriebenen case dauert switch-case insgesamt länger, jedoch bleiben die Zeiten konstant.
ab einer gewissen Anzahl Vergleiche die gemacht werden müssen, wenn immer einer wahr ist, dann liegt if vor switch.
/*
Doc_Arduino - german Arduino Forum
IDE 1.8.13
Arduino Mega2560
28.08.2017
*/
// add this to the top of your sketch
#define NOP __asm__ __volatile__ ("nop")
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) // setzt das angegebene Bit auf 1
#endif
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) // setzt (löscht) das angegebene Bit auf 0
#endif
byte i;
byte data = 8;
const byte pin_enableTX = 44; // Pin ein/aus
#define LED_EIN sbi (PORTL,5) //
#define LED_AUS cbi (PORTL,5) //
void setup() {
//Serial.begin(9600);
pinMode(pin_enableTX, OUTPUT);
digitalWrite(pin_enableTX, LOW); //
}
void loop() {
for ( i = 0; i < 8; i++ ) {
//Serial.print("data "); Serial.println(data); // Debug
// if Test ======================================= //
LED_EIN;
if (i == 0) {
data = 0; // LED ein 0,833µs
}
if (i == 1) {
data = 1; // LED ein 0,917µs
}
if (i == 2) {
data = 2; // LED ein 1,083µs
}
if (i == 3) {
data = 3; // LED ein 1,125µs
}
if (i == 4) {
data = 4; // LED ein 1,333µs
}
if (i == 5) {
data = 5; // LED ein 1,417µs
}
if (i == 6) {
data = 6; // LED ein 1,583µs
}
if (i == 7) {
data = 7; // LED ein 1,625µs
}
LED_AUS;
/* switch case Test //
==============================================================
LED_EIN;
switch (i) {
case 0: data = 0;
break;
case 1: data = 1;
break;
case 2: data = 2;
break;
case 3: data = 3;
break; // LED ein 1µs, alle vier konstant
// --------------------------------------------------
case 4: data = 4;
break;
case 5: data = 5;
break;
case 6: data = 6;
break;
case 7: data = 7;
break; // LED ein 2,2µs, alle acht konstant
default: data = 9; break;
}
LED_AUS;
==============================================================
*/
}
} // loop Ende
Hallo,
zweiter Test, alle Vergleiche werden als nicht wahr erkannt.
mit if:
da er alles vergleichen muss, weil keiner wahr wird, dauert jeder Durchgang gleich lang und nimmt mit jeden zusätzlich eingebauten Vergleich zu.
mit switch-case:
auch hier nimmt der gesamte case Vergleich mit jeden zusätzlich eingebauten case zu, bleibt jedoch auch konstant
>>> in dem Fall der unwahren Abfragen ist switch-case minimal schneller im Vergleich zu if.
Ja es sind "Mikrooptimierungen" die mir nichts bringen, dennoch verwende ich dafür nun switch-case, soll ja nicht umsonst gewesen sein. Es kommt eben drauf an was man abfragt und man kann bei if noch mit der Reihenfolge der Vergleiche nachhelfen. Die Wahrscheinlichsten "wahr Treffer" müssen immer zuerst überprüft werden. Bei switch case spielt die case Reihenfolge keine Geige, die Durchläufe sind immer konstant. Ist nur Abhängigkeit von der Anzahl der case's.
Ich hoffe den einen oder anderen hat das interessiert und ebenfalls zu neuen oder zu anderen Erkenntnissen gebracht.
Gelesen hatte ich nämlich hier und da gegenteiliges. Tenor war fast immer, dass switch-case immer schneller wie if sein soll. Erkenntnis ist jedoch, es kommt drauf an was man wie vergleicht.
Ich habe immer in 4er oder 8er Blöcken verglichen. Alle einzeln schien mir nicht nötigt für eine Erkenntnis.
/*
Doc_Arduino - german Arduino Forum
IDE 1.8.13
Arduino Mega2560
28.08.2017
*/
// add this to the top of your sketch
#define NOP __asm__ __volatile__ ("nop")
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) // setzt das angegebene Bit auf 1
#endif
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) // setzt (löscht) das angegebene Bit auf 0
#endif
byte i;
byte data = 8;
const byte pin_enableTX = 44; // Pin ein/aus
#define LED_EIN sbi (PORTL,5) //
#define LED_AUS cbi (PORTL,5) //
void setup() {
Serial.begin(9600);
pinMode(pin_enableTX, OUTPUT);
digitalWrite(pin_enableTX, LOW); //
}
void loop() {
for ( i = 0; i < 4; i++ ) { // 0 ... 3
//Serial.print("data "); Serial.println(data); // Debug
// if Test ======================================= //
LED_EIN;
if (i == 4) {
data = 4;
}
if (i == 5) {
data = 5;
}
if (i == 6) {
data = 6;
}
if (i == 7) {
data = 7; // mit 4x if, LED ein 1µs, konstant alle vier
}
// -------------------------------------------------
if (i == 8) {
data = 8;
}
if (i == 9) {
data = 9;
}
if (i == 10) {
data = 10;
}
if (i == 11) {
data = 11; // mit 8x if, LED ein 1,5µs, konstant alle acht
}
LED_AUS;
/*
// switch case Test //
// ==============================================================
LED_EIN;
switch (i) {
case 4: data = 4;
break;
case 5: data = 5;
break;
case 6: data = 6;
break;
case 7: data = 7;
break; // LED ein 0,875µs, alle vier konstant
// --------------------------------------------------
case 8: data = 8;
break;
case 9: data = 9;
break;
case 10: data = 10;
break;
case 11: data = 11;
break; // LED ein 1,0µs, alle acht konstant
default: break;
}
LED_AUS;
*/
}
} // loop Ende
Danke für deine Mühen, und die Dokumentation:
mit 8x if, LED ein 1,5µs, konstant alle acht
Ein byte per if mit einer Konstanten vergleichen dauert also insgesamt 187.5 ns, also 3 Takte.
Bei Tests mit weniger als 8 if spielen schon die zwei Takte, um die LED ein/aus zu schalten, mit rein ...
Hast du mal zum Vergleich 0 und 1 if Anweisung gemessen und verglichen?, mal mitvolatile byte i; experimentiert (wenn das zu vergleichende Byte tatsächlich erst aus dem RAM geholt wird) ?
Ja es sind "Mikrooptimierungen" die mir nichts bringen
Das bezieht sich nur auf die Ausführungszeit des Sketches
![]()
Hallo,
mit volatile kann ich heute Abend testen. Ich hatte gedacht mit der for Schleife den Compiler am optimieren zu hindern.
Ich hatte gedacht mit der for Schleife den Compiler am optimieren zu hindern.
Wenn du Optimierungen verhindern möchtest, dann solltest du das dem Compiler auch mitteilen!
![]()
#pragma GCC push_options
#pragma GCC optimize ("O0")
for(int i=0;i<11111;i++) tuwas();
#pragma GCC pop_options
Alternativ:
void __attribute__((optimize("O0"))) test()
{
for(int i=0;i<11111;i++) tuwas();
}
schon lange nicht mehr genutzt, also ungetestet
Dadurch dass du die Laufvariable i der for-Schleife global gemacht hast, ja, das sollte es dem Compiler schwerer machen.
Wenn z.B. in der Schleife eine Funktion aufgerufen würde, müsste sichergestellt sein, dass in dieser Funktion das globale i den jeweils aktuellen Wert hat.
Und umgekehrt gilt natürlich immer noch der Tip, dass Schleifenvariablen besser lokal im for definiert werden, um das Optimieren zu erleichtern.
Um genauer zu sehen was passiert, istavr-objdump -dsehr hilfreich.
Hallo,
ja genau, eigentlich wollen wir ja das der Compiler soviel wie möglich optimiert. In diesem Fall der Forschung wollen wir es unterbinden. ![]()
Mein Hauptproblem ist eher sinnvolle Tests zumachen. ![]()
Man bekommt viele Messwerte und weiß gar nicht recht wie man die sauber und verständlich dokumentieren soll. Es kommt immer auf den Einzelfall an.
Noch ein Test mit Vergleich auf wahr und volatile byte i.
>>> switch case ist schneller als if.
/*
Doc_Arduino - german Arduino Forum
IDE 1.8.13
Arduino Mega2560
28.08.2017
*/
// add this to the top of your sketch
#define NOP __asm__ __volatile__ ("nop")
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) // setzt das angegebene Bit auf 1
#endif
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) // setzt (löscht) das angegebene Bit auf 0
#endif
volatile byte i; // mit volatile
byte data = 8;
const byte pin_Messung = 44; // Messung ein/aus
#define LED_EIN sbi (PORTL,5) // Pin 44
#define LED_AUS cbi (PORTL,5) // Pin 44
void setup() {
Serial.begin(9600);
pinMode(pin_Messung, OUTPUT);
digitalWrite(pin_Messung, LOW); //
}
void loop() {
for ( i = 0; i < 8; i++ ) { // 0...3 oder 0...7
//Serial.print("data "); Serial.println(data); // Debug
/*
// if Test ======================================= //
LED_EIN;
if (i == 0) {
data = 0;
}
if (i == 1) {
data = 1;
}
if (i == 2) {
data = 2;
}
if (i == 3) {
data = 3; // mit 4x if, alle vier konstant mit 1,625µs
}
// ----------------------------------------------------------------------------------------------
if (i == 4) {
data = 4;
}
if (i == 5) {
data = 5;
}
if (i == 6) {
data = 6;
}
if (i == 7) {
data = 7; // mit 8x if, alle acht konstant mit 2,875µs
}
LED_AUS;
*/
// switch case Test //
// ==============================================================
LED_EIN;
switch (i) {
case 0: data = 0; // mit 4x case, dieser Vergleich 0,958µs, // mit 8x case, dieser Vergleich 2,208µs
break;
case 1: data = 1; // mit 4x case, dieser Vergleich 0,75µs, // mit 8x case, dieser Vergleich 2,167µs
break;
case 2: data = 2; // mit 4x case, dieser Vergleich 0,917µs, // mit 8x case, dieser Vergleich 2,208µs
break;
case 3: data = 3; // mit 4x case, dieser Vergleich 1,083µs, // mit 8x case, dieser Vergleich 2,167µs
break;
// ----------------------------------------------------------------------------------------------------
case 4: data = 4; // mit 8x case, dieser Vergleich 2,208µs
break;
case 5: data = 5; // mit 8x case, dieser Vergleich 2,167µs
break;
case 6: data = 6; // mit 8x case, dieser Vergleich 2,208µs
break;
case 7: data = 7; // mit 8x case, dieser Vergleich 2,042µs
break;
default: break; // 1µs
}
LED_AUS;
}
} // loop Ende
Hallo,
Vergleich auf volatile mit 0 und 1
Zusammenfassung überflüssig ...
/*
Doc_Arduino - german Arduino Forum
IDE 1.8.13
Arduino Mega2560
29.08.2017
*/
// add this to the top of your sketch
#define NOP __asm__ __volatile__ ("nop")
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) // setzt das angegebene Bit auf 1
#endif
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) // setzt (löscht) das angegebene Bit auf 0
#endif
volatile byte i; // mit volatile
byte data = 8;
const byte pin_Messung = 44; // Messung ein/aus
#define LED_EIN sbi (PORTL,5) // Pin 44
#define LED_AUS cbi (PORTL,5) // Pin 44
void setup() {
Serial.begin(9600);
pinMode(pin_Messung, OUTPUT);
digitalWrite(pin_Messung, LOW); //
}
void loop() {
for ( i = 0; i < 1; i++ ) { // 0 oder 1
//Serial.print("data "); Serial.println(data); // Debug
// if Test ======================================= //
LED_EIN;
if (i == 0) { // 1µs
data = 0;
}
if (i == 1) { // 1µs
data = 1;
}
LED_AUS;
/*
// switch case Test //
// ==============================================================
LED_EIN;
switch (i) {
case 0: data = 0; // 0,75µs
break;
case 1: data = 1; // 0,75µs
break;
default: break;
}
LED_AUS;
*/
}
} // loop Ende
Hallo,
und jetzt wird es spannend, weil ich nicht weiß ob ich die Optimierungsverhinderung richtig umgesetzt habe?
Das mit pragma will nicht, ist verboten, laut Compilerhinweis.
Ich sehe jetzt nur das es noch langsamer wurdeim Vergleich zum rein volatile vorher.
/*
Doc_Arduino - german Arduino Forum
IDE 1.8.13
Arduino Mega2560
29.08.2017
*/
// add this to the top of your sketch
#define NOP __asm__ __volatile__ ("nop")
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) // setzt das angegebene Bit auf 1
#endif
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) // setzt (löscht) das angegebene Bit auf 0
#endif
volatile byte i; // mit volatile
byte data = 8;
const byte pin_Messung = 44; // Messung ein/aus
#define LED_EIN sbi (PORTL,5) // Pin 44
#define LED_AUS cbi (PORTL,5) // Pin 44
void __attribute__((optimize("O0"))) test();
void setup() {
pinMode(pin_Messung, OUTPUT);
digitalWrite(pin_Messung, LOW); //
delay(10);
}
void loop() {
for ( i = 0; i < 1; i++ ) { // 0 oder 1
test();
}
} // loop Ende
void test ()
{
/*
LED_EIN;
if (i == 0) { // 1,875µs
data = 0;
}
if (i == 1) { // 1,875µs
data = 1;
}
LED_AUS;
*/
// switch case Test //
// ==============================================================
LED_EIN;
switch (i) {
case 0: data = 0; // 1,458µs
break;
case 1: data = 1; // 1,417µs
break;
default: break;
}
LED_AUS;
}
Das mit pragma will nicht, ist verboten, laut Compilerhinweis.
Danke!
Schade.
Habe das auf einem anderen System genutzt.
Mit 0 meinte ich eigentlich null if Abfragen, im Vergleich zu einer.
void loop() {
LED_EIN;
for (i = 0; i < 0; i++ ) {
if ( i == 8 ) data = 8; // kommt gar nicht dran
}
LED_AUS;
Dass dein neuer Test langsamer wird, ist klar: Du machst ja auch viel mehr: Funktion aufrufen, zurückspringen ...
Schön dass du siehst, wie viel mehr das ausmacht, im Vergleich zu einem kleinen if ![]()
Hallo,
jemand wollte noch auf !=0 getestet haben.
Ich denke mehr Tests sind nicht möglich ohne Romane für die Doku zu schreiben.
/*
Doc_Arduino - german Arduino Forum
IDE 1.8.13
Arduino Mega2560
29.08.2017
*/
// add this to the top of your sketch
#define NOP __asm__ __volatile__ ("nop")
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) // setzt das angegebene Bit auf 1
#endif
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) // setzt (löscht) das angegebene Bit auf 0
#endif
byte i;
byte data = 8;
const byte pin_Messung = 44; // Messung ein/aus
#define LED_EIN sbi (PORTL,5) // Pin 44
#define LED_AUS cbi (PORTL,5) // Pin 44
void setup() {
pinMode(pin_Messung, OUTPUT);
digitalWrite(pin_Messung, LOW); //
delay(10); // damit ich den richtigen Anfang erkenne
}
void loop() {
for ( i = 0; i < 4; i++ ) { // 0 ... 3
LED_EIN;
if (i == 0) { // 0,79µs
data = 0;
}
if (i != 0) { // 1,083µs oder 1,042µs
data = 1;
}
if (i == 3) { // 0,875µs
data = 3;
}
LED_AUS;
}
} // loop Ende
Hallo,
@ Micha, aha kleines Missverständnis.
Compiler sagt einem schon ... ![]()
warning: comparison is always false due to limited range of data type [-Wtype-limits]
ohne volatile konstant 0,4583µs
mit volatile konstant 0,5833µs
/*
Doc_Arduino - german Arduino Forum
IDE 1.8.13
Arduino Mega2560
29.08.2017
*/
// add this to the top of your sketch
#define NOP __asm__ __volatile__ ("nop")
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) // setzt das angegebene Bit auf 1
#endif
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) // setzt (löscht) das angegebene Bit auf 0
#endif
byte i;
byte data = 8;
const byte pin_Messung = 44; // Messung ein/aus
#define LED_EIN sbi (PORTL,5) // Pin 44
#define LED_AUS cbi (PORTL,5) // Pin 44
void setup() {
pinMode(pin_Messung, OUTPUT);
digitalWrite(pin_Messung, LOW); //
delay(10); // damit ich den richtigen Anfang erkenne
}
void loop() {
LED_EIN;
for (i = 0; i < 0; i++ ) {
if ( i == 8 ) data = 8; // kommt gar nicht dran
}
LED_AUS;
} // loop Ende
Hallo,
man kann vielleicht als Resümee schreiben, dass der Compiler schon ganz schön optimieren kann, egal was man wie schreibt. Die Unterschiede zwischen if und switch kann man sich merken, falls es wirklich einmal darauf ankommen sollte. Muss aber im Einzelfall ausgetestet werden. Ansonsten bin ich nun auch zur Einsicht gekommen das es für meine Anwendung keinen Unterschied macht.
Danke für die vielen Tipps und Hinweise @ all.