Datatyp für lokale Variable für max. Performance

Hallo,

ich habe mal irgendwo aufgeschnappt, dass es aus Performance-Sicht ideal sein soll,
wenn lokale Variablen folgende Implementierung haben:

Auf 8bit mC --> byte
Auf 16bit mC --> int

Beispiel:
Ein Schleifenzähler i auf einem 16bit System als int anlegen,
obwohl bekannt ist, dass i nie höher zählen wird als 10
(und byte eigentlich ausreichend wäre).

Kann das jemand bestätigen?

Danke.

Hab ich da was verpaßt? Gibt es einen 16 Bit Arduino?
Falls nein, doglike wende dich bitte an ein C-Forum.

Grüße Uwe

Hallo,

davon einmal abgesehen. Aus Performance- und Speichergründen sollte man auf unseren kleinen µC immer den passenden Datentyp verwenden. Dann ergibt sich der Rest von alleine. So wie für Pinnummern alles was größer Byte Unsinn ist, so ist Byte für millis() Werte unpassend.

Ich experimentiere mit dem ESP32 mit Arduino IDE. Das ist ein 32bit mC.
Aber ich merke schon, das, was ich da aufgeschnappt habe, scheint sich nicht wirklich zu bestätigen.

Eine 32 bit CPU kann bei geeigneten Maschienenprogrammbefehlen eine 32 Bit Zahl schneller verarbeiten als ein 8 Bit CPU, die das in 4 Schritten machen muß.

Ob das bei den meisten Sketchen eine reelle Notwendigkeit ist, das letzte Quentchen an Preformace herauszukitzeln sei dahingestellt.
Bei auf Geschwindigkeit bzw preziesen Zeiten optimierten Programmen ist eine Programmierung in Assembler bzw mittels Hardwarekomponenten wie Timern zurückzugreifen, oder wenigstens nicht die allgemeinen Compiler setups der IDE zu verwenden sondern einen geeignete Compilereinstellung zu wählen. Seht Gute C-Kenntnisse vorausgesetzt.

Auf einer 32 Bit CPU nur 32 Bit Variablen zu verwenden weil 32 Bit Verarbeitet werden können finde ich nicht zielführend. Da unter Umständen eine Variable aus dem RAM geholt werden muß (und nicht in einem Register vorhanden ist) ist zB die RAM-Anbindung (breite Adress bzw Datenbus) auch nocht auf die Verarbeitungsgeschwindigkeit Ausschlaggebend.

Generell, ohne die genaue Hardware bzw Compilereinstellungen zu kennen ist meiner Meinung Die Zitierte Aussagen nicht zutreffend.

Grüße Uwe

Danke für die detaillierte Antwort, Uwe!

Das Thema ist einerseits total interessant, andererseits auch total langweilig. Von daher ist das, was ich jetzt sage, in Teilen Widersprüchlich!
Aber dennoch wahr.

Wirklich erfassen kann man es nur, wen man wechselnde Standpunkte/Sichtweisen durchspielt. Und den generierten Code im Detail untersucht.

Grundsätzlich und allgemeingültig, ist int der natürliche Datentype des C/C++ Compilers. Nicht unbedingt der zugrundeliegenden Maschine, z.B. 8Bit AVR, aber des Compilers. Alle eingebauten Funktionen, Operatoren, mitgelieferten Libs, z.B. die Libc, sind auf int Verarbeitung optimiert. Es ist also anzuraten, überall int zu verwenden. Wo es nur geht, und halbwegs Sinn macht. Trotz der unterschiedlichen Breite von int. So erzeugt mal portable Software welche (fast) überall am weitesten optimiert wird.
Ja, auch der eingebaute Optimizer arbeitet am besten mit int. Einige der Optimierungsstrategien greifen nur bei int.

Das folgende ohne Gewähr, weil zusätzlich von anderen Faktoren abhängig:
Ein Beispiel ist die for(int i = 0; i<=42; i++) tuwas(); Schleife. Schaut man in den generierten Assemblercode, wird man erkennen, dass trotz int Vorgabe auf AVR Byte genutzt wird. Nutzt man stattdessen unsigned int, ist Ende mit dieser automatischen Optimierung.

Manche Prozessoren tun sich sehr schwer Bytes zu verarbeiten, weil sie halt 32 Bit breit organisiert sind. Ein for(uint8_t i = 0; i<=42; i++) tuwas(); tut denen richtig weh. Ein weitgehender Optimizer wird den Code auf int umbauen

