www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Laufzeitoptimierung


Autor: Marc (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Leute,

ich habe folgenden Code:

#define Dmin 7838
#define Dmax 57457
 unsigned int phase;
...
 // Normierung
  phase = (phase - Dmin) * 65535UL / (Dmax - Dmin);
 // #0.47ms !!!!!!!!!!!!!!!!!!
...

Das Problem: die Berechnung dauert im meinem 8-Bitter 0,48ms!
Gibt es Optimierungsmöglichkeiten in der Rechengeschwindigkeit?
z.B: optimieren der Codzeile, Lösung in Asembler,...?

Danke für alle Tipps,
Marc

Autor: Null (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, mach (Dmax-Dmin) eine 2er potenz und schiebe ein paar positionen 
nach rechts.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Marc wrote:

> Das Problem: die Berechnung dauert im meinem 8-Bitter 0,48ms!

Und wie oft mußt Du sie ausführen ?


Peter

Autor: crazy horse (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich glaube gar nicht, dass es solange dauert. Ist natürlich auch direkt 
von der Taktfrequenz abhängig, ebenso vom MC selbst und auch vom 
Compiler und dessen Optimierungseinstellungen.
Hauen die vielleicht ständig Interrupts dazwischen? Also gib mal 
vollständige Informationen.

Autor: Null (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Man koennte es als multiplikation umformen und dann auf interne 
multiplizierer zurueckgreifen, sofern vorhanden.

Autor: Markus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
die integer division wird auf einem 8bitter lange dauern.
Als Alternative eine long Multiplikation mit anschliessendem 
Rechts-Shift.

#define Dmin 7838
#define Dmax 57457
 unsigned int phase;
 unsigned long temp;
...
 // Normierung
  temp = (unsigned long)(phase - Dmin)
  temp = temp * ((unsigned long)((65535.0 * 32768.0) / ((float)(Dmax - 
Dmin))));
  phase = (unsigned int)(temp >> 15);
...

Zu beachten:
65535UL / (Dmax - Dmin) darf nicht grösser als 2 werden.
Im Compiler muss Constant folding aktiviert sein.

gruss markus

Autor: Wolfram (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>phase = (phase - Dmin) * 65535UL / (Dmax - Dmin);
>// #0.47ms !!!!!!!!!!!!!!!!!!

Wie lange dauert
 phase=((phase*65536)/(Dmax - Dmin))-((Dmin*65536)/(Dmax-Dmin));

wenn man die Optimierung anschaltet?
Das macht zumindest den gleichen Fehler, wie du bisher
Wenn du das nicht möchtest, wäre es sehr empfehlenswert long zu benutzen
da für (phase-Dmin) >1 der Wertebereich zumindest für eine 16bit int 
überschritten wird.

Autor: Wolfram (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>  temp = (unsigned long)(phase - Dmin)
>  temp = temp * ((unsigned long)((65535.0 * 32768.0) / ((float)(Dmax -
>Dmin))));

was ist den das???
Dmax-Dmin sthet zur compilezeit fest (#define) warum soll das noch nach 
float
umgeformt werden?

>  temp = temp * ((unsigned long)((65535.0 * 32768.0) / ((float)(Dmax -
>Dmin))));

also bei >(65535.0 * 32768.0) blieben für temp=(phase-Dmin) noch 9Bit 
(0..512) übrig. Das ist doch wohl ein Scherz gewesen?

Autor: Markus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>>  temp = (unsigned long)(phase - Dmin)
>>  temp = temp * ((unsigned long)((65535.0 * 32768.0) / ((float)(Dmax -
>>Dmin))));
>
>was ist den das???
>Dmax-Dmin sthet zur compilezeit fest (#define) warum soll das noch nach
>float
>umgeformt werden?

Umwandlung in float damit sichergestellt ist das das constant folding 
korrekt ausgeführt wird ( keine wertüberschreitung, genauigkeit)

>>  temp = temp * ((unsigned long)((65535.0 * 32768.0) / ((float)(Dmax -
>>Dmin))));
>
>also bei >(65535.0 * 32768.0) blieben für temp=(phase-Dmin) noch 9Bit
>(0..512) übrig. Das ist doch wohl ein Scherz gewesen?

((unsigned long)((65535.0 * 32768.0) / ((float)(Dmax - Dmin))));
-> wird vom compiler ausgerechnet. Ergebnis: 43278
bleiben 16 bit für (phase-Dmin) übrig!!!
Bei der Annahme:
int -> 16 Bit
long -> 32 Bit

Autor: Wolfram (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Markus
Sorry ich hatte einen Fehler,
wenn du float verwendest könnte das ganze sogar funktionieren,abgesehen 
von der Genauigkeit die sich ergibt. Aber sein Problem ist die 
Geschwindigkeit.
Das beschriebene Problem ist bei
>temp * ((unsigned long)((65535.0 * 32768.0)
(int)*(long)
bedeutet temp wird umgeformt nach long, soweit ok
65535*32768 = (rund) 2^16*2^15= 2^31
wenn temp >1 multiplizierst du mit temp*2^31 und kommst über 2^32 das 
ist
größer als dein Datentyp long(32 Bit)
und ich nehme an temp wird >1 werden

Autor: Marc (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe nun auch die Division verkürzt. Mein Ergebnis:

        phase -= Dmin;
        long32 = (unsigned long)phase;
        long32 = long32 << 16;
        div = (Dmax - Dmin);
        div = div >> Div;
        long32 = long32 / (unsigned long)div;
        phase = (unsigned int)long32 >> Div;
        // #0.054 bei Div = 8;

Autor: Markus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@wolfram
temp ist unsigned long!
temp = temp * ((unsigned long)((65535.0 * 32768.0) / ((float)(Dmax - 
Dmin))));
entspricht (unsigned long)*(unsigned long)

Da Dmax und Dmin konstanten sind, rechnet der compiler den ausdruck aus:
temp = temp * 43278;

temp hat einen gültigen Wertebereich von 0 bis 4294967295. Also darf 
temp davor maximal 4294967295/43278= 99241 enthalten, um einen überlauf 
zu vermeiden.

Der Mikrocontroller selbst rechnet bei der Lösung kein float!

@marc
Sinn und Zweck ist die Division vollständig zu eliminieren.

>div = (Dmax - Dmin);
>div = div >> Div;
Hier verlierst du Genauigkeit! Die unteren 8 Bit von div werden einfach 
abgeschnitten!

>div = div >> Div;
>phase = (unsigned int)long32 >> Div;
diese beide Zeilen heben sich gegenseitig auf, bringen dir aber ausser 
ungenauigkeiten nichts!

>phase = (unsigned int)long32 >> Div;
der cast operator hat eine höhere priorität als der shift operator. wenn 
long32 grösser 65535 ist, werden die oberen 16bit abgeschnitten bevor 
geshiftet wird!
richtig:
phase = (unsigned int)(long32 >> Div);

Autor: Wolfram (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Markus:
die Auswertung des Ausdrucks erfolgt von links nach rechts, deshalb 
läuft dir dein Datentyp bei
>temp * ((unsigned long)((65535.0 * 32768.0)
schon über für temp>2, bevor die Division das ganze "verkleinert"

mag sein das der Compiler mit konstant folding etwas anderes macht
(ich wär mir da nicht so sicher), wie auch immer das läuft darauf 
hinaus, daß beim Debuggen (ohne Optimierung) was anderes als Ergebnis 
rauskommt, als beim späteren Programm mit Optimierung. Sowas bewusst zu 
programmieren halte ich für fahrlässig, zumindest wenn es später weiter 
verwendet wird.

Autor: Markus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Wofram
>die Auswertung des Ausdrucks erfolgt von links nach rechts, deshalb
>läuft dir dein Datentyp bei
>temp * ((unsigned long)((65535.0 * 32768.0)
>schon über für temp>2, bevor die Division das ganze "verkleinert"
Deswegen ist der konstante ausdruck komplett umklammert, damit dieser 
erst vollständig gerechnet wird! also kein überlauf...

>mag sein das der Compiler mit konstant folding etwas anderes macht
>(ich wär mir da nicht so sicher), wie auch immer das läuft darauf
>hinaus, daß beim Debuggen (ohne Optimierung) was anderes als Ergebnis
>rauskommt, als beim späteren Programm mit Optimierung. Sowas bewusst zu
>programmieren halte ich für fahrlässig, zumindest wenn es später weiter
>verwendet wird.
Einfach sowohl in der debug als auch release version constant folding 
aktivieren. Während dem debuggen stört diese optimierung nicht, da der 
code nicht umsortiert wird.
Meiner Erfahrung nach führt der gcc schon ohne optimierung constant 
folding automatisch aus.

Autor: Ralph (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo


mach daraus :
#define Dmin 7838
#define Dmax 57457

/* wird zur Compilezeit berechnet */
#define ScaleFaktor ((unsigned int)(65535UL / (Dmax - Dmin)))

 unsigned int phase;
...
 // Normierung
  phase = (phase - Dmin) * ScaleFaktor;
 //
...


damit bleibt zur Laufzeit nur eine Addition und eine Multiplikation 
übrig.

Willst du es noch schneller haben. dann scaliere alles so um das du mit 
8 Bit Variablen hinkommst, oder das dein ScaleFaktor eine 2er Potenz 
beträgt und du nur schiften musst, statt zu multiplizieren.

Autor: Marc (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke Ralph für den Tipp!

"Die einfachen Dinge sind die größten - und darum auch am schwersten zu 
bewältigen!"

ScaleFaktor wäre bei
#define ScaleFaktor ((unsigned int)(65535UL / (Dmax - Dmin)))
1,32 -> also unsigned int 1 - oder sehe ich das falsch!?

Ich habe deshalb folgendes gemacht:

#define Dmin 7838
#define Dmax 57457

#define ScaleFaktor ((unsigned long)(65535UL * 32768UL / (Dmax - Dmin)))

 unsigned int phase;
 unsigned long long32;
...
 // Normierung
        long32 = (phase - Dmin) * ScaleFaktor;
        phase = (unsigned int)(long32 >> 15);
 //
...

Autor: Ralph (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Warum shiftest du 15 Bit ?
8 Bit würden es auch tun.
Unter der Vorraussetzung das (Dmax - Dmin) > 256 kannst du dann auch auf 
Long verzichten und bei Int bleiben.

Autor: Arc Net (arc)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Für solche und komplexere Sachen gibt's auf http://spiral.net/index.html 
entsprechende Generatoren und den theoretischen Hintergrund.

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.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

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