Forum: Mikrocontroller und Digitale Elektronik Laufzeitoptimierung


von Marc (Gast)


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

von Null (Gast)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

Marc wrote:

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

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


Peter

von crazy horse (Gast)


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.

von Null (Gast)


Lesenswert?

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

von Markus (Gast)


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

von Wolfram (Gast)


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.

von Wolfram (Gast)


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?

von Markus (Gast)


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

von Wolfram (Gast)


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

von Marc (Gast)


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;

von Markus (Gast)


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);

von Wolfram (Gast)


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.

von Markus (Gast)


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.

von Ralph (Gast)


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.

von Marc (Gast)


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

von Ralph (Gast)


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.

von Arc N. (arc)


Lesenswert?

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