mikrocontroller.net

Forum: Compiler & IDEs AVR GCC Optimizer Level in C-Programm abfragen


Autor: RichardHi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

ich suche nun schon länger nach einer Lösung für folgende Aufgabe:

Ich möchte in einem C-Programm (für den ATMega2560) ermitteln, mit 
welchen Optimizer-Einstellungen der Code compiliert wurde. Dabei genügt 
es mit, -O0 bis -O3 zu erkennen. Wer hat das mal gemacht? Vielen Dank.

Ich compiliere mit avr-gcc im AVR Studio.

Grüße an alle Leser,
RichardHi

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es gibt dafür Macros:
https://gcc.gnu.org/onlinedocs/cpp/Common-Predefin...
_OPTIMIZE_
_OPTIMIZE_SIZE_
_NO_INLINE_

Warum brauchst du das? Korrekter Code funktioniert immer unabhängig von 
der Optimierung. Wenn das bei deinem Code nicht der Fall ist, solltest 
du ihn reparieren.

Autor: RichardHi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ja, das hatte ich auch schon gefunden. Aber den Level kann ich damit 
nicht abfragen - oder: Ich wüsste nicht, wie.

Am Liebsten hätte ich gerne die Nummer des Levels zurück, ob Macro oder 
Funktion ist egal.

Wofür brauche ich das?
Ich möchte sicherstellen, dass ich den richtigen Optimizer-Level 
benutze, weil ich zum debugen und testen oft daran ändere wegen 
zeitkritischer Routinen, und in der Final-Version möchte ich sicher 
sein, dass ich mit -O2 kompiliert habe.

Danke für weitere Ideen,
RichardHi

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
RichardHi schrieb:
> Ich möchte sicherstellen, dass ich den richtigen Optimizer-Level
> benutze,

Klingt nach einem ziemlichen Hack, im Source-Code zu prüfen ob im 
Build-System der "richtige" Level ausgewählt wurde! Und wie prüfst du im 
Code ob die Optimierungen jetzt wirklich an sein sollen oder nicht?

Normalerweise macht man das so, dass man im Build System oder in der IDE 
zwei (oder mehr) Konfigurationen, z.B. namens "Debug" und "Release" 
anlegt, welche eben zum Debuggen bzw. auf Effizienz optimieren. Da kann 
man mit einem Klick so kompilieren wie man es braucht, und muss nicht 
von Hand den Optimierungs-Level umstellen.

Autor: RichardHi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Klingt nach einem ziemlichen Hack, im Source-Code zu prüfen ob im
> Build-System der "richtige" Level ausgewählt wurde!

Naja, ganz so abwegig scheint das erstmal nicht zu sein. Man kann ja 
auch Anderes aus dem Build-System in das Programm bekommen, z.B. die 
Compiler-Version. Warum nicht auch den Optimizer-Level - das ist Idee.

Hat noch jemand einen Vorschlag ?

Autor: Markus F. (mfro)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
RichardHi schrieb:
> Naja, ganz so abwegig scheint das erstmal nicht zu sein. Man kann ja
> auch Anderes aus dem Build-System in das Programm bekommen, z.B. die
> Compiler-Version. Warum nicht auch den Optimizer-Level - das ist Idee.
>
> Hat noch jemand einen Vorschlag ?

Mir erscheint das schon ein wenig abwegig. Wenn man eine vernünftige 
Build-Umgebung hat, die einmal erzeugte Binaries reproduzierbar wieder 
erzeugen kann (VCS), dann braucht man so was m.E. nicht. Im Zweifel 
erzeugt man das Programm eben nochmal und macht einen Vergleich.

Wenn's denn unbedingt sein muss, bau' dein Makefile eben entsprechend 
aus:
.PHONY: flags
flags:
    echo "const char used_flags[] = \"$(CFLAGS)\";" > flags.c

Das Ergebnis baust Du irgendwo in dein Compilat ein und machst dir noch 
eine Abfragefunktion dazu.

Autor: Rolf Magnus (rmagnus)
Datum:

Bewertung
2 lesenswert
nicht lesenswert
Über die Compiler-Kommandozeile selber ein Makro definieren, in dem das 
Optimierungslevel drinsteht.
Grob etwas in der Art:
OPTIMIZATION=3

CFLAGS=-O$(OPTIMIZATION) -DOPTIMIZATON_LEVEL=\\"$(OPTIMIZATION)\\" # ...

Das Escaping des Anführungszeichens ist möglicherweise nicht korrekt.

Autor: Oliver S. (oliverso)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
RichardHi schrieb:
>
und in der Final-Version möchte ich sicher
> sein, dass ich mit -O2 kompiliert habe.

Nur mal rein interessehalber: warum -O2 und nicht -Os ?

Oliver

Autor: RichardHi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oliver S. schrieb:
> warum -O2 und nicht -Os ?

-Os ist Optimierung nach SIZE und lässt laut Doku die Optimierungen weg, 
die zur Vergößerung des Binär-Codes führen würden. Das brauche ich nicht 
auf dem Mega. Dann lieber schnelleren Code.

Autor: RichardHi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Markus F. schrieb:
> Wenn's denn unbedingt sein muss, bau' dein Makefile eben entsprechend
> aus:
> .PHONY: flags
> flags:
>     echo "const char used_flags[] = \"$(CFLAGS)\";" > flags.c
>
> Das Ergebnis baust Du irgendwo in dein Compilat ein und machst dir noch
> eine Abfragefunktion dazu.

