mikrocontroller.net

Forum: Compiler & IDEs Multiplikation ist zu schnell...


Autor: Marc Vollmer (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich habe mal ein Projekt angehängt (Zusammbau aus einigen gefundenen
Funktionen), im main mulitpliziere ich zwei doubles. Ich möchte nun
gerne wissen, wie lange diese Berechnung dauert. Also habe ich einen
Timer initialisiert und lese den Wert vor und nach der Berechnung aus.

Ich bekomme aber als Ergebnis, daß die Berechnung nur zwei Takte
benötigt, und dies kann bei einem 8 Bit Prozessor eigentlich nicht
sein. Wo ist denn mein Denkfehler?

-- Hier der relevante Ausschnitt --

int main(void)
{
    double a1=1.234, a2=2.756, a=0.0;
    int i;
    u08 data;
    u16 ticks1,ticks2;
    timer_init();                   /* init timer1 */
    UART_Init();                    /* Initialise UART */
    PRINT("Hello World !");
    EOL();
    for (;;) {  /* loop forever */
  ticks1 = TCNT1;   /* start measurement */
  //for(i=0;i<1000;i++)
    a = a1 * a2;
        ticks2 = TCNT1;   /* start measurement */
  UartPrintF("Ticks1? %u\r\n",ticks1);
  UartPrintF("Ticks2? %u\r\n",ticks2);
  UartPrintF("dTicks? %u us\r\n",(ticks2-ticks1)/4);
    }
}

Autor: Matthias (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

kann schon sein da der Compiler das wohl rausoptimiert. Da du
anscheinend den AVRGCC verwendest:

Schau dir mal mit

avr-objdump -t -h -S out.elf >out.lst (Dateinamen anpassen)

den ASM-Output an.

Matthias

Autor: Marc Vollmer (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Matthias,

eigentlich sehe ich in der *.lst nichts, da keine ASMs hinter der
Berechung folgen. Siehe Zeile 280.

Wo sind denn dann die Befehle für die Berechnung?

Zumindest würde dies die schnelle Zeitmessung erklären, aber wo ist das
Ergebnis?

Sorry, aber ich blicke dieses ASM nicht sonderlich, dies scheint auch
der Grund zu sein, warum ich in C programmiere.

Gruss
Marc

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du benutzt den Wert in A nie - also berechnet der Compiler ihn garnicht
erst.
Compiler sind faul - vor allem gcc. Das macht sie so menschlich ;-)

Definier mal a als volatile, d.h. gcc muss a berechnen, auch wenn er es
für unsinnig hält.

Aber auch dann kann es sein, dass gcc a1 und a2 als Konstanten erkennt
und die Multiplikation garnicht ausführt, man muss ziemlich tricksen,
um gcc sinnlos malochen zu lassen.

Stefan

Autor: Marc Vollmer (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Stefan,

ich habe a mal über die Schnittstelle ausgegeben:

--
for(i=0;i<1;i++) {
    a = a1 * a2;
    a16 = (unsigned int)(a*1000);
}
ticks2 = TCNT1;   /* start measurement */
UartPrintF("a16? %u\r\n",a16);
--

Das Ergebnis ist richtig, und der Timer gibt mir raus, daß er nun 6
Takte brauch. Eigentlich immer noch zu wenig, da nun noch ein for
Anweisung und eine Umrechnung drin ist.

Gruss
Marc

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi Mark,

ich habe mal den asm-Code rausgeschnitte, der Deine Berechnung macht,
den anderen Kruscht habe ich weggelassen, damits klarer wird:

  47:mv_gcctest9.c ****       a = a1 * a2;
  48:mv_gcctest9.c ****       a16 = (unsigned
 ...
 158 0038 88E4          ldi r24,lo8(3400)
 159 003a 9DE0          ldi r25,hi8(3400)
 ...
 166 0048 0E94 0000     call UartPrintF


gcc lädt a (sprich: R24/R25) mit 3400 und ruft UartPrintF auf. Sprich:
Deine Berechnung wird wegoptimiert ...

Stefan

Autor: Marc Vollmer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jetzt hab ich es verstanden, der Compiler hatte überhaupt keine
Veranlassung die Berechnung online auszuführen zu lassen, da sich der
Wert nicht änderte.

Nun habe ich die Routine etwas geändert:

double i;
for(i=0;i<1;i++) {
    a = a1 * i;
    a16 = (unsigned int)(a*1000);
}

Jetzt mußte der uP rechnen, da er das Ergebnis nicht vorbestimmen
konnte. Und siehe da, jetzt brauch die Berechnung 1068 Takte, bei einem
4 Mhz sind dies 267 us.  Schade eigentlich :-)

Danke an alle für die interessante Diskussion.

Gruss
Marc

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, so habe ich mir das gedacht.

Wenn Du nach der Zeit optimieren willst, dann solltest Du als erstes
die Floats rausschmeissen. Meistens sind die garnicht nötig:

z.B. kannst Du statt 4,36V oft genausogut mit 4360mV rechnen, und schon
ist es eine "normale" Integer.

Je tiefer Du dich in Deine Zahlen reinversetzt, desto mehr Arbeit
kannst Du der CPU ersparen. Denke immer daran, dass Deine Zahlen im
Binärsystem gespeichert werden. Im Int-Bereich ist z.B *2 oder /2 ein
einfaches shiften, /256 bedeutet Weglassen des niederwertigsten Bytes
(shift rechts 8 Bits), dagegen /100 richtig viel Rechenarbeit.

Stefan

Autor: Joerg Wunsch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Warum schade?  Andere Microcontroller-Compiler haben gar kein
Gleitkomma erst...

Übrigens hängt die Ausführungszeit eine Multiplikation (und noch mehr
einer Divsion) nicht unerheblich von den Operanden ab.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Auf einem Mega 8 dauert eine Multiplikation in C:

8 bit: 2 Zyklen
16 bit: 9 Zyklen
32 bit: 37 Zyklen


Peter

Autor: Marc Vollmer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Peter,

wie kommst Du auf diese Zyklen? Vorallem bei den 32 bit, gibt es
irgendwo eine Referenz.

Gruss
Marc

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Marc,

ich habe einfach im Assemblerlisting die Zyklen gezählt:

<__mulsi3>:
mul     r22, r18
movw    r26, r0
mul     r23, r19
movw    r30, r0
mul     r24, r18
add     r30, r0
adc     r31, r1
mul     r22, r20
add     r30, r0
adc     r31, r1
mul     r25, r18
add     r31, r0
mul     r24, r19
add     r31, r0
mul     r23, r20
add     r31, r0
mul     r22, r21
add     r31, r0
eor     r25, r25
mul     r23, r18
add     r27, r0
adc     r30, r1
adc     r31, r25
mul     r22, r19
add     r27, r0
adc     r30, r1
adc     r31, r25


Peter

Autor: Michael (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn man in C programmiert, können floats auch von Vorteil sein: keine
fehlerträchtige Skalierung, um die Auflösung zu 'retten'. Wenn der
Compiler nicht die ATmega-MUL-Befehle unterstützt, kann die
float-Berechnung auch schneller sein als 32-bit Integer. Die Mantisse
ist ja nur 24-bit breit. Bei der Division kommt dies auf jeden Fall zum
tragen.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.