Forum: Mikrocontroller und Digitale Elektronik AVR Float Geschwindigkeit


von ber l. (berluska)


Lesenswert?

Ich würde gerne wissen wie schnell mein AVR rechnen kann und habe auch 
schon ein Testprogramm für eine Floatmultiplikation geschrieben. Doch 
wenn ich die Compileroptimierung einschalte, dann wird meine Berechnung 
wegoptimiert. Wie kann ich aber trotzdem herausfinden wie schnell mein 
AVR mit eingeschalteter Optimierung rechnet? Vielleicht ist diese Frage 
für einige trivial, aber ich bin noch Anfänger .....

Hier noch mein Testprogramm
1
int main(void)
2
{
3
    DDRA  = 0xFF;
4
    PORTA = 0x00;
5
    float x = 9876.54321;
6
    float y = 1234.56789;
7
    float erg = 0;
8
    uint8_t i = 0;
9
  
10
    while(1)
11
    {
12
        PORTA |= (1 << PINA0);
13
        for (i = 0; i < 100; i++)
14
        {
15
            erg = x * y;
16
        }  
17
        PORTA &= ~(1 << PINA0);   
18
    }
19
}

von Karl H. (kbuchegg)


Lesenswert?

Mit ein paar eingestreuten 'volatiles' kannst du den Compiler bei 
Optimierungen 'zurückpfeifen'.

FAQ: Was hat es mit volatile auf sich

von ber l. (berluska)


Lesenswert?

WOW! Das ging aber schnell :)

Habe jetzt das Programm mit volatile geändert. Doch jetzt bin ich 
verwirrter als vorher. Irgendwie ist mir das Ergebnis nicht ganz klar.
Folgende Szenarien habe ich getestet:

Optimierung most: -O3

float x,y
float erg
=> Dauer: 2µs

float x,y
volatile float erg
=> Dauer: 1,1ms

volatile float x,y
volatile float erg
=> Dauer: 38,0ms

volatile float x,y
float erg
=> Dauer: 1,9ms

Wie lange dauert denn nun eine float Multiplikation auf einem AVR? Gibt 
es dafür vielleicht ein standadisiertes Messverfahren? z.B. einen AVR 
Speedtest?

von Purzel H. (hacky)


Lesenswert?

Der compiler koennte auch smart sein. Eine Multiplikation mit 2^N, resp 
2^-N ist auch nur ein Shift, dh ein Exponent. Daher sollte man die Daten 
per Kommunikation einspeisen.

von asdfs (Gast)


Lesenswert?

Siebzehn mal Fuenfzehn schrieb:
> Daher sollte man die Daten
> per Kommunikation einspeisen.

Oder aus dem EEPROM holen

von Karl H. (kbuchegg)


Lesenswert?

ber luska schrieb:

> verwirrter als vorher. Irgendwie ist mir das Ergebnis nicht ganz klar.

Du musst dir im klaren sein, welche Optimierungen du dem COmpiler 
jeweils offen lässt. Und du musst dir im klaren sein, dass der COmpiler 
jede Möglichkeit ausnutzen wird, die du ihm lässt.

In
1
    float x = 9876.54321;
2
    float y = 1234.56789;
3
    volatile float erg = 0;
4
5
...
6
            erg = x * y;
... muss da tatsächlich multipliziert werden? x ändert sich nicht, y 
ändert sich nicht, die Zahlenwerte sind bekannt. D.h. der Compiler kann 
das Ergebnis für x*y bereits ausrechnen und das ganze kürzen zu
1
      erg = 12193263.1112635269;

d.h. wenn der Compiler diese Optimierung gemacht hat, was man im 
Assembler-Code sehen würde) dann hast du wegen den volatile erg 
gemessen, wie lange 100 float Zuweisungen dauern.

von amateur (Gast)


Lesenswert?

Der Compiler ist nicht völlig Doof. Bei deinem ersten Beispiel "merkt" 
er, dass Du zwar rechnest, das Ergebnis aber nicht verwendest: Also raus 
damit.