Ja gute Idee, das scheint prinzipiell ein Weg zu sein.

Aber ich habe kein eigenes Make-File, sondern das generiert AVR Studio 
für mich. Kann ich das irgendwie ergänzen, so ähnlich wie ein 
Include-File?
Ich benutze allerdings noch 4.18 (never change a running system) und bin 
zufrieden damit.

Autor: Walter Tarpan (nicolas)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Markus F. schrieb:
> Mir erscheint das schon ein wenig abwegig. Wenn man eine vernünftige
> Build-Umgebung hat, die einmal erzeugte Binaries reproduzierbar wieder
> erzeugen kann (VCS),

Nur als Zwischenfrage: Welche Build-Umgebung kann aus einem bestimmten 
Quelltext-Stand wieder das gleiche Binary erzeugen? Bei einem Artikel 
über den Review von TrueCrypt wurde erwähnt, daß ein nicht unerheblicher 
Aufwand schon allein darin entstand, daß moderne Build-Umgebungen mit 
mehreren parallelen Threads aus dem gleichen Quellmaterial selten das 
gleiche Binary erzeugen, selbst wenn auf Konstrukte wie __DATE 
verzichtet wird, da hier Laufzeiteffekte zum tragen kommen.

Autor: Walter Tarpan (nicolas)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
RichardHi schrieb:
> Kann ich das irgendwie ergänzen, so ähnlich wie ein
> Include-File?

Du kannst in Atmel-Studio Pre-Build und Post-Build Scripte definieren. 
Ich nutze das gerne, um die Revisionsnummer aus dem 
Versionsverwaltungssystem in ein *.c-File einzubauen.

Autor: Oliver S. (oliverso)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
RichardHi schrieb:
> Oliver S. schrieb:
>> warum -O2 und nicht -Os ?
>
> -Os ist Optimierung nach SIZE und lässt laut Doku die Optimierungen weg,
> die zur Vergößerung des Binär-Codes führen würden. Das brauche ich nicht
> auf dem Mega. Dann lieber schnelleren Code.

Nun ja, und du hast nachgemessen, ob O2 tatsächlich schneller ist als 
Os?

Denn ich bezweifele das ganz stark. gcc ist ein toller Compiler, der ist 
aber primär nicht für AVRs gemacht. Dessen Optimierungen sind vor allem 
für die Hauptplattformen optimiert.

Und selbst auf Intels oder Arms gilt immer noch : messen, messen, 
messen, nicht glauben.

Oliver

Autor: RichardHi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oliver S. schrieb:
> Nun ja, und du hast nachgemessen, ob O2 tatsächlich schneller ist als
> Os?
>
> Denn ich bezweifele das ganz stark. gcc ist ein toller Compiler, der ist
> aber primär nicht für AVRs gemacht. Dessen Optimierungen sind vor allem
> für die Hauptplattformen optimiert.
>
> Und selbst auf Intels oder Arms gilt immer noch : messen, messen,
> messen, nicht glauben.

Ich habe zeitkritischen Programmcode, bei dem die 
Optimierungseinstellung weiterhilft oder eben nicht. Deshalb muss ich 
auch immer mal "an diesen Schrauben drehen".

Insbesondere habe ich einen Codeabschnitt gehabt, den der Optimizer 
kaputt-optimiert hat. Den musste ich einklammern in
  #pragma GCC optimize ("-O1")
  <Codeabschnitt>
  #pragma GCC reset_options

Autor: RichardHi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Korrekter Code funktioniert immer unabhängig von
> der Optimierung.

Denkste. Wäre schön, ist aber wohl nicht generell so:

Insbesondere habe ich einen Codeabschnitt gehabt, den der Optimizer 
kaputt-optimiert hat. Den musste ich einklammern in
  #pragma GCC optimize ("-O1")
  <Codeabschnitt>
  #pragma GCC reset_options

Autor: RichardHi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Walter T. schrieb:
> Du kannst in Atmel-Studio Pre-Build und Post-Build Scripte definieren.

Hab ich leider nicht gefunden in der IDE. Gib mir bitte mal nen Tipp.

Autor: RichardHi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oliver S. schrieb:
> Und selbst auf Intels oder Arms gilt immer noch : messen, messen,
> messen, nicht glauben.

Genau. Deshalb habe ich häufig einen Frequenzzähler an einem Pin, über 
den ich Loops ausmessen kann, wenn ich in der Loop das Pin toggle.

Meinst du sowas?

Autor: Walter Tarpan (nicolas)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
RichardHi schrieb:
> Hab ich leider nicht gefunden in der IDE. Gib mir bitte mal nen Tipp.

Tut mir leid, aus Atmel Studio 4 bin ich jetzt seit 2012 heraus. 
Irgendwo bei den Build-Optionen.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
RichardHi schrieb:
> Insbesondere habe ich einen Codeabschnitt gehabt, den der Optimizer
> kaputt-optimiert hat.

Dann war er falsch geschrieben. ;-)

Wenn du zyklengenauen Code brauchst, musst du (Inline-)Assembler
benutzen.  In allen anderen Fällen sollte es bestenfalls „zu schlecht
optimiert“ (=> zu groß oder zu langsam für die Aufgabe) geben, aber
nicht „zu gut optimiert“.

Auf dem AVR ist kürzerer Code in der überwiegenden Anzahl der Fälle
auch schnellerer Code¹, weshalb man mit -Os meist gar nicht so
schlecht fährt.

