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); } }
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
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
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
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
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
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
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
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.
Auf einem Mega 8 dauert eine Multiplikation in C: 8 bit: 2 Zyklen 16 bit: 9 Zyklen 32 bit: 37 Zyklen Peter
Hallo Peter, wie kommst Du auf diese Zyklen? Vorallem bei den 32 bit, gibt es irgendwo eine Referenz. Gruss Marc
@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
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.