Forum: Mikrocontroller und Digitale Elektronik Frage zur Code-Optimierung


von Werner (Gast)


Lesenswert?

Hallo zusammen,

ich wäre sehr daran interessiert, die folgenden Codefragmente etwas 
performanter zu lösen. Die beiden Berechnungen brauchen in der Form wie 
ich sie zur Zeit programmiert habe fast 500 us auf einem ATMega8 mit 
3,686 MHz Quartz. Kann ich hier evtl. mit geschickten Shift-Operationen 
die Sache etwas schneller berechnen? Mir fehlt auf diesem Gebiet 
sicherlich einiges an Erfahrung.

...
#define COUNTER_INCS_PER_MILLISECOND_T1  461 // (3.686.400 / 8) / 1000

volatile unsigned long ulAktuelleUmlaufzeitG=0;
volatile unsigned long ulAktuelleDrehzahlG=0;

ISR(INT0_vect)
{
   ...
   ulAktuelleDrehzahlG = 600000UL / (ulAktuelleUmlaufzeitG*10 / 
COUNTER_INCS_PER_MILLISECOND_T1);

   ...

   unsigned int uiTableIndex = ulAktuelleDrehzahlG / 50;

   ...
}


Gruß
Werner

von Randy N. (huskynet)


Lesenswert?

Solltest du noch nicht haben, versuche mal in den Projektoptionen die 
Compileroptimierung aufs Höchste zu stellen - was optimierbar ist 
(Shifts z.B.) macht der Compiler dann automatisch für dich.

Ansonsten wüsste ich nicht, was man optimieren kann (das macht alles 
schon der Compiler). Ich weiß nicht ob es möglich ist, aber kannst du 
vielleicht aus den Longs Integers machen? Das würde einiges bringen 
(fast die Hälfte, geschätzt).  Aber das wird sicherlich nicht gehen, 
sonst wären es ja schon Ints :-)

von Werner (Gast)


Lesenswert?

Danke Randy für den Hinweis. Im Makefile benutze ich z.Zt. die folgenden 
Einstellungen:

CC             = avr-gcc

CFLAGS        = -g -Wall $(OPTIMIZE) -mmcu=$(MCU_TARGET) $(DEFS)
LDFLAGS       = -Wl,-Map,$(PRG).map

Ich bin mir aber nicht sicher, ob das schon das Maximum an 
Codeoptimierung ist.


Gruß
Werner

von Randy N. (huskynet)


Lesenswert?

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Optimierungsgrad

Ich weiß nicht in welcher Form das jetzt schon in dem Makefile steckt, 
aber sowas wie -Os müsste da z.B. noch rein.

Einfach mal die Zeiten für die verschiedenen Optimierungsgrade testen, 
wobei der kompakteste Code meistens nicht der Schnellste ist. Kommt 
drauf an.

von Werner (Gast)


Lesenswert?

Sorry, hinter dem OPTIMIZE verbirgt sich die -Os Option. Ich werde das 
mit den verschiedenen Compiler-Optionen mal ausprobieren.

Danke.

Gruß
Werner

von I_ H. (i_h)


Lesenswert?

Warum hast du die beiden globalen Variablen denn als volatile 
deklariert?

von chris (Gast)


Lesenswert?

Schreib das ganze doch mal wie folgt, dann sparst du eine Division und 
eine Multiplikation:


#define COUNTER_INCS_PER_MILLISECOND_T1  461
#define TIME_TO_RPM_CONST                (60000L * 
COUNTER_INCS_PER_MILLISECOND_T1) // (600000 * 
COUNTER_INCS_PER_MILLISECOND_T1) / 10

ISR(INT0_vect)
{
   ...

   ulAktuelleDrehzahlG = TIME_TO_RPM_CONST / ulAktuelleUmlaufzeitG;

   ...

   unsigned int uiTableIndex = ulAktuelleDrehzahlG / 50;

   ...
}


Wenn der Wert für ulAktuelleDrehzahlG etwas ungenauer sein darf, kann 
man noch eine Division sparen und schreiben:

#define COUNTER_INCS_PER_MILLISECOND_T1  461
#define TIME_TO_RPM_CONST                (1200 * 
COUNTER_INCS_PER_MILLISECOND_T1) // (600000 * 
COUNTER_INCS_PER_MILLISECOND_T1) / ( 10 * 50)

ISR(INT0_vect)
{
   ...

   unsigned int uiTableIndex = TIME_TO_RPM_CONST / 
ulAktuelleUmlaufzeitG;

   ...

   ulAktuelleDrehzahlG = 50 * uiTableIndex;     // da zuerst die 
Division ausgeführt wird, treten hier Rundungsfehler auf!!!!!

   ...

}