¹Ausnahme: die aggressiven (und platzintensiven) Optimierungen von -O3
wie das Aufrollen von Schleifen bewirken tatsächlich nochmal eine
Steigerung der Geschwindigkeit, allerdings für einen vergleichsweise
hohen Preis.

Autor: Oliver S. (oliverso)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
RichardHi schrieb:
> Genau. Deshalb habe ich häufig einen Frequenzzähler an einem Pin, über
> den ich Loops ausmessen kann, wenn ich in der Loop das Pin toggle.
>
> Meinst du sowas?

Das, oder ähnliches. Wenn dein Code mit O2 nachgewiesenermaßen schneller 
läuft als mit Os, dann ist das so.

Oliver

Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Walter T. schrieb:
> Markus F. schrieb:
>> Mir erscheint das schon ein wenig abwegig. Wenn man eine vernünftige
>> Build-Umgebung hat, die einmal erzeugte Binaries reproduzierbar wieder
>> erzeugen kann (VCS),
>
> Nur als Zwischenfrage: Welche Build-Umgebung kann aus einem bestimmten
> Quelltext-Stand wieder das gleiche Binary erzeugen? Bei einem Artikel
> über den Review von TrueCrypt wurde erwähnt, daß ein nicht unerheblicher
> Aufwand schon allein darin entstand, daß moderne Build-Umgebungen mit
> mehreren parallelen Threads aus dem gleichen Quellmaterial selten das
> gleiche Binary erzeugen, selbst wenn auf Konstrukte wie __DATE
> verzichtet wird, da hier Laufzeiteffekte zum tragen kommen.

Antwort: jede, die sorgfältig genug zusammengebaut wurde. Hier: 
https://reproducible-builds.org wird das Thema erschöpfend behandelt.

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
RichardHi schrieb:
> Denkste. Wäre schön, ist aber wohl nicht generell so:
Ist so, bis auf sehr seltene Compiler Fehler.
> Insbesondere habe ich einen Codeabschnitt gehabt, den der Optimizer
> kaputt-optimiert hat.
Die Wahrscheinlichkeit, dass dein Code falsch war, ist wesentlich höher. 
Es gibt in C und C++ viele Möglichkeiten, solche Fehler zu machen, von 
denen viele Programmierer nicht wissen. Es gibt hier öfters Fragen nach 
solchen Problemen...

Kann man in deinem alten Atmel Studio denn keine 2 Build Configurations 
anlegen (das neue kann's)? Da macht man einfach 2 für Debug/Release, und 
hat das Problem mit einem Mausklick erledigt und muss nie wieder an den 
Compiler Optionen rumfummeln. So ist das üblich, keiner außer dir 
braucht dafür Makros für den Optimizer Level.

Autor: RichardHi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Die Wahrscheinlichkeit, dass dein Code falsch war, ist wesentlich höher.
> Es gibt in C und C++ viele Möglichkeiten, solche Fehler zu machen, von
> denen viele Programmierer nicht wissen.

Hallo zusammen,

es geht hier im Grunde gar nicht um die Frage, ob irgendein Code falsch 
oder richtig war. Das ist ein weites Feld, das wir hier sicher nicht 
erschöpfend behandeln können. Darüber gibt es viele Abhandlungen, auch 
im Rahmen der theoretischen Informatik z.B. über Berechenbarkeit und 
Entscheidbarkeit.
Alan Turing lässt grüßen.

Für die Interessierten, was der Compiler/Optimizer wegoptimiert hat:
  Ich hatte zweimal kurz hintereinander den ADC abgefragt und die Werte
  in zwei Variable gestellt. Eine ADC-Abfrage wurde nach dem Optimieren
  mit -O2 oder -O3 nicht ausgeführt. Auch volatile half da nicht.

Jetzt aber zurück zum eigentlichen Thread:

Wer kann mir zeigen, wie ich in AVR Studio 4.18 das automatisch 
generierte make-File um einen Schritt ergänzen kann, in dem ich den 
Optimizer-Level in ein Includefile schreibe, was ich im C-Programm 
mitkompiliere? Das muss vor dem Kompilieren passieren.

Autor: Oliver S. (oliverso)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Kann man in deinem alten Atmel Studio denn keine 2 Build Configurations
> anlegen (das neue kann's)?

Man kann. Und da die Build-Zeiten naturgemäß überschaubar sind, ist ein 
kompletter rebuild ja auch kein Beinbruch.

Oliver

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oliver S. schrieb:
> Und da die Build-Zeiten naturgemäß überschaubar sind, ist ein
> kompletter rebuild ja auch kein Beinbruch.
Kommt drauf an, wie viel C++ template Wahnsinn man verwendet ;-)

Um die Optimierung zu ändern, muss man ja aber sowieso alles neu 
kompilieren.

RichardHi schrieb:
> Eine ADC-Abfrage wurde nach dem Optimieren
>   mit -O2 oder -O3 nicht ausgeführt. Auch volatile half da nicht.
Klingt seltsam, bei allen Programmen bisher funktioniert das. Sicher, 
dass das Programm nicht einfach nur zu schnell war und die Abfragen zu 
schnell hintereinander ausgeführt wurden oder so?

RichardHi schrieb:
> Darüber gibt es viele Abhandlungen, auch
> im Rahmen der theoretischen Informatik
Na, mit Halteproblem & Co hat das weniger zu tun, mehr mit dem 
C(++)-Standard und den magischen Worten "undefined behaviour"...

RichardHi schrieb:
> Jetzt aber zurück zum eigentlichen Thread:
Die wichtigste Frage ist noch nicht geklärt: Angenommen du hast im Code 
den tatsächlichen Optimizer Level abgefragt, woher weiß der Code dann 
welcher Level korrekt ist? Du müsstest im Code dann ja noch zusätzlich 
eintragen, wie du optimieren möchtest. Das ist komplizierter und 
fehleranfälliger als die schlichte Nutzung von 2 Build Configurations.

Autor: Walter Tarpan (nicolas)
Datum:
Angehängte Dateien:

Bewertung
1 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> woher weiß der Code dann welcher Level korrekt ist?

Vielleicht ist es keine Information, die der Quelltext auswertet, 
sondern der Nutzer?

Ich mache bei meinen Projektchen, sobald ein LCD dran ist, auch immer 
eine kleine Info-Darstellungsfunktion, wo Versionsstand, 
Compilerversion, usw. dargestellt werden. Das hilft, wenn man die Geräte 
nach langer Zeit wieder in die Hand bekommt, durchaus weiter.

: Bearbeitet durch User
Autor: Dr. Sommer (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Walter T. schrieb:
> Vielleicht ist es keine Information, die der Quelltext auswertet,
> sondern der Nutzer?
Der Workflow wäre also:
1.) In den Build-Einstellungen die Optimierung auswählen
2.) flashen
3.) Im LCD schauen ob der richtige Optimierungslevel ausgewählt wurde

