(→Berechnung mit rekursiver Funktion) |
(→Zusammenfassung) |
||
(31 dazwischenliegende Versionen von 5 Benutzern werden nicht angezeigt) | |||
Zeile 1: | Zeile 1: | ||
Ein wichtiges Merkmal eines [[Compiler]]s ist die Güte des erzeugten Codes. | Ein wichtiges Merkmal eines [[Compiler]]s ist die Güte des erzeugten Codes. | ||
− | Immerhin | + | Immerhin soll die Hardware optimal genutzt werden, und die geschriebenen Programme sollen möglichst wenig Laufzeit brauchen und möglichst wenig Speicher – also RAM und [[Flash]] – belegen. |
{{Ausbauwunsch|Bascom Beispiele}} | {{Ausbauwunsch|Bascom Beispiele}} | ||
Zeile 8: | Zeile 8: | ||
Dieser Artikel versucht ansatzweise einen Codevergleich weit verbreiteter [[AVR]]-Compiler anhand sehr einfacher Aufgaben, die "geradeaus" und ohne Umschweife programmiert wurden. | Dieser Artikel versucht ansatzweise einen Codevergleich weit verbreiteter [[AVR]]-Compiler anhand sehr einfacher Aufgaben, die "geradeaus" und ohne Umschweife programmiert wurden. | ||
− | Ein Vergleich der Programmierung von Hardware-Komponenten und Peripherie wie [[Timer]]-, [[UART]]- oder [[I2C]]-Module scheint dabei weniger interessant, denn obwohl die Codes zum Steuern dieser Komponente in unterschiedlichen Sprachen recht verschieden aussehen, werden sie doch auf die selben Maschinen-Codes abgebildet, die sich im wesentlichen auf das Setzen und Lesen von Registern (SFRs) reduzieren. | + | Ein Vergleich der Programmierung von Hardware-Komponenten und Peripherie wie [[Timer]]-, [[UART]]- oder [[I2C]]-Module scheint dabei weniger interessant, denn obwohl die Codes zum Steuern dieser Komponente in unterschiedlichen Sprachen recht verschieden aussehen, werden sie doch auf die selben Maschinen-Codes abgebildet, die sich im wesentlichen auf das Setzen und Lesen von Registern (SFRs) reduzieren. Der BASCOM Compiler hat allerdings gerade bei IO Aufgaben seine Stärken, wenn dafür vorgefertigte Funktionen existieren. |
Neben diesen für jedes Programm essenziellen Abschnitten besteht ein Programm aber zum großen Teil aus hardwareunabhängigen Aufgaben wie Registerverwaltung, Funktionsaufrufen, Schleifen, Abfragen, Zuweisungen, Parameterübergaben, Abfragen von Bedingungen, Arithmetik, Implementierung von [[Interrupt Service Routine]]n, etc. | Neben diesen für jedes Programm essenziellen Abschnitten besteht ein Programm aber zum großen Teil aus hardwareunabhängigen Aufgaben wie Registerverwaltung, Funktionsaufrufen, Schleifen, Abfragen, Zuweisungen, Parameterübergaben, Abfragen von Bedingungen, Arithmetik, Implementierung von [[Interrupt Service Routine]]n, etc. | ||
Zeile 19: | Zeile 19: | ||
;[[avr-gcc]]: Die Assembler-Ausgaben wurden mit der Optimierungsstufe "auf Größe optimieren" (<tt>-Os</tt>) für einen [[ATmega8]] erstellt. [[GCC]]-Version war 3.4.x. Andere Optimierungsstufen haben wenig bis keinen Einfluss auf den erzeugtenCode. Codes für andere Controller der ATmega-Familie unterscheiden sich praktisch nicht vom ATmega8-Code. | ;[[avr-gcc]]: Die Assembler-Ausgaben wurden mit der Optimierungsstufe "auf Größe optimieren" (<tt>-Os</tt>) für einen [[ATmega8]] erstellt. [[GCC]]-Version war 3.4.x. Andere Optimierungsstufen haben wenig bis keinen Einfluss auf den erzeugtenCode. Codes für andere Controller der ATmega-Familie unterscheiden sich praktisch nicht vom ATmega8-Code. | ||
− | :Die Assembler-Dumps wurden erzeugt mit | + | :Die acr-gcc Assembler-Dumps wurden erzeugt mit |
:{| | :{| | ||
|- | |- | ||
Zeile 30: | Zeile 30: | ||
;[[Bascom]]: Das einzige Assembler-Code-Listing von Bascom (Aufsummieren in einer Schleife) wurde von PicNick mit einem selbstgestrickten *.HEX File Analyzer erstellt, da der Disassembler von AVR-Studio etwas schwer lesbar ist. | ;[[Bascom]]: Das einzige Assembler-Code-Listing von Bascom (Aufsummieren in einer Schleife) wurde von PicNick mit einem selbstgestrickten *.HEX File Analyzer erstellt, da der Disassembler von AVR-Studio etwas schwer lesbar ist. | ||
+ | |||
+ | ;[[Keil-ARM7]]: Es wurden einige weitere Codezeilen für ein RISC ARM7 System (LPC2138) mit Keil Compiler jeweils unter die gcc-avr Varianten gestellt um auch hier einen Vergleich außerhalb der Konkurrenz gcc-avr/Bascom ziehen zu können. Die Optimierung lag auf 'speed' und Registeroptimierung. Man sieht gewisse Ähnlichkeiten zwischen gcc-avr und Keil-Arm7, im Detail sind die Aufgaben jedoch unterschiedlich gelöst. Auf Grund von deutlichen Unterschieden z.B. im Interrupt Handling sind die Codebeispiele teilweise nicht 1:1 vergleichbar. Die Codebeispiele sind ebenfalls noch nicht loktierte / gelinkte Objekte. Siehe oben. | ||
= Summe der ersten ''n'' Zahlen = | = Summe der ersten ''n'' Zahlen = | ||
Zeile 255: | Zeile 257: | ||
| | | | ||
<pre> | <pre> | ||
− | + | Function Summe(byval N As Integer) As Integer | |
+ | Local M As Integer | ||
+ | M = 0 | ||
+ | If N > 1 Then | ||
+ | N = N - 1 | ||
+ | M = Summe(n ) | ||
+ | End If | ||
+ | Summe = N + M | ||
+ | End Function | ||
</pre> | </pre> | ||
+ | Damit das Programm auch richtig läuft muß der Stackbereich deutlich erweitert werden. | ||
}} | }} | ||
Zeile 323: | Zeile 334: | ||
'''Quellcodes:''' | '''Quellcodes:''' | ||
+ | {{Codevergleich|avr-gcc|BASCOM | ||
+ | | | ||
+ | <pre> | ||
+ | unsigned int | ||
+ | sum_n_formel (unsigned int n) | ||
+ | { | ||
+ | return n*(n+1) / 2; | ||
+ | } | ||
+ | </pre> | ||
+ | | | ||
+ | <pre> | ||
+ | Bascom mag keine Formeln. Diese müssen erst zerlegt werden. | ||
+ | Ob diese Art der Berechnug schlechter ist kann so nicht gesagt | ||
+ | werden. Auf jedem Fall ist diese Methode nicht elegant und | ||
+ | ziemlich unübersichtlich, da der Zusammenhang verloren geht. | ||
+ | |||
+ | Declare Function Sum_n_formel(byval I As Integer) As Integer | ||
+ | |||
+ | Dim X As Integer | ||
+ | |||
+ | 'Start | ||
+ | |||
+ | X = Sum_n_formel(10) | ||
+ | End | ||
+ | |||
+ | ' ----- | ||
+ | |||
+ | Function Sum_n_formel(byval I As Integer) | ||
+ | |||
+ | X = I + 1 '(n+1) | ||
+ | X = X * I 'n * (n+1) | ||
+ | |||
+ | Sum_n_formel = X / 2 'n * (n+1) / 2 | ||
+ | |||
+ | End Function | ||
+ | |||
+ | </pre> | ||
+ | }} | ||
+ | |||
{{Codevergleich|avr-gcc|BASCOM | {{Codevergleich|avr-gcc|BASCOM | ||
Zeile 347: | Zeile 397: | ||
16: ror r24 | 16: ror r24 | ||
18: ret | 18: ret | ||
+ | </pre> | ||
+ | Es geht auch mit deutlich weniger Registern im ARM7 | ||
+ | <pre> | ||
+ | *** CODE SEGMENT '?PR?sum_n_formel?A?main': | ||
+ | 284: sum_n_formel (unsigned int n) | ||
+ | 00 MOV R2,R0 ; n | ||
+ | 286: return n*(n+1) / 2; | ||
+ | 04 MOV R1,R2 ; n | ||
+ | 08 ADD R1,R1,#0x0001 ; n | ||
+ | 0C MOV R0,R2 ; n | ||
+ | 10 MUL R0,R1,R0 | ||
+ | 14 MOV R0,R0,LSR #1 | ||
+ | 287: } | ||
+ | 18 BX R14 | ||
</pre> | </pre> | ||
| | | | ||
Zeile 376: | Zeile 440: | ||
| | | | ||
<pre> | <pre> | ||
+ | $regfile = "m8def.dat" | ||
+ | |||
+ | Dim Count As Integer | ||
+ | |||
+ | On Int0 Int0_int ' Initialise the INT0 Interrupt | ||
+ | Enable Int0 ' enable the interrupt | ||
+ | Enable Interrupts | ||
+ | |||
+ | Do | ||
+ | Loop | ||
+ | |||
+ | Int0_int: ' The interrupt Handler for Int0 | ||
+ | Incr Count | ||
+ | Return | ||
</pre> | </pre> | ||
}} | }} | ||
Zeile 407: | Zeile 485: | ||
reti | reti | ||
</pre> | </pre> | ||
+ | Variable holen, addieren, wegschreiben.. den Rest macht der ARM7 selbst. Viel interessanter ist, das der ARM den Counter in ein Register kriegt, der AVR aber über 2 Register rechnen muss (r24 und r25). Dafür muss aber der Arm 2 LDR ausführen um an die Countervariable zu kommen. | ||
+ | <pre> | ||
+ | *** CODE SEGMENT '?PR?ISR?A?main': | ||
+ | 288: count++; | ||
+ | 00 LDR R0,=count ; count | ||
+ | 04 LDR R1,[R0,#0x0] ; count | ||
+ | 08 ADD R1,R1,#0x0001 | ||
+ | 0C STR R1,[R0,#0x0] ; count | ||
+ | 10 BX R14 | ||
+ | </pre> | ||
+ | Der Code ist auf Grund der Architektur nicht direkt vergleichbar mit dem AVR. Der Arm 7 besitzt einen eigenen Interrupt Stack, Befehle um ganze Registersätze zu popen/puschen, einen Vectorinterruptcontroller und User/Supervisor Prioritätsebenen, ähnlich der einer x86 CPU. Das wäre als wolle man avr-gcc und Bascom vergleichen :) | ||
| | | | ||
<pre> | <pre> | ||
+ | PUSH R0 ;Push register on stack | ||
+ | PUSH R1 | ||
+ | PUSH R2 | ||
+ | PUSH R3 | ||
+ | PUSH R4 | ||
+ | PUSH R5 | ||
+ | PUSH R7 | ||
+ | PUSH R10 | ||
+ | PUSH R11 | ||
+ | PUSH R16 | ||
+ | PUSH R17 | ||
+ | PUSH R18 | ||
+ | PUSH R19 | ||
+ | PUSH R20 | ||
+ | PUSH R21 | ||
+ | PUSH R22 | ||
+ | PUSH R23 | ||
+ | PUSH R24 | ||
+ | PUSH R25 | ||
+ | PUSH R26 | ||
+ | PUSH R27 | ||
+ | PUSH R28 | ||
+ | PUSH R29 | ||
+ | PUSH R30 | ||
+ | PUSH R31 | ||
+ | IN R24,0x3F ; get SREG | ||
+ | PUSH R24 | ||
+ | LDI R26,0x00 ;Load immediate | ||
+ | LDI R27,0x01 ;Load immediate | ||
+ | RCALL PC+0x001D ;Relative call sub_inc: | ||
+ | POP R24 ;Pop register from stack | ||
+ | OUT 0x3F,R24 ;Out SREG | ||
+ | POP R31 | ||
+ | POP R30 | ||
+ | POP R29 | ||
+ | POP R28 | ||
+ | POP R27 | ||
+ | POP R26 | ||
+ | POP R25 | ||
+ | POP R24 | ||
+ | POP R23 | ||
+ | POP R22 | ||
+ | POP R21 | ||
+ | POP R20 | ||
+ | POP R19 | ||
+ | POP R18 | ||
+ | POP R17 | ||
+ | POP R16 | ||
+ | POP R11 | ||
+ | POP R10 | ||
+ | POP R7 | ||
+ | POP R5 | ||
+ | POP R4 | ||
+ | POP R3 | ||
+ | POP R2 | ||
+ | POP R1 | ||
+ | POP R0 | ||
+ | RETI | ||
+ | |||
+ | sub_inc: | ||
+ | LD R30,X+ ;Load indirect and postincrement | ||
+ | LD R31,X ;Load indirect | ||
+ | SUBI R30,0xFF ;Subtract immediate | ||
+ | SBCI R31,0xFF ;Subtract immediate with carry | ||
+ | ST X,R31 ;Store indirect | ||
+ | ST -X,R30 ;Store indirect and predecrement | ||
+ | RET ;Subroutine return | ||
</pre> | </pre> | ||
}} | }} | ||
Zeile 478: | Zeile 634: | ||
reti | reti | ||
</pre> | </pre> | ||
+ | Und hier der Arm 7 Code. | ||
+ | <pre> | ||
+ | *** CODE SEGMENT '?PR?ISR?A?main': | ||
+ | 285: void ISR( void ) | ||
+ | 00 STMDB R13!,{LR} | ||
+ | 287: foo(); | ||
+ | 04 BL foo?A ; Targ=0x0 | ||
+ | 08 LDMIA R13!,{R3} | ||
+ | 0C BX R3 | ||
+ | </pre> | ||
+ | Der Code ist auf Grund der Architektur nicht direkt vergleichbar mit dem AVR. | ||
+ | Der Arm 7 besitzt einen eigenen Interrupt Stack, Befehle um ganze Registersätze zu popen/puschen, einen Vectorinterruptcontroller und User/Supervisor Prioritätsebenen, ähnlich der einer x86 CPU. | ||
+ | Das wäre als wolle man avr-gcc und Bascom vergleichen :) | ||
| | | | ||
<pre> | <pre> | ||
Zeile 596: | Zeile 765: | ||
3a: ret | 3a: ret | ||
} | } | ||
+ | </pre> | ||
+ | Und hier das Ganze noch mal als ARM7 Code | ||
+ | <pre> | ||
+ | *** CODE SEGMENT '?PR?bubble_sort?A?main': | ||
+ | 285: void bubble_sort (uint8_t n, char * a) | ||
+ | 00 STMDB R13!,{R4-R6} | ||
+ | 04 MOV R3,R0 ; n | ||
+ | 286: { | ||
+ | 290: for (i = n; i >= 2; i--) | ||
+ | 08 MOV R0,R3 ; n | ||
+ | 0C B L_11 ; Targ=0xD4 | ||
+ | 10 L_12: | ||
+ | 291: { | ||
+ | 292: char max = CHAR_MIN; | ||
+ | 10 MOV R3,#0x0 | ||
+ | 14 MOV R6,R3 ; max | ||
+ | 296: for (j = 0; j < i; j++) | ||
+ | 18 B L_16 ; Targ=0x64 | ||
+ | 1C L_17: | ||
+ | 298: if (a[j] >= max) | ||
+ | 1C MOV R4,R3 ; j | ||
+ | 20 MOV R5,R4,LSL #24 ; j | ||
+ | 24 MOV R5,R5,LSR #24 | ||
+ | 28 MOV R4,R1 ; a | ||
+ | 2C LDRB R4,[R4,+R5] | ||
+ | 30 MOV R5,R6 ; max | ||
+ | 34 MOV R5,R5,LSL #24 ; max | ||
+ | 38 MOV R5,R5,LSR #24 | ||
+ | 3C CMP R4,R5 | ||
+ | 40 BCC L_14 ; Targ=0x5C | ||
+ | 301: j_max = j; | ||
+ | 44 MOV R2,R3 ; j | ||
+ | 302: max = a[j_max]; | ||
+ | 48 MOV R4,R2 ; j_max | ||
+ | 4C MOV R5,R4,LSL #24 ; j_max | ||
+ | 50 MOV R5,R5,LSR #24 | ||
+ | 54 MOV R4,R1 ; a | ||
+ | 58 LDRB R6,[R4,+R5] | ||
+ | 304: } | ||
+ | 5C L_14: | ||
+ | 5C ADD R3,R3,#0x0001 ; j | ||
+ | 60 AND R3,R3,#0x00FF | ||
+ | 64 L_16: | ||
+ | 64 MOV R4,R0 ; i | ||
+ | 68 MOV R5,R4,LSL #24 ; i | ||
+ | 6C MOV R5,R5,LSR #24 | ||
+ | 70 MOVR R4,R3 ; j | ||
+ | 74 MOV R4,R4,LSL #24 ; j | ||
+ | 78 MOV R4,R4,LSR #24 | ||
+ | 7C CMP R4,R5 | ||
+ | 80 BCC L_17 ; Targ=0x1C | ||
+ | 307: a[j_max] = a[i-1]; | ||
+ | 84 MOV R3,R0 ; i | ||
+ | 88 MOV R4,R3,LSL #24 ; i | ||
+ | 8C MOV R4,R4,LSR #24 | ||
+ | 90 MOV R3,R1 ; a | ||
+ | 94 ADD R3,R3,R4 ; a | ||
+ | 98 LDRB R3,[R3,#0xFFFFFFFF] | ||
+ | 9C MOV R4,R2 ; j_max | ||
+ | A0 MOV R5,R4,LSL #24 ; j_max | ||
+ | A4 MOV R5,R5,LSR #24 | ||
+ | A8 MOV R4,R1 ; a | ||
+ | AC STRB R3,[R4,+R5] | ||
+ | 308: a[i-1] = max; | ||
+ | B0 MOV R3,R6 ; max | ||
+ | B4 MOV R4,R0 ; i | ||
+ | B8 MOV R5,R4,LSL #24 ; i | ||
+ | BC MOV R5,R5,LSR #24 | ||
+ | C0 MOV R4,R1 ; a | ||
+ | C4 ADD R4,R4,R5 ; a | ||
+ | C8 STRB R3,[R4,#0xFFFFFFFF] | ||
+ | 309: } | ||
+ | CC SUB R0,R0,#0x0001 ; i | ||
+ | D0 AND R0,R0,#0x00FF | ||
+ | D4 L_11: | ||
+ | D4 MOV R3,R0 ; i | ||
+ | D8 MOV R3,R3,LSL #24 ; i | ||
+ | DC MOV R3,R3,LSR #24 | ||
+ | E0 CMP R3,#0x0002 | ||
+ | E4 BGE L_12 ; Targ=0x10 | ||
+ | 310: } | ||
+ | E8 LDMIA R13!,{R4-R6} | ||
+ | EC BX R14 | ||
</pre> | </pre> | ||
| | | | ||
Zeile 603: | Zeile 855: | ||
Die Codes wurden etwas nachkommentiert und -formatiert, um besser den Zusammenhang mit der Quelle durchblicken zu können. | Die Codes wurden etwas nachkommentiert und -formatiert, um besser den Zusammenhang mit der Quelle durchblicken zu können. | ||
+ | |||
+ | = Zusammenfassung = | ||
+ | |||
+ | Für diese drei Summier-Routinen erhält man folgende Ergebnisse für Laufzeit und Platzverbrauch. Die Laufzeittest wurden gemacht für N=20, d.h. es wurden die ersten 20 ganzen Zahlen aufsummiert. | ||
+ | |||
+ | {| {{Blauetabelle}} | ||
+ | <!-------------------------------------------------------------------------------------> | ||
+ | |- {{Hintergrund1}} | ||
+ | ! Verbrauch an Flash (in Bytes) || avr-gcc || BASCOM | ||
+ | |- | ||
+ | |<tt>sum_n_loop</tt> || 20 || ca. 40 + 6 | ||
+ | |- | ||
+ | |<tt>sum_n_rekursiv</tt> || 24 ||ca. 188 ? | ||
+ | |- | ||
+ | |<tt>sum_n_formel</tt> || 26 || ca. 300 | ||
+ | |} | ||
+ | |||
+ | |||
+ | {| {{Blauetabelle}} | ||
+ | <!-------------------------------------------------------------------------------------> | ||
+ | |- {{Hintergrund1}} | ||
+ | ! Verbrauch an Zeit (in CPU-Zyklen) || avr-gcc || BASCOM | ||
+ | |- | ||
+ | |<tt>sum_n_loop (20)</tt> || 191 || ca. 1355 | ||
+ | |- | ||
+ | |<tt>sum_n_rekursiv (20)</tt> || 477 || ca. 2058 | ||
+ | |- | ||
+ | |<tt>sum_n_formel (20)</tt> || 19 || ca. 417 | ||
+ | |} | ||
+ | Die Formelversion ist in BASCOM so langsam, weil eine echte Division benutzt wird. Bei so einer kurzen Schleife sind die Vorteile durch die Optimierung von GCC besonders groß. Bei anderem Code wird der Unterschied eher kleiner ausfallen. Die Code-Größe enthält zum Teil vom Compiler bereitgestellte Unterroutinen (z.B. für die Division). Bei einem längeren Code taucht dieser Teil nur einmal auf. Die 2. Division wird entsprechend viel kürzer als die erste. | ||
=Siehe auch= | =Siehe auch= | ||
Zeile 609: | Zeile 891: | ||
* [[Bascom]] | * [[Bascom]] | ||
* [[AVR]] | * [[AVR]] | ||
+ | * [[ARM]] | ||
[[Kategorie:Quellcode Bascom]] | [[Kategorie:Quellcode Bascom]] |
Aktuelle Version vom 5. Juli 2011, 19:55 Uhr
Ein wichtiges Merkmal eines Compilers ist die Güte des erzeugten Codes. Immerhin soll die Hardware optimal genutzt werden, und die geschriebenen Programme sollen möglichst wenig Laufzeit brauchen und möglichst wenig Speicher – also RAM und Flash – belegen.
Dieser Artikel ist noch lange nicht vollständig. Der Auto/Initiator hofft das sich weitere User am Ausbau des Artikels beteiligen.
Das Ergänzen ist also ausdrücklich gewünscht! Besonders folgende Dinge würden noch fehlen: Bascom Beispiele |
Ein Vergleich der erzeugten Codes ist jedoch nicht einfach, denn ein Problem kann bereits innerhalb ein und der selben Programmiersprache auf sehr unterschiedliche Art und Weisen formuliert oder gelöst werden.
Dieser Artikel versucht ansatzweise einen Codevergleich weit verbreiteter AVR-Compiler anhand sehr einfacher Aufgaben, die "geradeaus" und ohne Umschweife programmiert wurden.
Ein Vergleich der Programmierung von Hardware-Komponenten und Peripherie wie Timer-, UART- oder I2C-Module scheint dabei weniger interessant, denn obwohl die Codes zum Steuern dieser Komponente in unterschiedlichen Sprachen recht verschieden aussehen, werden sie doch auf die selben Maschinen-Codes abgebildet, die sich im wesentlichen auf das Setzen und Lesen von Registern (SFRs) reduzieren. Der BASCOM Compiler hat allerdings gerade bei IO Aufgaben seine Stärken, wenn dafür vorgefertigte Funktionen existieren.
Neben diesen für jedes Programm essenziellen Abschnitten besteht ein Programm aber zum großen Teil aus hardwareunabhängigen Aufgaben wie Registerverwaltung, Funktionsaufrufen, Schleifen, Abfragen, Zuweisungen, Parameterübergaben, Abfragen von Bedingungen, Arithmetik, Implementierung von Interrupt Service Routinen, etc.
Interessanter erscheint ein Vergleich einfacher Aufgaben, die erkennen lassen, wie gut ein Compiler in der Lage ist, die Ressourcen eines Mikrocontrollers zu nutzen bzw. zu schonen.
Inhaltsverzeichnis
Rahmenbedingungen
- avr-gcc
- Die Assembler-Ausgaben wurden mit der Optimierungsstufe "auf Größe optimieren" (-Os) für einen ATmega8 erstellt. GCC-Version war 3.4.x. Andere Optimierungsstufen haben wenig bis keinen Einfluss auf den erzeugtenCode. Codes für andere Controller der ATmega-Familie unterscheiden sich praktisch nicht vom ATmega8-Code.
- Die acr-gcc Assembler-Dumps wurden erzeugt mit
> avr-gcc -mmcu=atmega8 -g -Os datei.c -c -o datei.o > avr-objdump -d -S datei.o
(Zu beachten ist, daß datei.o ein noch nicht loktiertes Objekt ist und daher Adressen noch mit Platzhaltern (üblicherweise 0) gefüllt sind und daher im Dump noch als 0 angezeigt werden.)
- Bascom
- Das einzige Assembler-Code-Listing von Bascom (Aufsummieren in einer Schleife) wurde von PicNick mit einem selbstgestrickten *.HEX File Analyzer erstellt, da der Disassembler von AVR-Studio etwas schwer lesbar ist.
- Keil-ARM7
- Es wurden einige weitere Codezeilen für ein RISC ARM7 System (LPC2138) mit Keil Compiler jeweils unter die gcc-avr Varianten gestellt um auch hier einen Vergleich außerhalb der Konkurrenz gcc-avr/Bascom ziehen zu können. Die Optimierung lag auf 'speed' und Registeroptimierung. Man sieht gewisse Ähnlichkeiten zwischen gcc-avr und Keil-Arm7, im Detail sind die Aufgaben jedoch unterschiedlich gelöst. Auf Grund von deutlichen Unterschieden z.B. im Interrupt Handling sind die Codebeispiele teilweise nicht 1:1 vergleichbar. Die Codebeispiele sind ebenfalls noch nicht loktierte / gelinkte Objekte. Siehe oben.
Summe der ersten n Zahlen
Berechnet wird die Summe der ersten n Zahlen:
- [math] \operatorname{sum}(n) \,=\, \sum_{k=1}^n k \,=\, 1 + 2 + \ldots + n [/math]
Die Zahl n wird als 16-Bit Zahl angegeben und das Ergebnis als 16-Bit-Zahl berechnet. Ein eventueller Überlauf wird nicht beachtet.
Der Code wird jeweils als eigene Funktion implementiert, um Abhängigkeiten vom umliegenden Code zu vermeiden.
Für diese Berechnung gibt es mehrere Möglichkeiten.
Aufsummieren in einer Schleife
Quellcodes:
|
|
Compilat:
|
|
Berechnung mit rekursiver Funktion
Quellcodes:
|
|
Compilat:
|
|
Berechnung durch Formel
Quellcodes:
|
|
|
|
Interrupt-Routinen
Auch diese Beispiele machen nicht viel. Das erste zählt nur eine 16-Bit Variable hoch, das zweite macht nichts weiter, als ein Funktionsaufruf.
Eine Variable hochzählen
Quellcodes:
|
|
Compilat:
|
|
Eine Funktion aufrufen
Quellcodes:
|
|
Compilat:
|
|
Sortieren mit Bubble-Sort
Zum Abschluss noch ein komplexeres Beispiel: Ein Array mit 8-Bit-Werten soll mit Bubble-Sort der Größe nach sortiert werden.
Im Array wird nach dem größten Wert gesucht und dieser ans Ende getauscht. Danach macht man den Teilbereich, in dem man das Maximum sucht, um 1 kleiner, bis man fertig ist. Die größten Zahlen wandern wie Blasen nach oben, daher der Name Bubble-Sort für diesen Sortier-Algorithmus.
Variablen:
- n Array-Größe
- a das Array
- i Größe der Teilbereichs
- j Laufvariable durch den Teilbereich
Quellcodes:
|
|
Compilat:
|
|
Die Codes wurden etwas nachkommentiert und -formatiert, um besser den Zusammenhang mit der Quelle durchblicken zu können.
Zusammenfassung
Für diese drei Summier-Routinen erhält man folgende Ergebnisse für Laufzeit und Platzverbrauch. Die Laufzeittest wurden gemacht für N=20, d.h. es wurden die ersten 20 ganzen Zahlen aufsummiert.
Verbrauch an Flash (in Bytes) | avr-gcc | BASCOM |
---|---|---|
sum_n_loop | 20 | ca. 40 + 6 |
sum_n_rekursiv | 24 | ca. 188 ? |
sum_n_formel | 26 | ca. 300 |
Verbrauch an Zeit (in CPU-Zyklen) | avr-gcc | BASCOM |
---|---|---|
sum_n_loop (20) | 191 | ca. 1355 |
sum_n_rekursiv (20) | 477 | ca. 2058 |
sum_n_formel (20) | 19 | ca. 417 |
Die Formelversion ist in BASCOM so langsam, weil eine echte Division benutzt wird. Bei so einer kurzen Schleife sind die Vorteile durch die Optimierung von GCC besonders groß. Bei anderem Code wird der Unterschied eher kleiner ausfallen. Die Code-Größe enthält zum Teil vom Compiler bereitgestellte Unterroutinen (z.B. für die Division). Bei einem längeren Code taucht dieser Teil nur einmal auf. Die 2. Division wird entsprechend viel kürzer als die erste.