Die Laufzeit von Fließkommaberechnungen ist ganz schwer einzuschätzen, 
da sie zum Teil von den Zahlen selbst abhängt und natürlich vom Typ der 
Operation.

Verglichen mit normaler Ganzzahlarithmetik heiß das: Ewig und drei Tage 
bzw. vermeide sie.

von ber l. (berluska)


Lesenswert?

Zusammenfassend kann ich also sagen, dass mein AVR für eine float 
Multiplikation mit den oben gewählten Zahlen 380µs benötigt?!?

von Karl H. (kbuchegg)


Lesenswert?

ber luska schrieb:
> Zusammenfassend kann ich also sagen, dass mein AVR für eine float
> Multiplikation mit den oben gewählten Zahlen 380µs benötigt?!?

So ungefähr.
Denn du hast ja nicht nur die Multiplikation gemessen.
Du hast ja auch gemessen, wie lange die Zuweisung dauert und in der Zeit 
ist auch die Schleifensteuerung der for-Schleife drinnen.

von Yalu X. (yalu) (Moderator)


Lesenswert?

ber luska schrieb:
> Zusammenfassend kann ich also sagen, dass mein AVR für eine float
> Multiplikation mit den oben gewählten Zahlen 380µs benötigt?!?

Im Prinzip ja.

Wenn du es ganz genau haben möchtest, musst du noch den Overhead durch 
die Schleife und die RAM-Zugriffe für die drei Variablen herausrechnen. 
Das geht folgendermaßen:
1
#include <stdint.h>
2
#include <avr/io.h>
3
4
int main(void)
5
{
6
  DDRA  = 0xFF;
7
  PORTA = 0x00;
8
  volatile float x = 9876.54321;
9
  volatile float y = 1234.56789;
10
  volatile float erg;
11
  uint8_t i;
12
13
  while(1)
14
  {
15
    PORTA |= (1 << PINA0);
16
    for (i = 0; i < 100; i++)
17
    {
18
      erg = x * y;
19
    }  
20
    PORTA &= ~(1 << PINA0);   
21
22
    PORTA |= (1 << PINA1);
23
    for (i = 0; i < 100; i++)
24
    {
25
      erg = x , y;
26
    }  
27
    PORTA &= ~(1 << PINA1);   
28
  }
29
}

Die Dauer der reinen Multiplikation (also die Ausführung von call 
__mulsf3) ist

Edit:

Die Zeit von 380µs erscheint mir etwas lang. Welchen AVR und welche 
Taktfrequenz verwendest du? Hast du die Option -lm angegeben (bei 
AVR-GCC 4.8 nicht mehr nötig)?

von ber l. (berluska)


Lesenswert?

Danke Yalu X. für die professionelle Antwort. Ich habe hier nur einen 
kleinen ATtiny der mit einem internen Oszi auf 8MHz läuft.
Sollte dieser wohl schneller rechnen? Bin leider noch nicht ganz so tief 
in der Materie. Was sich aber bald ändern wird, bei soviel Kompetenz 
hier im Board :)

Noch 2 Fragen auf die ich keine Antwort im Netz fand:
Was macht die Codezeile erg = x , y; Im Debugger wird erg nur der Wert 
von x zugewiesen.

Was bewirkt die Compileroption -lm? Verwende Atmel Studio 6.1

von Purzel H. (hacky)


Lesenswert?

Fuer eine Steuerung sollte man Floating point vermeiden, fuer ein 
langsames Benutzerinterface ist Float akzeptierbar

von M. N. (Gast)


Lesenswert?

Siebzehn mal Fuenfzehn schrieb:
> Fuer eine Steuerung sollte man Floating point vermeiden, fuer ein
> langsames Benutzerinterface ist Float akzeptierbar

Immer wieder die gleichen Vorurteile.
Als Faustformel für FMUL und FDIV auf einem AVR mit 16MHz nehme ich 
20-30µs an. Von der Ausführungszeit besteht kein großer Unterschied ob 
man MUL mit zwei int32_t oder zwei float-Varialen rechnen läßt, sofern 
man die Sonderfälle wie 0, 1 oder -1 als Multipli/-kator -kant mal außen 
vor läßt.