Anstatt:
1.) Auf "Release" klicken (Siehe Anhang, Screenshot aus VS, auf dem neue 
Atmel Studio Versionen basieren)

Klingt total praktisch...

Autor: Walter Tarpan (nicolas)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Der Workflow wäre also:
> 1.) In den Build-Einstellungen die Optimierung auswählen
> 2.) flashen
> 3.) Im LCD schauen ob der richtige Optimierungslevel ausgewählt wurde

Nein, der Workflow wäre:
 1.) In den Build-Einstellungen die Optimierung auswählen
 2.) Flashen
 3.) Erste Geräte an diejenigen verteilen, die es auch nutzen
 4.) Firmware weiterentwickeln
  ...
 5<m<n-2.) Vielleicht für einen speziellen Zweck eine Firmware-Variante 
stricken, die sich von den anderen unterscheidet.
 n-2.) Zu einem Gerät kommt eine Frage vom Benutzer
 n-1.) Benutzer macht Foto von Infoscreen
 n.) Ich weiß wieder genau, welches Gerät mit welcher Firmwarerevision 
überhaupt gemeint ist.

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Walter T. schrieb:
> Ich weiß wieder genau, welches Gerät mit welcher Firmwarerevision
> überhaupt gemeint ist.
Um so ein Problem ging es in diesem  Thread aber überhaupt nicht:

RichardHi schrieb:
> Wofür brauche ich das?
> Ich möchte sicherstellen, dass ich den richtigen Optimizer-Level
> benutze, weil ich zum debugen und testen oft daran ändere wegen
> zeitkritischer Routinen, und in der Final-Version möchte ich sicher
> sein, dass ich mit -O2 kompiliert habe.

Autor: Walter Tarpan (nicolas)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Um so ein Problem ging es in diesem  Thread aber überhaupt nicht

Ich habe keine Ahnung, was für den TO eine "finale Version" ist. Für 
mich ist eine "finale Version" etwas, (notwendige aber leider manchmal 
nicht hinreichende Bedingung), was meinen Schreibtisch vor langer Zeit 
verlassen hat.

Nachtrag: Ausnahme sind natürlich Gadgets für meinen Schreibtisch.

: Bearbeitet durch User
Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
RichardHi schrieb:
> Hat noch jemand einen Vorschlag ?

Generier doch einfach immer beides, dauert bei heitigen PCs vielleicht 
maximal 1 Sekunde länger.

Das eine nennst du porjekt-debug.elf und das andere projekt-release.elf. 
Ersteres verwendest du zum Debuggen, zweiteres zur Release.  Oder gleich 
alle generierten Dateien in eigene Ordner:

debug/projekt.elf
debug/modul1.o
debug/modul2.o
...

release/projekt.elf
release/modul1.o
release/modul2.o
...

Autor: RichardHi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Walter T. schrieb:
>> woher weiß der Code dann welcher Level korrekt ist?
>
> Vielleicht ist es keine Information, die der Quelltext auswertet,
> sondern der Nutzer?

Das denke ich auch. Wenn der Optimizer-Level beim Programmstart 
angezeigt wird, kann der Nutzer entscheiden: Shit happens, nochmal 
kompilieren.

> Ich mache bei meinen Projektchen, sobald ein LCD dran ist, auch immer
> eine kleine Info-Darstellungsfunktion, wo Versionsstand,
> Compilerversion, usw. dargestellt werden. Das hilft, wenn man die Geräte
> nach langer Zeit wieder in die Hand bekommt, durchaus weiter.

Jawoll, genau so sehe ich das auch! Walter versteht mich. D A N K E