Am Rande:
Die Standard Optimierungsrichtung ist in der Arduinowelt "Size".
Wenn man jetzt händisch auf "Speed" optimiert, arbeitet man gegen den Compiler/Optimizer.
Das kann zu hässlichen bis unwirksamen Konstruktionen führen. Oft kontraproduktiv.

Regeln/Erfahrungen:

  1. Händische Optimierungen setzen oft am falschen Punkt an
  2. Händische Mikro Optimierungen machen den Code hässlich bzw. schlecht lesbar und schlecht portabel
  3. Erst die Stelle finden, wo die Zeit liegen bleibt, dann optimieren

Die Sicht einer anderen Person, welche mir auch gefällt:

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,

solche Sprüche wie im Zitat sind einfach unsinnig. Demnach dürften sich selbst Experten nie über Codeoptimierung unterhalten. Egal ob in der Arbeitsgruppe oder in einem Forum.

Allgemein wird man sich meistens zwischen Speicher- und Geschwindigkeitsoptimierung entscheiden müssen.

solche Sprüche wie im Zitat sind einfach unsinnig.

Ich befürchte, dass du das Thema noch nicht in der Tiefe ausgeleuchtet hast...
Ich vermute, ein vorschnelles Urteil. Ein Vorurteil.

Vergleichbares Mantra:

Um die Rekursion verstehen zu können, muss man zuerst die Rekursion verstehen.

doglike:
... aus Performance-Sicht ideal sein soll,

... in Assembler zu programmieren. Sagt jemand, den ich kenne, der ATmega328 genau so programmiert.

Das ist keine direkte Antwort auf die Frage, aber wohl eine Stelle, wo man richtig Geschwindigkeit generieren kann.

combie:
Die Sicht einer anderen Person, ...

... die mir überhaupt nicht gefällt, da die Stärke unserer Spezies die komplexe Kommunikation auch zum Zwecke des Lernens ist.

Eine andere Stärke sollte die Meinungsvielfalt sein :smiley:

Demnach dürften sich selbst Experten nie über Codeoptimierung unterhalten. Egal ob in der Arbeitsgruppe oder in einem Forum.

... die mir überhaupt nicht gefällt, da die Stärke unserer Spezies die komplexe Kommunikation auch zum Zwecke des Lernens ist.

Es ist wirklich interessant, was ihr beiden da rein interpretiert!

In dem Spruch befindet sich keinerlei Ansatz, eine Diskussion zu unterbinden.
Noch nicht mal versteckt.
Das passiert nur in euren Köpfen.

Jetzt bin ich mal gemein:
Dass euch der Spruch so gut schmeckt, liegt daran, dass er sehr gut trifft.
Ihr seid davon "betroffen".
Also kann ja nur der Spruch schlecht sein.
Denn eure Wahrnehmung ist ja völlig in Ordnung.

Jetzt mal im Ernst...
Wenn einen die Eingangsfrage wirklich interessiert, dann fehlt da ganz was wesentliches.
Es fehlt der Kontext. Die konkrete Anwendung.
Und wenn man die hat, schaut man einfach in den generierten Code und die Frage erübrigt sich.
Fertig!

Unser Doc_Arduino spricht von "nicht mehr unterhalten dürfen". Das ist aus meiner Sicht schief gewickelt. Denn Experten "dürfen" sich sehr wohl darüber unterhalten. Sie müssen es aber nicht. Experten gewinnen keinerlei Erkenntnis, dadurch, dass sie sich gegenseitig althergebrachtes vorbrabbeln. Experten wissen ihre Ressourcen meist besser einzusetzen. Z.B. sich gegenseitig die neuesten Erkenntnisse zuzurufen. Oder Schüler auszubilden.

agmue, bei dir höre ich, dass der Spruch irgendwie das lernen unterbinden soll. Wie kommst du da drauf?
Das Gegenteil ist der Fall. Es ist viel eher eine Aufforderung sich kundig zu machen.

ich habe mal irgendwo aufgeschnappt

Das sind ganz schlechte Vorraussetzungen.
Denn viele Ansagen, welche man irgendwie aufschnappt sind so dermaßen falsch, dass noch nicht mal das Gegenteil richtig ist.
Hier ist der Kern wenigstens halb richtig. Denn natürlich ist ein Prozessor fixer, wenn er in seiner natürlichen Breite arbeiten darf.

Hätte unser doglike die aufgeschnappte Ansage überprüft, dann hätte die Chance bestanden, dass sein Compiler dank -Os ein int i als Byte behandelt. Natürlich getestet auf einem 8 Bitter. In der Situation ist es doch völlig egal, ob als byte oder int definiert.

