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
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.
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
>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.
> 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?
>> 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
@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
@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);
@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.
@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.
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.
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);
//
...
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.