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
Ja, mach (Dmax-Dmin) eine 2er potenz und schiebe ein paar positionen nach rechts.
Marc wrote:
> Das Problem: die Berechnung dauert im meinem 8-Bitter 0,48ms!
Und wie oft mußt Du sie ausführen ?
Peter
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.
Man koennte es als multiplikation umformen und dann auf interne multiplizierer zurueckgreifen, sofern vorhanden.
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
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;
@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.
Für solche und komplexere Sachen gibt's auf http://spiral.net/index.html entsprechende Generatoren und den theoretischen Hintergrund.
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.