Hätte er mit -O3 getestet, oh Wunder, dann hätte die Chance bestanden, wenn die Randbedingungen stimmen, dass der Compiler die Schleife komplett, durch ausrollen, entfernt. Das int i findet sich dann an keiner Stelle im generierten Code wieder.
Stichwort: "gcc unroll_loops" und dann findet man auch dessen Brüder.

Und jetzt die Frage:
Was wird schneller auf einem 8 Bit Prozessor verarbeitet, ein nicht vorhandenes Byte, oder ein nicht vorhandener int?
Diese Frage zeigt wie irrwitzig es ist, auf die Eingangsfrage eine korrekte Ja/Nein Antwort zu erwarten. Ohne umfassende Bekanntgabe des Kontextes.

Hi

Trotzdem hat 'die Sicht einer anderen Person' grundlegend Recht.
Wer solche Fragen HIER stellt (und nicht etwa im Kollegenkreis - also wo die Leute rumspringen, Die auf gleichem Niveau 'denken'), wird absolut keine Ahnung haben, über Was Er Da spricht.
Auch wird Er keine wirkliche Optimierung ausführen KÖNNEN - klar, etwas C&P hier, ein BYTE statt INT DA, und schon sind zwei Takte eingespart.
DANN darf ich aber nicht die Funktionsaufrufe vom Arduino benutzen, Die 'alle möglichen Wege abdecken' - DAS geht nämlich nicht mit Einsparung von Code und Steigerung der Geschwindigkeit, sondern mit Umfang an Code und daraus resultierender 'Langsamkeit'.

Wer Sich erst über solche Dinge informieren muß, ist Laie und kann damit nur 'per Zufall ein Korn finden' - aber auch klar: Je mehr der Laie an Wissen anhäuft, desto besser/ausführlicher ist das Wissen und der Nutzen Dieses.

Ebenfalls klar: Jeder hat in Seinem Gebiet als Laie angefangen und konnte nur durch Nachfragen zu Was werden - aber um hier die Einzeltakte im µC einzusparen sollte man wohl wirklich weniger die Arduino-IDE benutzen, sondern direkt mit der Hardware schwätzen (ggf. den erzeugten Assembler-Code analysieren/optimieren - wobei DAS wohl echte Königsklasse wäre - Das dürfte noch 'einige Meilen weiter' sein, als sich fremde Sketche anzueignen).

MfG

Hallo,

ich sage mal so. Jeder nimmt Zitate oder sonstigen Schriftkram anders auf wie er vielleicht gedacht ist. Das passiert aus den verschiedensten Gründen. Ich belasse es dabei. :wink:

combie:
agmue, bei dir höre ich, dass der Spruch irgendwie das lernen unterbinden soll. Wie kommst du da drauf?
Das Gegenteil ist der Fall. Es ist viel eher eine Aufforderung sich kundig zu machen.

Auch wenn ich es nicht so verstehe, so fände ich es durchaus gut, wenn die Aussage so gemeint wäre.

Hallo,

zurück zum eigentlichen Thema. Ich überprüfe manchmal Aussagen soweit mir das möglich ist. Diesmal komme ich allerdings zu einem anderen Ergebnis. Ich nutze die IDE 1.8.7.
Ich habe einen einfachen Code mit einer for Schleife und den Schleifenzähler mit allen Datentypen kompiliert und wieder disassembliert. Mit uint8, uint16, int16, uint32 und int32 kommt für mich gleicher kurzer Assemblercode raus.
Manchmal bleibt allerdings der C Code mit drin. Manchmal nicht.
Vielleicht war es ein unpassendes Beispiel dafür. Keine Ahnung. Warum aber C Code im Assemblercode stehen bleibt würde mich interessieren. Ich ziehe die .elf Datei auf eine Batchdatei die dann objdump.exe aufruft und das Ergebnis als Textdatei speichert.

74HC595_compilertest.ino (1.97 KB)

74HC595_compilertest_uint8_.elf.txt (2.66 KB)

74HC595_compilertest_uint16_.elf.txt (2.7 KB)

74HC595_compilertest_int16_.elf.txt (4.12 KB)

74HC595_compilertest_uint32_.elf.txt (2.77 KB)

74HC595_compilertest_uint32_.elf.txt (2.77 KB)

Keine Ahnung. Warum aber C Code im Assemblercode stehen bleibt würde mich interessieren.

Wenn objdump.exe auch die Quellen sieht, kann es das.
Wurde zwischendurch die Quelle editiert (Modifiziert Datum Urzeit), dann ist Ende damit.