Zeigst du auch Compiler-Parameter an, wie z.B. den Optimizer-Level?
-- das wäre ja die Lösung --

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
RichardHi schrieb:
> Das denke ich auch. Wenn der Optimizer-Level beim Programmstart
> angezeigt wird, kann der Nutzer entscheiden: Shit happens, nochmal
> kompilieren.
Du willst also Walter's Ansatz, der ein ganz anderes Problem löst, nur 
eingeschränkt nutzbar ist (nur wenn LCD vorhanden und flashen einfach 
ist), auf dein Problem anwenden, für welches es bereits eine etablierte 
Lösung gibt, die bei Tausenden Projekten wunderbar funktioniert und 
gegen die bisher kein einziges Gegenargument genannt wurde? Man kann es 
sich auch schwer machen...

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
PS: Du kannst ja in der Build-Configuration so etwas wie 
-DCONF=\"Relase\" bzw. -DCONF=\"Debug\" übergeben, und dann im Programm 
auf dem LCD CONF ausgeben, da kannst du dann sehen wie es kompiliert 
wurde. Da du in der Build-Configuration nie den Optimierungs-Level 
ändern solltest, kannst du so folgern welche Optimierung angewendet 
wurde.

Autor: RichardHi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> .., für welches es bereits eine etablierte
> Lösung gibt, die bei Tausenden Projekten wunderbar funktioniert

Sorry, wo ist die Lösung? Nur weil ich zwischen zwei Configurations in 
der IDE umschalten kann, weiß ich doch auf dem ATmega nicht, welche 
Build-Variante gefahren wurde, und ob die vielleicht geändert wurde, 
z.B. der Optimizer-Level.

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
RichardHi schrieb:
> weiß ich doch auf dem ATmega nicht, welche
> Build-Variante gefahren
Musst du ja auch nicht. Du flasht grundsätzlich immer die 
Release-Variante, die ist immer mit Optimierungen. Da brauchst du dann 
auch nix mehr zu prüfen.

RichardHi schrieb:
> und ob die vielleicht geändert wurde,
> z.B. der Optimizer-Level.
Du lässt bei Release immer die Optimierung an, und bei Debug aus.

Autor: Oliver S. (oliverso)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Genau. Und weil du das nicht weißt, ist der Ansatz, sich auf gar nichts 
zu verlassen, sondern einfach ein komplettes rebuild mit nachgeprüften 
und bekannten Optionen durchzuführen und zu flashen.

Oliver

Autor: Walter Tarpan (nicolas)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
RichardHi schrieb:
> Zeigst du auch Compiler-Parameter an, wie z.B. den Optimizer-Level?
> -- das wäre ja die Lösung --

Nein, ich kenne keine Lösung für das Problem. Ich zeige einfach nur an,
 -ob es ein Debug-Build ist oder nicht (im Screenshot: kein Debug) und
 -ob der Quelltext des Builds ins Versionsverwaltungssystem eingecheckt 
wurde (hier: noch nicht.)

Die Optimierungs-Optionen in Release ändere ich so selten, da meine 
Firmware immer massig Reserve hat, daß ich sie bei Bedarf mit der 
Kenntnis der Versionsnummer immer aus dem Makefile auslesen kann. 
Sprich: Für mich ist das Problem keins.

: Bearbeitet durch User
Autor: RichardHi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Du flasht grundsätzlich immer die
> Release-Variante, die ist immer mit Optimierungen

Ich muss doch aber auch mal eine andere Variante flashen können, um 
Tests auf der echten Hardware zu machen - nicht alles geht im Debugger. 
Besonders nicht, wenn vom ATmega noch einiges an Elektronik gesteuert 
wird.

Und dann habe ich die Notwendigkeit, auf der LCD-Anzeige (die ich immer 
dran habe) zu sehen, ob und wie optimiert wurde.

Vertrauen ist gut - Kontrolle ist besser. Meistens sitzt das Problem 
davor.

Autor: Bernd K. (prof7bit)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
RichardHi schrieb:
> Insbesondere habe ich einen Codeabschnitt gehabt, den der Optimizer
> kaputt-optimiert hat. Den musste ich einklammern in
>   #pragma GCC optimize ("-O1")
>   <Codeabschnitt>
>   #pragma GCC reset_options

Das hättest Du auch beheben können indem Du stattdessen einfach Deinen 
defekten Code repariert hättest.

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
RichardHi schrieb:
> Ich muss doch aber auch mal eine andere Variante flashen können, um
> Tests auf der echten Hardware zu machen - nicht alles geht im Debugger.
Kannst du ja...

RichardHi schrieb:
> Und dann habe ich die Notwendigkeit, auf der LCD-Anzeige (die ich immer
> dran habe) zu sehen, ob und wie optimiert wurde.
Statt zu prüfen, kannst du auch einfach die Release Variante drüber 
flashen. Wenn die schon drauf war, sollte der Programmer das merken und 
nichts tun, um Flash-Zyklen zu sparen.

Autor: RichardHi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bernd K. schrieb:
> Das hättest Du auch beheben können indem Du stattdessen einfach Deinen
> defekten Code repariert hättest.


Sage mal: warum behauptest du, der Code wäre defekt gewesen?

Wenn du etwas Produktives beizutragen hast, kannst du dich gerne wieder 
melden. Aber das ist gar nix: Behauptungen aufzustellen, ohne überhaupt 
die Idee eines Beweises zu haben.

Übrigens geht es in diesem Thread nicht um das ADC-Codestück.
Es geht um was ganz anderes.
"Wer lesen kann ist klar im Vorteil"

So.

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
RichardHi schrieb:
> Sage mal: warum behauptest du, der Code wäre defekt gewesen?

Aus purer Neugier: Zeig den Code doch einfach mal... Wenn er wirklich 
korrekt ist, hattest du ja Recht ;-)