von DerSchelm (Gast)


Lesenswert?

Wenn Dein Compiler nicht expilizit eine Division kann, dann ist die 
Division sehr sehr sehr teuer. Darin denke ich, liegt der Grund für 
Deine langen Zeiten.

Also: Nicht durch 50 teilen, sondern die Division durch eine 
Multiplikation und anschließende  Teilung durch eine Zweierpotenz 
(shiften) ersetzen. Wieso multiplizierst Du mit 60000 und teilst dann 
durch 50 (also nur mit 1200 multiplizieren)? Macht der Compiler wirklich 
eine Division zur Laufzeit daraus (schau mal ins LST-File)?

An der Division würde ich den Optimierungshebel ansetzen.

von Peter D. (peda)


Lesenswert?

Werner wrote:

> Die beiden Berechnungen brauchen in der Form wie
> ich sie zur Zeit programmiert habe fast 500 us auf einem ATMega8 mit
> 3,686 MHz Quartz.

Erklär dochmal, was Dich an den 500µs stört.
Warum brauchst Du die Ergebnisse schneller als 500µs?

Wie groß ist die Tabelle, die Du damit adressierst?
Eventuell reicht ja eine Berechnung mit 8Bit aus.

Du machst insgesamt 3 Divisionen mit 3 Konstanten. Fasse mal alle 3 
Konstanten zusammen und mache nur eine Division.

Bzw. wenn Du nur ne Tabelle zugreifen willst, sollte es auch ganz ohne 
Division gehen.


Peter

von Wolfram (Gast)


Lesenswert?

Wie wäre es mit einer intelligenteren Optimierung?
Wenn ich das richtig sehe möchtest du die Drehzahl ermitteln, 
wahrscheinlich
per Interrupt indem du die Ticks pro Zeiteinheit zählst. Die Berechnung 
ist also NUR NÖTIG wenn du die Drehzahl wirklich abfragst. Zähle in der 
Interruptroutine nur die Ticks und dort wo du die Drehzahl brauchst(im 
Hauptprogramm) berechnest du sie. Damit dürfte die Interruptzeit von 
500us (wirklich so viel?) auf 2us fallen und du sparst dir zusätzlich 
noch eine ganze Menge unnötige Berechnungen.

von Werner (Gast)


Lesenswert?

Hallo zusammen,

vielen Dank für zahlreichen hervorragenden Tipps, die ich gleich heute 
Abend umsetzen werde. Bin schon wirklich gespannt, um wieviel sich die 
ursprüngliche Zeit zusammenkürzen läßt.

@I_H.:
>Warum hast du die beiden globalen Variablen denn als volatile
>deklariert?

Die beiden globalen Variablen benutze ich sowohl in Interruptroutinen 
als auch im Hauptprogramm. Gesetzt werden sie allerdings nur in der 
Interruptroutine und bei der globalen Initialisierung. Ich war mir 
ehrlich gesagt nicht ganz sicher, ob ich hierfür das "volatile" nun 
zwingend angeben muß oder nicht.

@Peter Danneger:
>Erklär dochmal, was Dich an den 500µs stört.
>Warum brauchst Du die Ergebnisse schneller als 500µs?
>
>Wie groß ist die Tabelle, die Du damit adressierst?
>Eventuell reicht ja eine Berechnung mit 8Bit aus.

Na ja, das Ganze ist eine variable Zündsteuerung für einen 
Zweitakt-Motor. Der erste Geber-Impuls (PickUp) wird 72° Grad vor OT 
ausgelöst und aktiviert den Interrupt, der die aktuelle Drehzahl 
berechnet und dann davon abhängig einen Timer-Wert (Delay) aus einer 
Tabelle ausliest. Nach Ablauf des Timers wird dann der eigentliche 
Zündimpuls ausgelöst. Bei Drehzahlen > 10.000 U/Min wird bei 500us 
Berechnungszeit für den Timer die Luft für den Einstellungsspielraum 
schon ziemlich dünn.

Die Tabelle enthält 256 Werte und ich werde Deinen Tipp beherzigen und 
auf eine 8Bit-Berechnung umstellen.

Ich denke, dass sich alle Tipps gut umsetzen lassen. Nochmals Danke an 
Alle.

Gruß
Werner

von gAST (Gast)


Lesenswert?

Hallo  Werner,

wieso benutzt Du nicht einfach einen 7,373 bzw 14,746 MHz Quarz?

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
Noch kein Account? Hier anmelden.