Diesmal komme ich allerdings zu einem anderen Ergebnis. ... Mit uint8, uint16, int16, uint32 und int32 kommt für mich gleicher kurzer Assemblercode raus.

Das ist das (für mich) erwartete Ergebnis!
Sämtliche Versuche, an der Stelle, irgendwas händisch zu optimieren, sind für die Katz.

Hier ein Extrembeispiel wie weit die Optimierung geht:

/**
 * Das Programm toggelt, 6 mal den Pin 13 und bleibt 
 * dann in einer Endlosschleife stehen
 */


void tuwas()
{
  PINB = _BV(PB5); 
}


int main() 
{
  DDRB |= _BV(PB5);
  for(int i = 0; i <= 5; i++)
  {
    tuwas();
  }
  while(1);
}

Generierter Code (ein Auszug, nur die Main):

00000080 <main>:
  80: 25 9a       sbi 0x04, 5 ; 4   // DDRB |= _BV(PB5);
  82: 80 e2       ldi r24, 0x20 ; 32  // stopfe  _BV(PB5) ins Register
  84: 83 b9       out 0x03, r24 ; 3  // toggle
  86: 83 b9       out 0x03, r24 ; 3  // toggle
  88: 83 b9       out 0x03, r24 ; 3  // toggle
  8a: 83 b9       out 0x03, r24 ; 3  // toggle
  8c: 83 b9       out 0x03, r24 ; 3  // toggle
  8e: 83 b9       out 0x03, r24 ; 3  // toggle
  90: ff cf       rjmp .-2       ; 0x90 <main+0x10> // while(1)

( die // Kommentare habe ich rein gesetzt)
Man sieht:
Die Schleifenvariable i ist völlig verschwunden.
tuwas() wurde 6 mal inline eingebunden. Kein Funktionsaufruf, kein push, pop, call und Return

Kompakter bekommt das kein Assemblerprogrammierer hin.
Da ist nichts mehr zu optimieren.
Es ist völlig egal, welchen Datentype man für die Laufvariable nimmt, da verschwunden.


Damit ist auch klar, dass die Eingangsfrage, im Kern, kaum berechtigt ist.
Das kann in jedem Kontext anders aussehen.
Man muss sich das schon oft angeschaut haben, was da passiert, um zu sehen, wie sich das auswirkt, was man mit/in der Quelle anrichtet.
Viel schauen, üben usw., dann entwickelt man auf Dauer eine Intuition für die Vorgänge.

Und es wird einem auch klarer, dass die Wahl des Datentypes, nicht unbedingt, eine Auswirkung auf die Performance hat. Oft ja, aber auch oft nein.


Ich ziehe die .elf Datei auf eine Batchdatei die dann objdump.exe aufruft und das Ergebnis als Textdatei speichert.

Ich habe meine Arduino IDE 1.8.7 überredet, mir bei jedem Kompilerlauf, einen solchen Assemblerdump zu produzieren. Voll automatisch.


Nachtrag:
Wenn man

#pragma GCC optimize ("O0")

an den Anfang der *.ino setzt, kann man schön sehen wie nicht optimierter Code aussieht.

Danke für die Klarstellungen, combie. Ich stimme dir völlig zu.

Nur eine kleine Ergänzung:

Wurde zwischendurch die Quelle editiert (Modifiziert Datum Urzeit)

"Quelle" sollte sich auf die von der IDE generierte .cpp im temp Unterverzeichnis beziehen, richtig?
Die wird mit jedem Compilieren neu erzeugt, auch wenn die .ino selbst nicht geändert wurde.

Natürlich schadet es nicht, von Anfang an den richtigen Datentyp zu verwenden. Aber es gilt auch hier die
Grundregel: Keine Optimierung, jedenfalls jetzt noch nicht.

"Quelle" sollte sich auf die von der IDE generierte .cpp im temp Unterverzeichnis beziehen, richtig?
Die wird mit jedem Compilieren neu erzeugt, auch wenn die .ino selbst nicht geändert wurde.

Voll korrekt!

Hallo,

sollte nicht unsigned int von der Optimierung ausgeschlossen sein? Das war es was ich nachschauen wollte.
Anderereits ist es schön das der Compiler so schlau ist und in dem Fall alles optimieren kann.

Zum Rest - Danke für die Info.

sollte nicht unsigned int von der Optimierung ausgeschlossen sein? Das war es was ich nachschauen wollte.

So ist das mit den Erfahrungen...
Es gab einen Fall, wo das einen Unterschied machte.
Kann ich aber leider jetzt nicht reproduzieren.

So ist das...

Merksatz: (an mich)
Fakten, ohne Kontext, sind irrelevant.