Autor: Bernd K. (prof7bit)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Wenn Du ständig damit beschäftigt bist immer wieder aufs Neue auf gut 
Glück an den Optimierungsoptionen herumzuschrauben und dann ausprobieren 
ob es jetzt zufällig besser oder schlechter wird oder gar nicht mehr 
gehtr dann ist was anderes faul (Das ganze Konzept, die gewählte 
Hardware, die Umsetzung, die Fertigkeiten, etc).

Normalerweise hat man eine Einstellung für Release mit den üblichen 
empfohlenen Optionen und noch ne zweite Einstellung für Debug. Die 
meiste Zeit verbringt man empfohlenerweise damit sich über die Hardware 
und den Code und deren Zweck und Funktionsweise Gedanken zu machen 
anstatt dauernd blind an den Compileroptionen rumzuspielen.

Autor: RichardHi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> PS: Du kannst ja in der Build-Configuration so etwas wie
> -DCONF=\"Relase\" bzw. -DCONF=\"Debug\" übergeben, und dann im Programm
> auf dem LCD CONF ausgeben

Das möchte ich jetzt mal ausprobieren.

> auf dem LCD CONF ausgeben

Was kommt im Programm an?
Wie greife ich darauf zu?

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
RichardHi schrieb:
> Was kommt im Programm an?
> Wie greife ich darauf zu?

Einfach CONF reinschreiben. Das wird dann zu "Debug" bzw. "Release", 
d.h. einem schnöden String-Literal. Wie du vielleicht weißt, kann man 
String-Literale einfach hintereinander schreiben, um sie zu 
konkatenieren:
int main () {
  puts ("Build-Configuration: " CONF);
}

Autor: Bernd K. (prof7bit)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
RichardHi schrieb:
> Aber das ist gar nix: Behauptungen aufzustellen, ohne überhaupt
> die Idee eines Beweises zu haben.

Du hast doch angefangen zu behaupten der gcc würe Deinen Code 
kaputtmachen ohne den Hauch eines Beweises vorzulegen. Mein Rasiermesser 
sagt mir daß es überwältigend viel wahrscheinlicher ist daß Dein Code 
schon vorher kaputt war und der tausende von Mannjahren alte gcc alles 
vollkommen richtig gemacht hat.

Autor: RichardHi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bernd K. schrieb:
> dauernd blind an den Compileroptionen rumzuspielen

Wieso behauptest du das? Du weißt doch gar nicht, ob ich weiß was ich 
tue.

Weißt du was du schreibst?

Jetzt: konkreter Vorschlag und nicht so ein "du machst es 
falsch"-Gesabbel.

Autor: RichardHi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bernd K. schrieb:
> Du hast doch angefangen zu behaupten der gcc würe Deinen Code
> kaputtmachen

Hab ich nie gesagt. So, und jetzt geh mal offline.

Autor: Bernd K. (prof7bit)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
RichardHi schrieb:
> Bernd K. schrieb:
>> Du hast doch angefangen zu behaupten der gcc würe Deinen Code
>> kaputtmachen
>
> Hab ich nie gesagt. So, und jetzt geh mal offline.

Doch, und zwar da:
Beitrag "Re: AVR GCC Optimizer Level in C-Programm abfragen"

und zwar wörtlich "kaputt" soll er ihn gemacht haben, ich zitiere:

> habe ich einen Codeabschnitt gehabt, den der Optimizer kaputt-optimiert hat

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
2 lesenswert
nicht lesenswert
RichardHi schrieb:
> Hab ich nie gesagt.

Doch, hast du.

Beitrag "Re: AVR GCC Optimizer Level in C-Programm abfragen"

RichardHi schrieb:
>> dauernd blind an den Compileroptionen rumzuspielen
>
> Wieso behauptest du das? Du weißt doch gar nicht, ob ich weiß was ich
> tue.

Jedenfalls irgendwas Seltsames.

Ich programmiere nun schon ziemlich lange Mikrocontroller, aber ich
habe eigentlich so gut wie nie die Notwendigkeit gehabt, an den
Optimierungseinstellungen herumzuschrauben.  Wenn ich ein
Zwischenergebnis im Debugger sehen will, welches mir der Compiler
ansonsten wegoptimiert, dann werf' ich mal schnell eine "volatile"
deklarierte Variable da hinein, in der das Ergebnis notiert wird.

Debuggt wird bei mir prinzipiell mit den Einstellungen, mit denen das
am Ende im Feld laufen soll, denn ich möchte das debuggen, was dann
auch benutzt wird, und nicht ein völlig anderes Compilat.

Bei dieser Vorgehensweise erübrigt sich das ganze Ansinnen, das zu
diesem Thread geführt hat, eigentlich von vornherein.

Autor: RichardHi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Aus purer Neugier: Zeig den Code doch einfach mal... Wenn er wirklich
> korrekt ist, hattest du ja Recht ;-)

Nochmal: Ich will gar nicht Recht haben.

Ich zeige euch den Code, damit Ruhe ist.
#define mess_delay 5              // Verzögerung vor der Messung
#define mess_delta 50             // Differenz, die 0 und 1 unterscheidet
#define ADC_pin_check 0           // der ADC-Channel für Pin-Kontaktmessung
uint16_t messung0;
uint16_t messung1;

    initADC_single();

    // Bit ausschalten
    switch (pin[k].reg)
      {
      case 'A': PORTA &= ~(1<<pin[k].bit); break;
      case 'B': .. hier weitere Fälle
      default : error(4,tx_wrong_case);
      }
      
    delay_ms(mess_delay);
    messung0=ADC_read_avg(ADC_pin_check,2);

    // Bit einschalten
    switch (pin[k].reg)
      {
      case 'A': PORTA |= (1<<pin[k].bit); break;
      case 'B': .. hier weitere Fälle
      default : error(4,tx_wrong_case);
      }

    delay_ms(mess_delay);
    messung1=ADC_read_avg(ADC_pin_check,2);

