Hallo, bin Anfänger. Ich möchte innerhlab eines Programmes (s.ICP_UART_162_01.c) vom Programm durchgeführte Berechnungen auf ihren Zeitverbrauch hin messen. dazu schalte ich vor der Rechnung PINA4 auf 0 und nach der Messung wieder auf 1. Das dabei entstehende negative Signal messe ich mit dem Oszi. Habe das Bild angehängt. Nun die Frage: Es ist egal, welche Berechnung ich messe, ob Erg=Erg*100 / 36 oder die Nachfolgende, oder aber alle drei( incl signal = Faktor*1.000.000 / Erg, Es kommt immer ein Wert um die 13,84 µs raus. Habe ich hier einen grundsätzlichen Denkfehler? Würde mich über einen Tipp freuen Thomas
edit. Kuck dir mal das Listing / Assembler an, vor allem wenn du nur eine oder alle 3 Berechnungen verwendest. Sollte eigentlich bei 3 Berechnungen länger dauern.
Thomas schrieb: > Es kommt immer ein Wert um die 13,84 µs raus. Deine Grafik zeigt aber etwas anderes ... Gruß Jobst
Die C Compiler können da einiges Optimieren. Es ist gut möglich das sich durch die Umstellung der Faktoren am erzeugten Code nichts ändert. Einiger der Faktoren werden auch schon zur Laufzeit zusammengefasst - soweit es halt geht. Ob man 10000 oder 10*10*100 schreibt ist z.B. egal.
....weil ich aus der *.lss Datei nicht schlau werde, hatte ich versucht das Problem in der oben beschriebenen Art zu löen. In der assemblerdatei kann ich die einzelnen Rechenschritte nicht differenzieren, da ich kein assembler kann. In der assemblerdatei werden auch nicht alle Kommentierungen angezeigt, an denen ich mich hätte orientieren können. Gruß Thomas
@ Jobst sorry, ich habe das erste Mal mein neues Oszi in Betreib genommen. Habe vorher noch nie sowas genutzt. kann sein, dass ich damit vielleicht was ganz anderes gemessen hatte? Gruß Thomas
Wenn Optimierungen eingeschaltet sind, wird der Compiler die Berechnungen nicht zwangsläufig zwischen den Zuweisungen an PORTA durchführen. Die Initialisierung von Erg greift auf volatile Variablen zu und wird deshalb zwangsläufig vor dem ersten PORTA Zugriff abgearbeitet. Die anderen Berechnungen aber kann der Compiler beliebig umformen und auch hinter den zweiten PORTA verschieben, es zählt nur das Ergebnis signal, das itoa() übergeben wird.
Thomas schrieb: > @ Jobst > > sorry, ich habe das erste Mal mein neues Oszi in Betreib genommen. Habe > vorher noch nie sowas genutzt. kann sein, dass ich damit vielleicht was > ganz anderes gemessen hatte? > > Gruß > Thomas Naja, etwa 180us ... Gruß Jobst
Andreas B. schrieb: > Wenn Optimierungen eingeschaltet sind, wird der Compiler die > Berechnungen nicht zwangsläufig zwischen den Zuweisungen an PORTA > durchführen. Die Initialisierung von Erg greift auf volatile Variablen > zu und wird deshalb zwangsläufig vor dem ersten PORTA Zugriff > abgearbeitet. Die anderen Berechnungen aber kann der Compiler beliebig > umformen und auch hinter den zweiten PORTA verschieben, es zählt nur das > Ergebnis signal, das itoa() übergeben wird. Echt, so wat fieses tun Compiler?!
Ja. Ist nach ISO-C auch völlig ok. Das nennt sich "as-if rule" und besagt, daß das Programm nicht exakt das tun muß, was im Code steht, sondern daß es reicht, wenn das "observable behavior" gleich ist, und das ist definiert über I/O und den Zugriff auf volatile-Variablen. Alles andere darf beliebig verändert, umsortiert, zusammengefaßt oder wegoptimiert werden.
An alle: Vielen dank für Eure Hilfe! Insbesondere die erklärungen von Andreas B in Hinblick auf das Compilerverhalten waren sehr erhellend! Gruß Thoms
Rolf Magnus schrieb: > Ja. Ist nach ISO-C auch völlig ok. Das nennt sich "as-if rule" und > besagt, daß das Programm nicht exakt das tun muß, was im Code steht, > sondern daß es reicht, wenn das "observable behavior" gleich ist Aber das beobachtbare Verhalten ist m.E. anders, wenn der Compiler aus dem hier:
1 | PORTA &= ~(1<<PA4); // neg. Peak beginnt |
2 | |
3 | Erg = Erg*100 / 36; |
4 | Faktor = Erg*65 / 10000; |
5 | signal = Faktor*1000000 / Erg; |
6 | |
7 | PORTA |= (1<<PA4); // neg Peak endet |
das hier macht:
1 | PORTA &= ~(1<<PA4); // neg. Peak beginnt |
2 | PORTA |= (1<<PA4); // neg Peak endet |
3 | |
4 | Erg = Erg*100 / 36; |
5 | Faktor = Erg*65 / 10000; |
6 | signal = Faktor*1000000 / Erg; |
Das dürfte er ja. Und wenn es tatsächlich so ist, wie kann ich den Compiler sanft dazu zwingen, mir die Beobachtung der Rechenzeit zu ermöglichen? Ist da mit Klammern/Blöcken was zu machen?
1 | PORTA &= ~(1<<PA4); // neg. Peak beginnt |
2 | {
|
3 | Erg = Erg*100 / 36; |
4 | Faktor = Erg*65 / 10000; |
5 | signal = Faktor*1000000 / Erg; |
6 | }
|
7 | PORTA |= (1<<PA4); // neg Peak endet |
Und was ist bei sowas:
1 | void calc() |
2 | {
|
3 | Erg = Erg*100 / 36; |
4 | Faktor = Erg*65 / 10000; |
5 | signal = Faktor*1000000 / Erg; |
6 | }
|
7 | :
|
8 | :
|
9 | PORTA &= ~(1<<PA4); // neg. Peak beginnt |
10 | calc(); |
11 | PORTA |= (1<<PA4); // neg Peak endet |
Darf der Compiler das auch umstellen:
1 | void calc() |
2 | {
|
3 | Erg = Erg*100 / 36; |
4 | Faktor = Erg*65 / 10000; |
5 | signal = Faktor*1000000 / Erg; |
6 | }
|
7 | :
|
8 | :
|
9 | PORTA &= ~(1<<PA4); // neg. Peak beginnt |
10 | PORTA |= (1<<PA4); // neg Peak endet |
11 | calc(); |
Oder, um die Frage umzustellen: (Wie) kann ich denn mit den hier aufgeführten Mitteln (Oszi + Portpin) die benötigte Zeit für eine Berechnung ermitteln? Es hilft ja nichts, alle beteiligten Variablen auf volatile zu setzen, das wäre dann ja nicht mehr der Code, den ich eigentlich vermessen will...
Der Compiler darf auch den Funktionsaufruf umstellen und auch inline expandieren. Um es wirklich so mit Port-Pin zu machen, sollte im GCC wohl das funktionieren:
1 | int result = 0; // wird als Dummy verwendet |
2 | |
3 | PORTA &= ~(1<<PA4); // neg. Peak beginnt |
4 | asm volatile ("" : "X"(result)); |
5 | result = calc(); |
6 | asm volatile ("" : "X"(result)); |
7 | PORTA |= (1<<PA4); // neg Peak endet |
Damit wird ein Assembler-Befehl eingefügt (tatsächlich nur ein leerer String) und dem Compiler gesagt, dass er den nicht wegoptimieren darf. Dazu dann wird result darin referenziert, so dass auch da die Reihenfolge nicht geändert werden kann. Eine bessere Möglichkeit auch ohne Oszi dürfte sein, alles in eine Funktion zu packen (die das Ergebnis auch zurück gibt, damit halt nicht der ganze Inhalt der Funktion wegoptimiert wird) und die in einer anderen Datei (um dem Compiler Kenntnisse um Inhalt der Funktion zu verweigern) in einer Schleife 10000 mal oder so aufzurufen. Die Zeit der ganzen Schleife geteilt durch die Durchläufe gibt dann die Laufzeit der Funktion (+ Aufruf + Schleife). Man kann das ganze noch mal mit einer leeren Funktion machen, dann sollte man die Zeit für Aufruf und Schleife haben und kann die vom ersten Wert abziehen.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.