In der Codesammlung iat ein Beitrag 
Beitrag "Frequenz / Drehzahl, 4-stell. 7-Segm.-LCD, ATtiny45" wo ich zunächst mit 
int32_t gerechnet habe, weil ich dachte, float würde zu umfangreich 
werden.
Zuletzt habe ich dann doch float-Berechnungen genommen, die wesentlich 
einfacher und transparenter sind.

Fazit: wenn man float braucht, soll man auch float verwenden.
Beim AVR-Studio muß man unter "Project -> configuration options -> 
Libraries" die "libm.a" auswählen. Dann wird der Code nicht wesentlich 
vergrößert, sodass er auch auf ATtinyxx paßt.

von Yalu X. (yalu) (Moderator)


Lesenswert?

ber luska schrieb:
> Ich habe hier nur einen kleinen ATtiny der mit einem internen Oszi auf
> 8MHz läuft.

Ok, der ATtiny hat keinen Multiplizierer an Bord, dann darf er auch 
etwas länger brauchen. Ein ATmega sollte die Multiplikation bei gleicher 
Taktfrequenz ein ganzes Stück schneller als ein ATtiny ausführen.

> Sollte dieser wohl schneller rechnen? Bin leider noch nicht ganz so tief
> in der Materie. Was sich aber bald ändern wird, bei soviel Kompetenz
> hier im Board :)
>
> Noch 2 Fragen auf die ich keine Antwort im Netz fand:
> Was macht die Codezeile erg = x , y; Im Debugger wird erg nur der Wert
> von x zugewiesen.

Der Kommaoperator wertet zuerst den linken Operanden (in diesem Fall x) 
aus, dann den rechten (y). Das Gesamtergebnis ist das Ergebnis des 
rechten Operanden, das Ergebnis des linken Operanden wird 
weggeschmissen.

In diesem Fall soll einfach nur erreicht werden, dass x und y gelesen 
und erg geschrieben wird. Bei erg=x*y passiert genau das Gleiche, nur 
dass zusätzlich noch die Multiplikation ausgeführt wird. Die Differenz 
der beiden Ausführungszeiten ist somit die für die Multiplikation 
benötigte Zeit. Auch der Schleifenoverhead ist in beiden Fällen 
derselbe, so dass durch die Defferenzbildung auch dieser herausgerechnet 
wird.

> Was bewirkt die Compileroption -lm? Verwende Atmel Studio 6.1

Es gibt in der AVR-GCC-Toolchain zwei Bibliotheken mit 
FP-Grundrechenarten: Eine wird mit dem GCC mitgeliefert und eine mit der 
AVR-Libc. Letztere ist stark optimiert, erstere nicht. Die 
Ausführungszeiten und der Programmspeicherplatzbedarf sind bei der 
AVR-Libc-Variante deutlich geringer. Die AVR-Libc-FP-Bibliothek kommt 
aber bis GCC 4.6.x und teilweise 4.7.x nur durch explizites Linken der 
libm zum Einsatz, und das geschieht durch die Angabe von -lm im 
GCC-Aufruf. Werden Funktionen wie sqrt, sin, log usw. benutzt, braucht 
man die libm (also -lm) sowieso, da diese Funktionen in der 
GCC_Bibliothek nicht vorhanden sind.

Ab AVR-GCC 4.8 werden die FP-Routinen defaultmäßig aus der AVR-Libc 
genommen, weswegen hier das -lm nur dann benötigt wird, wenn man 
zusätzlich zu den Grundrechenarten die o. g. mathematischen Funktionen 
verwendet.

Je nach Entwicklungsumgebung wird die Verwendung der libm evtl. anders 
eingestellt, siehe hier:

M. N. schrieb:
> Beim AVR-Studio muß man unter "Project -> configuration options ->
> Libraries" die "libm.a" auswählen. Dann wird der Code nicht wesentlich
> vergrößert, sodass er auch auf ATtinyxx paßt.

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.