iniADC_single ist:

void initADC_single (void)  
{ 
ADCSRA |= ((1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0));   //Prescaler at CPUclock/128 
ADMUX  |= (1<<REFS0);                           //internal Reference 
ADMUX  &= ~(1<<REFS1);                          //Avcc(+5V) as voltage reference
//ADMUX  |= (1<<ADLAR);                         //Ergebnis linksbündig 
ADMUX  &= ~(1<<ADLAR);                          //Ergebnis rechtsbündig 
ADCSRA |= (1<<ADEN);                            //ADC enable
ADCSRA |= (1<<ADSC);                            //Start initial conversion
while (ADCSRA & (1<<ADSC) ) {;} // auf Abschluss der Konvertierung warten
}

und ADC_read_avg ist:

uint16_t ADC_read_avg (const uint8_t channel, const uint16_t times )
{
uint32_t sumADC=0;
for (uint8_t i=0;i<times;i++)
  {sumADC += ADC_read(channel);}
return sumADC/times;  // das dauert länger wegen der Division
}
Was ich erlebt habe ist, dass der Optimizer mit -O2 und -O3 den Code
  messung1=ADC_read_avg(ADC_pin_check,2);
nicht ausführt.

Das umgehe ich, indem ich temporär auf -O1 zurückgehe, wie bereits oben 
beschrieben.

: Bearbeitet durch Moderator
Beitrag #5166565 wurde vom Autor gelöscht.
Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
RichardHi schrieb:
> Was ich erlebt habe ist, dass der Optimizer mit -O2 und -O3 den Code
>   messung1=ADC_read_avg(ADC_pin_check,2);
> nicht ausführt.
Sieht alles soweit ok aus, aber ADC_read wird nicht gezeigt :/ Und ohne 
den Code selbst kompilieren zu können kann ich da auch nicht wirklich 
was zu sagen...

RichardHi schrieb:
> return sumADC/times;  // das dauert länger wegen der Division
... Es sei denn die Funktion wird geinlined, die Division durch 2 ist 
schnell (bitshift).

Le X. schrieb im Beitrag #5166565:
> Stelle nur kurze, präzise Fragen die sich ebenso kurz und präzise
> beantworten lassen.
Die kurze präzise Antwort ist "geht nicht". Das hilft ihm wenig. Daher 
wurden alternative bewährte Lösungsansätze geboten.

Autor: RichardHi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Sieht alles soweit ok aus, aber ADC_read wird nicht gezeigt

Hier ist der ADC_read:
/* ADC Einzelmessung */
uint16_t ADC_read(const uint8_t channel )
{
// Kanal waehlen
ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x07);   // select channel number 0..7  
ADCSRA |= (1<<ADSC);                            // eine Wandlung "single conversion"
while (ADCSRA & (1<<ADSC) ) {;}                 // warte auf Abschluss der Konvertierung 
return ADCW;                                    // ADC auslesen und zurückgeben
}

: Bearbeitet durch Moderator
Autor: RichardHi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Und ohne
> den Code selbst kompilieren zu können kann ich da auch nicht wirklich
> was zu sagen...

Und ohne die Schaltung, die an dem ADC-Pin hängt, vielleicht auch nicht.

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
RichardHi schrieb:
> Und ohne die Schaltung, die an dem ADC-Pin hängt, vielleicht auch nicht.
Och, den Compiler anwerfen und den Assembler-Code anschauen würde gehen. 
Da sollten ja die zwei Aufrufe zu sehen sein. Selbst wenn ich es flashen 
würde, könnte ich ja das Rauschen messen...

Autor: RichardHi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Selbst wenn ich es flashen
> würde, könnte ich ja das Rauschen messen...

Stimmt auch wieder.


Jörg W. schrieb:
> Bei dieser Vorgehensweise erübrigt sich das ganze Ansinnen, das zu
> diesem Thread geführt hat, eigentlich von vornherein.

Wer nicht mehr mitmachen möchte, darf sich ausklinken.

Und nicht vergessen: Die Spinner sind die Gewinner. War immer so.

==============================
Jetzt wieder zum Thread-Thema:
==============================

Dr. Sommer schrieb:
>> PS: Du kannst ja in der Build-Configuration so etwas wie
>> -DCONF=\"Relase\" bzw. -DCONF=\"Debug\" übergeben, und dann im Programm
>> auf dem LCD CONF ausgeben
>

Das möchte ich jetzt mal ausprobieren. Sagte ich schon.

Frage dazu:
Verstehe ich es richtig? Wenn ich  -DCONF=\"Relase\"  in die Options 
schreibe (dahin wo auch sowas wie -Wstrict-prototypes oder -Wall steht),
dann kann ich im C-Programm hinschreiben:
  char lcd[32];
  strncpy(lcd,CONF,32);
und in lcd steht dann "Relase"?

Autor: Ralf G. (ralg)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
RichardHi schrieb:
> Insbesondere habe ich einen Codeabschnitt gehabt, den der Optimizer
> kaputt-optimiert hat.

RichardHi schrieb:
> Ich muss doch aber auch mal eine andere Variante flashen können, um
> Tests auf der echten Hardware zu machen - nicht alles geht im Debugger.

