Forum: Mikrocontroller und Digitale Elektronik sinf() langsam?


von Walter T. (nicolas)


Lesenswert?

Guten Morgen,

ich nutze den gut abgehangenen ARM-GCC 5.4.1, aber die vorliegende 
Problematik dürfte sich nicht geändert haben. MCU ist ein Cortex M4F 
(STM32F446RE) und die float-Erweiterungen werden genutzt.

Meine Beobachtung: Für kleine Eingangswerte ist die Funktion sinf() 
ziemlich schnell (grob 250 Takte), für große ( > 10*2*pi) wird sie aber 
ziemlich schnell langsam (> 2000 Takte).

Wo kann ich ich mir den Quelltext der builtin-Funktionen anschauen, um 
der Sache auf die Schliche zu kommen?

Gibt es irgendwo Informationen, bei welchen eingebauten Funktionen ich 
ebenfalls unerwartet massiv unterschiedliche Laufzeiten in Abhängigkeit 
der Eingabewerte erwarten muss? (Bei einem memcpy() oder printf() ist es 
nicht unerwartet, bei einem sinf() schon.)

von Monk (roehrmond)


Lesenswert?

Die Standard Funktionen der C Bilbiothek benutzen den Datentyp double, 
die FPU kann aber nur float. Es gibt alternative Funktionen, die du 
stattdessen benutzen kannst.

Schau dir dazu diesen Absatz an, ich glaube der trifft 1:1 auch auf 
deinen STM32F446 zu: http://stefanfrings.de/stm32/stm32f3.html#fpu

Das CPACR Register ist im "Programming Manual" beschrieben.

: Bearbeitet durch User
von Walter T. (nicolas)


Lesenswert?

Steve van de Grens schrieb:
> Die Standard Funktionen der C Bilbiothek benutzen den Datentyp double

sin() benutzt double, sinf() single precision float.

Die FPU ist aktiv, sonst hätte ich noch ganz andere Taktzahlen.

von Oliver S. (oliverso)


Lesenswert?

Walter T. schrieb:
> Wo kann ich ich mir den Quelltext der builtin-Funktionen anschauen, um
> der Sache auf die Schliche zu kommen?

In den gcc-Sourcen?

Wenn sinf(Wert % 2*PI) erkennbar schneller ist als sinf(Wert), dann ist 
wohl was faul. Sonst eher nicht.

Oliver

von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

Hi

Je nach verwendetet libc kann das aus unterschiedlichen Quellen kommen. 
Das hier
https://sourceware.org/git/?p=newlib-cygwin.git;a=blob;f=newlib/libm/mathfp/sf_sine.c;h=8dd34d9a1dac27ecd3e3fce50264977edde88640;hb=refs/heads/master 
ist aber mit hoher Wahrscheinlichkeit das was du suchst.

Matthias

von Walter T. (nicolas)


Lesenswert?

Μαtthias W. schrieb:
> Das hier
> 
https://sourceware.org/git/?p=newlib-cygwin.git;a=blob;f=newlib/libm/mathfp/sf_sine.c;h=8dd34d9a1dac27ecd3e3fce50264977edde88640;hb=refs/heads/master
> ist aber mit hoher Wahrscheinlichkeit das was du suchst.

Besten Dank! Sogar mit zitiertem Lesestoff.

Das muss ich mich heute abend mal in Ruhe zu Gemüte führen. Auf Anhieb 
finde ich nämlich nichts, was abhängig vom Funktionsparameter die 
Laufzeit ernsthaft beeinflussen könnte.

von Monk (roehrmond)


Lesenswert?

Walter T. schrieb:
> sin() benutzt double, sinf() single precision float.

Ja genau. Ich dachte dass für "große Werten" double Funktion verwendest 
hast. Wo man konkrete Angaben zum Timing findet weiß ich nicht.

von W.S. (Gast)


Lesenswert?

Walter T. schrieb:
> Auf Anhieb
> finde ich nämlich nichts, was abhängig vom Funktionsparameter die
> Laufzeit ernsthaft beeinflussen könnte.

Rein technisch besteht der Knackpunkt darin, daß man für 
Winkelfunktionen nur Winkel in einer einzigen Vollperiode braucht und da 
zum Berechnen auch nur im ersten Quadranten.

Folglich müssen ganze Perioden zuvor abgetrennt werden und dann alles 
auf eben diesen ersten Quadranten gefaltet werden - unter Vermerk, in 
welchem Quadranten der Winkel eigentlich lag.

So ein Vorgeplänkel ist für allgemeine Winkelfunktionen immer nötig. Und 
darin liegt auch der Unterschied für verschiedene Input-Werte.

W.S.

von Εrnst B. (ernst)


Lesenswert?

W.S. schrieb:
> Und
> darin liegt auch der Unterschied für verschiedene Input-Werte.

Nicht nur. Die "sinf"-Implementation aus der Newlib hat auch noch als 
Optimierung drinnen dass sin(x)≅x, für kleine x.

Spart eine Handvoll an float-Multiplikationen.

: Bearbeitet durch User
von A. B. (Gast)


Lesenswert?

W.S. schrieb:
> Folglich müssen ganze Perioden zuvor abgetrennt werden und dann alles
> auf eben diesen ersten Quadranten gefaltet werden - unter Vermerk, in
> welchem Quadranten der Winkel eigentlich lag.

Genau, und man sollte es mit der Größe der Argumente - gerade bei der 
kümmerlichen Genauigkeit von float - nicht übertreiben, sonst kann man 
auch einfach würfeln ... So findet man im steinalten MC68881 Manual:

"If the source operand is not in the range of [-2*\pi ... 2*\pi], then 
the argument is reduced to within that range before ... is calculated. 
However, large arguments may lose accuracy during reduction, and very 
large arguments (greater than approximately 10^20) loose ALL 
accuracy." Und das war bei Register-Argumenten, also 80-Bit FP-Format. 
Wann das bei float zuschlägt, kann man sich dann auch überlegen ...

von Walter T. (nicolas)


Lesenswert?

W.S. schrieb:
> Folglich müssen ganze Perioden zuvor abgetrennt werden

Ja, und im vorliegenden Quelltext wird das sehr geschickt gemacht, um 
keine Genauigkeit durch Skalierung zu verlieren. Da sehe ich nichts, was 
vom Funktionsparameter heftig abhängt.

A. B. schrieb:
> Wann das bei float zuschlägt, kann man sich dann auch überlegen ..

Bei über 1000*2*pi.

: Bearbeitet durch User
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.