Laufzeitoptimierung

Gast #565510
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
Gast #565530
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.
Gast #565584
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
Gast #565585
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.
Gast #565588
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?
Gast #565597
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
Gast #565609
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
Gast #565618
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;
Gast #565658
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);
Gast #565692
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.
Gast #565717
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.
Gast #565906
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.
Gast #567191
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);
 //
...

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren