ArithmetikBenchmark

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche

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
  1. 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]

Weblinks