Was verstehst */du/* unter (un)echter Hardware und unter Debugger?

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
RichardHi schrieb:
> Frage dazu:
> Verstehe ich es richtig? Wenn ich  -DCONF=\"Relase\"  in die Options
> schreibe (dahin wo auch sowas wie -Wstrict-prototypes oder -Wall steht),
> dann kann ich im C-Programm hinschreiben:
>   char lcd[32];
>   strncpy(lcd,CONF,32);
> und in lcd steht dann "Relase"?
Ja ist korrekt. Das -DCONF=\"Release\" ist äquivalent zu
#define CONF "Release"
Somit wird aus der strncpy-Zeile einfach (simple Textersetzung durch den 
Präprozessor)
strncpy(lcd,"Release",32);
Einfacher & effizienter geht's mit
const char lcd [] = CONF;

Autor: Rolf Magnus (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ralf G. schrieb:
> RichardHi schrieb:
>> Ich muss doch aber auch mal eine andere Variante flashen können, um
>> Tests auf der echten Hardware zu machen - nicht alles geht im Debugger.
>
> Was verstehst */du/* unter (un)echter Hardware und unter Debugger?

Die Vermutung liegt nahe, dass er mit "echter Hardware" den Prozessor 
meint (eben das, worauf man die Firmare flasht) und mit vermutlich eine 
Simulation des Prozessors. Dr. Sommer hatte als "Lösung" allerdings 
vorgeschlagen, die Debug-Version einfach gar nicht mehr auf den 
Prozessor zu flashen.

Autor: RichardHi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf M. schrieb:
> Die Vermutung liegt nahe, dass er mit "echter Hardware" den Prozessor
> meint (eben das, worauf man die Firmare flasht) und mit "Debugger" vermutlich 
eine
> Simulation des Prozessors.

Fast richtig. Der Prozessor sitzt auf einem Prozessorboard, an das 
weitere Hardware angeschlossen wird, die vom Prozessorboard gesteuert 
wird. Die habe ich im Debugger nicht zur Verfügung.

Mit Debugger meine ich den internen Debugger in AVR Studio. Das ist eine 
Simulation des Prozessors. Damit teste ich den Code zeilenweise, wenn er 
nicht das tut was ich meinte oder wollte.

Falsch macht der Compiler ja nichts, wie wir gelernt haben, nur anders 
als man vielleicht wollte. Manchmal macht man eben selbst was falsch.

Autor: Ralf G. (ralg)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
RichardHi schrieb:
> Mit Debugger meine ich den internen Debugger in AVR Studio.

Dacht' ich mir's. Das nennt man Simulator. Da liegt die Vermutung nahe, 
dass der Code auch in höheren Optimierungsstufen schon richtig 
ausgeführt wird, der Simulator aber die Codestellen 'nicht 
wiederfindet', weil durch die Programmoptimierung Zwischenergebnisse 
kurzzeitig in Registern gehalten werden oder sinnlose Berechnungen 
einfach 'wegoptimiert' sind. Es scheint dann so, dass beim Steppen 
durch den Quelltext Berechnungen übersprungen werden...

Autor: Bernd K. (prof7bit)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
RichardHi schrieb:
> Was ich erlebt habe ist, dass der Optimizer mit -O2 und -O3 den Code
>   messung1=ADC_read_avg(ADC_pin_check,2);
> nicht ausführt.

Wie hast Du das diagnostiziert? Hast Du das Assemblerlisting angesehen 
oder bist durchgesteppt? Oder bist Du nur aufgrund anderer Symptome 
indirekt zu einer Vermutung gelangt ohne den eigentlichen erzeugten Code 
zu sichten?

Autor: RichardHi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bernd K. schrieb:
> Wie hast Du das diagnostiziert?
> Hast Du das Assemblerlisting angesehen
> oder bist durchgesteppt?

Ob ich das Assemblerlisting angesehen habe, weiß ich nicht mehr. Ist 
schon länger her, und ich habe das Problem ja gelöst.
Dass beim durchsteppen Codestellen nicht gezeigt werden, weiß ich.


Bernd K. schrieb:
> Oder bist Du nur aufgrund anderer Symptome
> indirekt zu einer Vermutung gelangt?

Und zwar bin ich mit dem Degubber drangegangen, weil der Coe nicht das 
tat was ich wollte. Da hab ich dann bemerkt, dass die erste und zweite 
Messung sich nicht unterscheiden. (Oder war es so, dass die zweite 
Messung immer gleich war egal mit welchen Pins - das läuft nämlich in 
einer Loop, das hatte ich vorhin weggelassen.)
Und auf dem Prozessorboard verhielt es sich ebenso.

Ich will an dies Thema jetzt auch nicht mehr dran, denn es funktioniert 
so wie es ist. Dieser Thread geht ja um etwa ganz anderes.

Autor: RichardHi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Das -DCONF=\"Release\" ist äquivalent zu #define CONF "Release"


Und  B I N G O

Ich habe jetzt mehrere Configurations angelegt mit identischen 
Einstellungen außer -Ox und -DCONF=

War ein bisschen verzwickt, weil AVR Studio kein kopieren einer 
Configuration kann. So habe ich das XML-File <projekt>.aps manipuliert 
und Configurations kopiert und später in der IDE geändert.

Und ein simples lcd_print("\nConfig="); lcd_print(CONF); zum 
Programmstart oder sowas ähnliches ist der Schlüssel zum Glück.

Danke, Dr. Sommer und allen anderen für die vielen Beiträge.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.