ArithmetikBenchmark
Benchmarks der Arithmetik-Funktionen auf einem AVR
Im Netz fanden sich leider keine geeigneten Informationen, wie lange ein AVR für eine Berechnung mit Datentypen verschiedener Größe benötigt. Daher hier die Ergebnisse meines Benchmarkprogramms.
Ausgeführt wurde das Programm auf einem ATmega328p. Compiliert wurde mit avr-gcc 4.3.2 und avr-libc 1.6.2.cvs20080610-2. Das sind die Versionen, die bei einem aktuellem Debian (stand März 2010) mit geliefert werden. Compiliert wurde mit einer Optimierung für minimale Größe. Zum Messen der benötigten Ticks (CPU Zyklen) wurde der 16 Bit Timer1 verwendet. Um zu verhindern, dass die Berechnungen außerhalb des Start und Stopp Codes des Timers erfolgten, wurden die Variablen volatile deklariert. Dies erzeugt gegebenenfalls ein paar extra Ticks, die in der Praxis weg optimiert werden. Auch für das Starten und Stoppen des Timers werden ein paar Ticks zu viel gezählt. Insbesondere bei den 8 Bit Berechnungen ist dies deutlich zu sehen.
Gerechnet wurde hier mit den Beispielzahlen a = 123456789123450 und b = 512. Bei den Berechnungen mit weniger Bits, wurden die Zahlen entsprechend abgeschnitten. Insbesondere bei den Double Berechnungen hängt die Laufzeit auch von den verwendeten Werten ab, so dass die Werte hier eher als Richtwerte betrachtet werden müssen.
a, b, c sind immer vom gleichem Datentyp. Die verwendete Funktion gibt an, ob durch den Compiler eine vordefinierte Funktion aufgerufen wurde, oder aber direkter Assembler Code generiert wurde (Inline). Ersteres ist platzsparender, wenn die Verwendung mehrfach im Programm geschieht und der Code für die Berechnung relativ lang ist, letzteres gibt dem Compiler hingegen mehr Möglichkeiten für Optimierungen. Die Bits sind sizeof(datentyp)*8.
Ergebnisse Datentyp Bits Berechnung Verwendete Funktion Ticks[1] signed long long 64 a+b=c Inline 135 signed long long 64 a*b=c __muldi3 895 signed long long 64 a/b=c __divdi3 3990 unsigned long long 64 a+b=c Inline 135 unsigned long long 64 a*b=c __muldi3 895 unsigned long long 64 a/b=c __udivdi3 3715 signed long 32 a+b=c Inline 30 signed long 32 a*b=c __mulsi3 74 signed long 32 a/b=c __divmodsi4 637 unsigned long 32 a+b=c Inline 30 unsigned long 32 a*b=c __mulsi3 74 unsigned long 32 a/b=c __udivmodsi4 604 signed int 16 a+b=c Inline 16 signed int 16 a*b=c Inline 25 signed int 16 a/b=c __divmodhi4 245 signed char 8 a+b=c Inline 9 signed char 8 a*b=c Inline 12 signed char 8 a/b=c __divmodhi4 245 double 32 a+b=c __addsf3 804 double 32 a*b=c __mulsf3 1670 double 32 a/b=c __divsf3 1274
- ↑ Für Arithmetik, die vom Compiler direkt und ohne Funktionsaufruf in den Code engefügt wird, ist die Anzahl der Ticks im wesentlichen durch das Laden/Speichern der Daten bedingt und nicht durch die Arithmetik selbst. Grund dafür ist das volatile in den Testfällen. So ist eine long-Addition mit 30 Ticks angegeben, obwohl diese in Wirklichkeit nur 4−8 Ticks dauert.
Die Assemblerausgabe des Benchmark-Programms lässt sich mit
avr-gcc -S -mmcu=atmega328p -I. -gstabs -DF_CPU=16000000 -Os -Wall -Wstrict-prototypes -Wextra -std=gnu99 -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -ffunction-sections -fdata-sections -mcall-prologues -fsplit-wide-types benchmark.c -o benchmark.s
generieren. Beim Compilieren ist es ferner wichtig, dass die vollständige printf Funktionen verwendet werden, sonst werden bei den double Berechnungen nur '?' als Ergebnis angezeigt. An den gezählten Ticks ändert dies jedoch nichts. Siehe hierzu auch [1]
- Quellcode: Benchmark.c