Lahme mathlib für STM32F411

Gast #7286879
Lesenswert?

Ich habe gerade mal die Geschwindigkeit der sinf() - Funktion mit 
folgndem Progrämmchen getestet:
1
  volatile float f = 0.0f;
2
  float phi = 0.0f;
3
  float dphi = 0.1f;
4
  volatile uint32_t start, dt;
5
  start = uwTick;
6
  for (uint32_t i=0; i< 100000;i++) {
7
   f += sinf(phi);
8
   phi += dphi;
9
  }
10
  dt = uwTick - start;
Die volatile sind notwendig, damit der Compiler die Schleife nicht 
wegoptimiert. dt enthält die vergangene Zeit in Millisekunden.
Bei 48MHz CPU-Takt bekomme ich dt= 5948 ms. Ersetze ich den Ausdruck 
sinf(phi) durch phi, braucht der STM nur 33ms. Daraus folgt, dass eine 
Sinusfunktion 59µs, also über 2800 Takte benötigt. Wie kann das sein?
Meine Vermutung ist, dass die Mathematikbibliothek die 
Gleitkommahardware nicht nutzt. Leider habe ich keinen Sourcecode.

Die Hardware floating point unit ist aktiviert (-mfloat=abi-hard), die 
Linker-Option -lm ist aktiv.
Gast #7287205
Lesenswert?

Was erwartest du denn?
Soll die FPU den Sinus in einem Takt berechnen? Das geht doch gar nicht.
Wenn du dir mal die FPU anschaust, dann sind dort keine Operationen 
enthalten, die irgendeine trigonometrische Funktion direkt berechnen.

https://developer.arm.com/documentation/ddi0439/b/Floating-Point-Unit/FPU-Functional-Description/FPU-instruction-set?lang=en
Tabelle 7.1

Der Sinus muss also von Hand berechnen werden, und FPU unterstützt den 
Algorithmus mit den 4 Grundrechnenarten auf Floating Point Zahlen.

Dann wird evtl so etwas wie ein Cordic-Algorithmus benutzt, ähnlich 
diesem hier:
https://www.mikrocontroller.net/articles/AVR-CORDIC

Da sind doch einige Berechnungen in einer Schleife drin.
Gast #7287215
Lesenswert?

Wenn man schon einen F411 hat dann lässt man den auch mit
seiner maximalen Taktrate arbeiten, das sind dann 100 MHz
oder - um optimale Taktkonfigurationen zu erreichen - zum
Beispiel 96 MHz.
Gast #7287281
Lesenswert?

Hans-jürgen H. schrieb:
> Versuchs mal mit diesem Unterprogramm und berichte das Ergebnis.

ist schon interessant. Da wird mit Standard Float gerechnet (also nicht 
double), und bei der Konstanten
0.15915494309189533576888376337251F
werden 32 Nachkommastellen angegeben.

Bei IEEE-754 werden für Single-Precision 23 Bit Mantisse benutzt, was 
dann ca 7 Dezimalstellen bedeutet.
Gast #7288416
Lesenswert?

PittyJ schrieb:
> Bei IEEE-754 werden für Single-Precision 23 Bit Mantisse benutzt, was
> dann ca 7 Dezimalstellen bedeutet.

Eigentlich sollten es 24 Bit sein. Vielleicht hast du beim Zählen das 
Hiddenbit vergessen.

Also 6 ms für den Sinus bei einem 50 MHz Cortex will auch mir schon 
etwas arg langsam vorkommen. Zum Vergleich: mit dem Pedersen braucht man 
nach der Quadrantenseparation 3 Additionen, 3 Multiplikationen und 1 
Division. Da sollte der Sinus im unteren µs Bereich erledigt sein. Und 
für etwa 7 Dezimalstellen reicht der auch.

W.S.
Gast #7291021
Lesenswert?

Walter T. schrieb:
> Die Rückskalierung auf den Bereich
> 0...pi scheint recht aufwendig zu sein.

Eigentlich nicht. Wenn man das Ganze auf den Bereich 0..4 skaliert 
(genauer 0<=x<4), dann kostet das nur eine Multiplikation und schon hat 
man den Quadranten im ganzen Teil und den Restwinkel im gebrochenen 
Teil. Zum anschließenden Auseinandernehmen ist allerdings etwas 
Assembler dabei.

Natürlich kann man's auch viel umständlicher machen.

W.S.
Gast #7291080
Lesenswert?

W.S. schrieb:
> Wenn [...]

Die Implementation der libc ist einschlägig und man kann auch nachsehen 
anstelle zu raten.

Die Entwickler haben sich viel Mühe gegeben, dass große Argumente keinen 
vorzeitigen Auflösungsverlust ergeben, und das kostet Rechenzeit.

Und umgekehrt bedeutet das, dass Benchmarking von sinf() unbedingt im 
Ziel-Wertebereich erfolgen sollte, ansonsten ist die 
Rechenzeit-Abschätzung grob falsch.

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