Hallo Sprinter (schreibe mal hier)
In Formeln bin ich sehr schlecht, aber was hälst DU davon, wenn man dazu fixe Werte für die Variablen verwendet, einen Einheitlichen Prozessor (z.B. den Mega8) und einen Gleichbeleibenden Quarz. Dann könnte man die Codes auch untereinander in der Ausführungszeit und dem Speicherverbrauch vergleichen..? Mann müsste dann aber auch den kompletten Code aufzeigen... (für C alle Libs..)--Roberto 22:31, 6. Apr 2006 (CEST)
Die C-Codes habe ich für einen ATmega8 übersetzt. Die Einstellungen für den CPU-Takt haben keinen Einfluss auf die Codeerzeugung (oder etwa bei Bascom??). Den Flash-Verbrauch kann man abzählen oder anzeigen lassen, Zeit- und RAM-Verbrauch sind statisch bestimmbar – letzeres ist allerdings etwas Arbeit.
Die Beispiele habe ich als einfache Funktionen gewählt. Einerseits, weil man da schon viel der Eigenheiten eines Compilers erkennen kann. Der erzeugte Code ist wie ein Fingerabdruck und exemplarisch für größere Beispiele. Zudem finde ich, das große Beispiele nicht viel mehr aussagen, und durch meterlange Listings steigt niemand durch und langweilen eher.
Zudem habe ich Beispiele gewählt, die keine Bibliotheken brauchen. Mehr als der Code, der da steht, wird nicht ausgeführt und ist irrelevant, also auch, wie es "ausserhalb" der Funktionen aussieht. Der einzige externe Bezug ist die Funktion foo() bei den Interrupt-Routinen. Welcher Code sich dahinter verbirgt ist egal. Es ist einfach irgendeine parameterlose Funktion ohne Rückgabewert, die irgendwas macht und dann zurückkehrt. Genauso ist die Code, aus denen die Beispiele aufgerufen werden, für die Übersetzung unerheblich. Es sind ja keine statischen oder inline-Funktionen.
--SprinterSB 23:20, 6. Apr 2006 (CEST)
sum_n_loop
Ich hab mal einen Code in Bascom reingestellt. Ich glaube nicht dass Dir das Endergebnis so zusagt. Ist das Ergebnis 45 bei 10 Durchläufen korrekt?? Falls doch, gib mir per PM bescheid, dann mach ich ggf. mehr.
--Darwin.nuernberg 21:43, 23. Apr 2006 (CEST)--Darwin.nuernberg 21:43, 23. Apr 2006 (CEST)
Am hex-File sieht man leider nicht viel, weil da vermutlich das genze Programm drinne ist und noch StartUp-Code und so.
Ich hab mir mal in BASCOM geschaut, aber auf Anhieb hab ich nix gesehen, wie man an den Assembler-Code kommt.
Hier mal ein Disassemble deines iHex:
Disassembly of section .sec1: 00000000 <.sec1>: 0: 12 c0 rjmp .+36 ; 0x26 2: 18 95 reti 4: 18 95 reti 6: 18 95 reti 8: 18 95 reti a: 18 95 reti c: 18 95 reti e: 18 95 reti 10: 18 95 reti 12: 18 95 reti 14: 18 95 reti 16: 18 95 reti 18: 18 95 reti 1a: 18 95 reti 1c: 18 95 reti 1e: 18 95 reti 20: 18 95 reti 22: 18 95 reti 24: 18 95 reti 26: 8f e5 ldi r24, 0x5F ; 95 28: 8d bf out 0x3d, r24 ; 61 2a: c0 e4 ldi r28, 0x40 ; 64 2c: e8 e3 ldi r30, 0x38 ; 56 2e: 4e 2e mov r4, r30 30: 84 e0 ldi r24, 0x04 ; 4 32: 8e bf out 0x3e, r24 ; 62 34: d4 e0 ldi r29, 0x04 ; 4 36: f4 e0 ldi r31, 0x04 ; 4 38: 5f 2e mov r5, r31 3a: ee ef ldi r30, 0xFE ; 254 3c: f3 e0 ldi r31, 0x03 ; 3 3e: a0 e6 ldi r26, 0x60 ; 96 40: b0 e0 ldi r27, 0x00 ; 0 42: 88 27 eor r24, r24 44: 8d 93 st X+, r24 46: 31 97 sbiw r30, 0x01 ; 1 48: e9 f7 brne .-6 ; 0x44 4a: 66 24 eor r6, r6 4c: e0 e6 ldi r30, 0x60 ; 96 4e: f0 e0 ldi r31, 0x00 ; 0 50: fa 93 st -Y, r31 52: ea 93 st -Y, r30 54: 5a 92 st -Y, r5 56: 4a 92 st -Y, r4 58: 8a e0 ldi r24, 0x0A ; 10 5a: 90 e0 ldi r25, 0x00 ; 0 5c: a4 2d mov r26, r4 5e: b5 2d mov r27, r5 60: 8d 93 st X+, r24 62: 9c 93 st X, r25 64: 82 e0 ldi r24, 0x02 ; 2 66: 35 d0 rcall .+106 ; 0xd2 68: 05 d0 rcall .+10 ; 0x74 6a: 24 96 adiw r28, 0x04 ; 4 6c: 82 e0 ldi r24, 0x02 ; 2 6e: 2d d0 rcall .+90 ; 0xca 70: f8 94 cli 72: ff cf rjmp .-2 ; 0x72 74: a8 81 ld r26, Y 76: b9 81 ldd r27, Y+1 ; 0x01 78: 8d 91 ld r24, X+ 7a: 9c 91 ld r25, X 7c: a2 e6 ldi r26, 0x62 ; 98 7e: b0 e0 ldi r27, 0x00 ; 0 80: 8d 93 st X+, r24 82: 9c 93 st X, r25 84: aa 81 ldd r26, Y+2 ; 0x02 86: bb 81 ldd r27, Y+3 ; 0x03 88: 37 d0 rcall .+110 ; 0xf8 8a: a2 e6 ldi r26, 0x62 ; 98 8c: b0 e0 ldi r27, 0x00 ; 0 8e: 0d 91 ld r16, X+ 90: 1c 91 ld r17, X 92: 00 30 cpi r16, 0x00 ; 0 94: 50 e0 ldi r21, 0x00 ; 0 96: 15 07 cpc r17, r21 98: 14 f0 brlt .+4 ; 0x9e 9a: 09 f0 breq .+2 ; 0x9e 9c: 01 c0 rjmp .+2 ; 0xa0 9e: 12 c0 rjmp .+36 ; 0xc4 a0: a2 e6 ldi r26, 0x62 ; 98 a2: b0 e0 ldi r27, 0x00 ; 0 a4: 1a d0 rcall .+52 ; 0xda a6: aa 81 ldd r26, Y+2 ; 0x02 a8: bb 81 ldd r27, Y+3 ; 0x03 aa: 0d 91 ld r16, X+ ac: 1c 91 ld r17, X ae: a2 e6 ldi r26, 0x62 ; 98 b0: b0 e0 ldi r27, 0x00 ; 0 b2: 4d 91 ld r20, X+ b4: 5c 91 ld r21, X b6: 04 0f add r16, r20 b8: 15 1f adc r17, r21 ba: aa 81 ldd r26, Y+2 ; 0x02 bc: bb 81 ldd r27, Y+3 ; 0x03 be: 0d 93 st X+, r16 c0: 1c 93 st X, r17 c2: e3 cf rjmp .-58 ; 0x8a c4: 08 95 ret c6: f8 94 cli c8: ff cf rjmp .-2 ; 0xc8 ca: 48 1a sub r4, r24 cc: 88 27 eor r24, r24 ce: 58 0a sbc r5, r24 d0: 08 95 ret d2: 48 0e add r4, r24 d4: 88 27 eor r24, r24 d6: 58 1e adc r5, r24 d8: 08 95 ret da: ed 91 ld r30, X+ dc: fc 91 ld r31, X de: 31 97 sbiw r30, 0x01 ; 1 e0: fc 93 st X, r31 e2: ee 93 st -X, r30 e4: 08 95 ret e6: 31 97 sbiw r30, 0x01 ; 1 e8: f1 f7 brne .-4 ; 0xe6 ea: 08 95 ret ec: 68 94 set ee: 62 f8 bld r6, 2 f0: 08 95 ret f2: e8 94 clt f4: 62 f8 bld r6, 2 f6: 08 95 ret f8: 82 e0 ldi r24, 0x02 ; 2 fa: 01 c0 rjmp .+2 ; 0xfe fc: 84 e0 ldi r24, 0x04 ; 4 fe: 99 27 eor r25, r25 100: 9d 93 st X+, r25 102: 8a 95 dec r24 104: e9 f7 brne .-6 ; 0x100 106: 08 95 ret
ihex ist leider ein absolutes low-Level-Format, ohne jegliche Symbolinformation...
Es ist ein ganzes ausführbares Prog, enthält also ausser der Funktion noch zusätzlichen Code und mehr als nur die Funktion:
0x0000 -- 0x0024: VecTab 0x0026 -- 0x????: Startup-Code 0x???? -- 0x????: Hauptprogramm 0x0058(?) -- 0x006a :X = Sum_n_loop(10) 0x006c - 0x0072: End 0x0074--0x00c4: Sum_n_loop 0x00ca: Helper 0x00da: Helper 0x00d2: Helper 0x00f8: Helper
Die Helper sind kleine Funktionen, die auf dem Stack und BASCOM-internen GPRs operieren. Zumindest was ich auf die schnelle sehe. Ich kenne BASCOM wie gesagt nicht.
1+2+3+4+5+6+7+8+9+10 = 55
Deine 45 kommt daher, daß das Decr zu früh gemacht wird. Es berechnet also nut die Summe 1...N-1.
Jemand mit besserer Kenntnis von BASCOM könnte den Spaghetti-Code besser auseinanderklabüstern als ich.
Die Helper gehören eigentlich nicht zum Code der Funktion, auch wenn sie teilweise von dort aufgerufen werden. Bei mehreren Funktionen werden die anderen Funks die SChnippsel auch benutzen, so daß der Code nicht der Summation zuzuschlagen ist (find ich jedenfalls).
--SprinterSB 23:07, 23. Apr 2006 (CEST)
Problem bei BASCOM ist, dass es nicht compiliert, wenn der Code nicht ausführbar ist. Ein Objectfile oder sowas wird nicht generiert, ist ja kein C oder Pascal oder sowas. Also ein Vergleich ist wenn überhaupt am endgültigen Resultat, sprich HEX-File möglich. Aber das hattest Du vermutlich nicht vor. Das mit der Korrektur des Schleifendurchlauf es wäre kein akt, aber wenn Du das Endresultat nicht zum Vergleich heranziehen willst, macht es keinen Sinn da noch weitere Energie hinein zu stecken. EIgentlich wäre anstelle "While I > 0" nuer ein "While I >= 0" nötig um die Funktion korrekt zum Ende zu bringen.
PS: wo ist das denn ein Spagetti-Code? Ist doch schön strukturiert. Nur weil Du die Befehle nicht kennst muss es doch kein Spagetticode sein. Und so schlimm ist es wohl auch nicht. Umgekehrt könnte ich zu C Spagetti sagen, weil mir das nicht zusagt.
--Darwin.nuernberg 00:28, 24. Apr 2006 (CEST)
Mich interessiert der Vergleich eigentlich nicht, aber wenn er denn was nützen soll dann sollte der Code auch vergleichbar sein. Mit Codeauszügen ist das schwierig weil es mühseelig ist den speziellen Code der für die Subroutine zuständig ist herauszupicken, zudem greift Bascom auch in Assember sehr schnell auf Subroutinen im Code zurück, so das dies ohnehin schwierig ist. Es wäre viel einfacher und auch aussagekräftiger wenn das komplette Programm auf beidne Seiten als Assembler aufgelistet würde, so wie es Darwin jetzt oben in Bascom gemacht hat. Ansonsten würden Benchmarks für bestimmte Aufgaben (Rechenzeit/Codegröße) für viele sicher interessanter sein.
--Frank 01:12, 24. Apr 2006 (CEST)
Das "Spaghetti-Code" bezog sich auf das Disassemble der hex-Datei, nicht auf die BASIC-Quelle.
Das "While I >= 0" ist immer noch nicht korrekt. Der erste Wert, der addiert wird, ist dann immer noch N-1 und nicht N. Ausserden würde mit >=0 am Ende eine -1 addiert werden, weil das Decr vor der Summe steht.
Ein Codevergleich ist ja so was wie ein Benchmark. Und einfache Routinen, die in den Sprachen gleich aussehen (die Summenberechnung sieht in C ja genauso aus wie in BASIC), sind ja im Artikel als Vorschlag drinne. Irgendw muss man ja anfangen. Laufzeitbestimmungen könnte man natürlich auch machen...
Den Assembler-Code von Darwin sehe ich momentan nicht. Das einzige was ich hab, ist ein HEX-Dump (Maschinen-Code) und ein Assembler-Code aus dem hex. Der Code der Routine kann man schon in etwa zuordnen. Und die kleinen Funktiönchen, die Bascom aufruft, sind wahrscheinlich feste Bausteine, die auch von anderen Funktionen (falls vorhanden) mitverwendet würden.
Das sehe ich nicht so. Ein "Bänschmarg" sollte eigentlich die reíne Rechenleistung wiedergeben. Codeoptimierung ist da was ganz anderes. Zumal ein längerer Code durchaus schneller als ein kompakter Code sein KANN. Manche Befehle benötigen mehr "Ticks" als andere. In Vielen Sprachen ist zwar x=x+1 langsamer und länger als inc x, dies muss aber nicht für alle Befehle gelten und ist ja auch noch kein Compilat, es wird aber auf unterschiedliche Weisen das gleiche Ergebniss erzielt (Das Ziel ist der Weg). Waurm wurde wohl der RISC entwickelt (Reduced InStruction Code). Da muss ma auch mehrere Befehle ausführen als mit einem "NonRISC" System, trozdem sollte das "RISC-System" schneller arbeiten.
--Darwin.nuernberg 23:43, 26. Apr 2006 (CEST)
Fazit?
Hallo
Gibt es jetzt darüber eigentlich schon ein Fazit ? Um wieviel ist C jetzt schneller als Bascom ? --Roberto 07:42, 10. Aug 2006 (CEST)
Zunächst mal zur Deiner Frage. Sie wird in dieser Form oder als "Ist C schneller als BASIC?" öfter gestellt. Diese Frage kann nicht beantwortet werden. Bei "C" und "BASIC" handelt es sich um zwei Programmiersprachen. Grundsätzlich ist es für jeden Compiler vorstellbar, daß er den optimalen Assembler-Code zu einer gegeben Quelle findet (wobei man natürlich noch angeben muss, was unter "optimal" zu verstehen ist).
Hier geht es jedoch nicht um den Vergleich zweier Programmiersprachen, welche man anhand von Attributen/Kriterien wie objektorientiert, aspektorientiert, imperativ, modular, funktional, Befolgung von Standards, Verbreitung, Plattformunabhängigkeit, Möglichkeiten zur Fehlererkennung/-vermeidung, Verifizierbarkeit, Sicherheit/Robustheit etc. vergleichen würde.
In dem Artikel geht es um einen Vergleich von Compilern, mit welchen Quellcode nach Maschinen- bzw. Assemblercode für AVR übersetzt werden kann, und zwar nur im Hinblick auf den generierten Code.
Verglichen werden (Stand 08/2006) avr-gcc und BASCOM, wobei auf BASCOM-Seite nur eine einzige Quelle ausgearbeitet wurde, was dadurch erschwert wird, daß BASCOM keinen Assembler-Code erzeugt sondern direkt nach Maschinensprache übersetzt und keine Quellbezüge einfügt.
Bereits anhand dieses einen Beispiels kann man auf die Arbeitsweise der beiden Compiler zurückschliessen.
Die Codegröße von 70 Bytes (ohne die Lib-Funktion für den Dekrement) bei BASCOM im Vergleich zu 20 Bytes bei avr-gcc liegt zum großen Teil darin begründet, daß BASCOM Werte nicht in Registern hält, sondern immer wieder aus dem SRAM lesen und schreiben muss. Die dafür notwendigen Befehle ergeben sowohl einen breiteren Code als auch eine längere Laufzeit. Einem Compiler dieses Register-Rechnen beizubringen ist jedoch alles andere als einfach und m.E. kurz- oder mittelfristig in BASCOM nicht zu erwarten.
Dies hat z.B. zur Folge, daß auch ein Dekrement (eine häufige arithmetische Operation in vielen Anwendungen) auf dem SRAM geschieht. Die lokale Variable N lebt im Frame der Funktion (hier als Softstack realisiert), und der Dekrement wird umgesetzt in eine Lib-Funktion und einen Aufruf mit vorheriger Adressberechnung von N:
... LDD XL,Y + 0 LDD XH,Y + 1 CALL L_Decrement ... L_Decrement: LD ZL,X+ LD ZH,X SBIW ZL,0x0001 ST X,ZH ST -X,ZL RET
Das dauert ca. 20 Takte, im Gegensatz zu den 2 Takten des avr-gcc-Dekrements (n lebt im Registerpaar r25:r24)
sbiw r24, 0x01
Erstaunlich finde ich, daß der Vergleich gegen 0. Der abzuprüfende Wert wurde schon nach r17:r16 geladen:
CPI r16,0x00 ; LOW <> 0 ? LDI r21,0x00 CPC r17,r21 ; HIGH <> 0 ? BRLT L_0x00D6 ; branch lower ->function exit BREQ L_0x00D6 ; branch equal ->function exit
Diese häufig benötigte Sequenz könnte mit wenig Aufwand geschickter gestaltet werden (evtl. mit anderem Sprungbefehl), so wie in avr-gcc geschehen (es wird r25:r24 getestet):
sbiw r24, 0x00 breq .+8
Mit Optimierungen ist BASCOM sehr zurückhaltend; in dem Beispiel sind sie nicht erkennbar. avr-gcc kann auf einen ganzen Pool von Optimierungen zurückgreifen, die gcc schon mitbringt.
Für spezielle und häufig nachgefragte Routinen wie "Zeichen übern UART senden" bringt BASCOM Lib-Funktionen mit. Diese Lib-Funktionen sind in Assembler programmiert und dürften dementsprechender effizienter sein als BASCOM-compiliertes BASIC. Zwar kommt die Effizienz dieser Libs nicht vom BASCOM-Compiler, für einen Anwender, der diese Funktionen benutzt, ist dieser Unterschied jedoch rein akademischer Natur und irrelevant. Solange man nicht selber programmieren muss und viele Funktionen schreibt, bleibt man dann im Rahmen der Lib-Effizienz.
Momentan ist BASCOM noch in einem Stadium, in dem Compiler-Optimierungen sowohl einen schnelleren auch einen dichteren Code bringen. Ausgefeilte Optimierungsstrategien wie in gcc werden nicht ergriffen. Für einfache Programme wie im AVR-Umfeld üblich dürften Codegröße und Laufzeit mindestens um den Faktor 2 schlechter sein als bei avr-gcc, teilweise auch deutlich darüber. Beschränkt man sich vorwiegend auf BASCOM-Libs, dürfte sich dieses Verhältnis etwas entspannen.
--SprinterSB 15:48, 21. Aug 2006 (CEST)
Danke für die ausführliche Beschreibung :-) --Roberto 21:14, 21. Aug 2006 (CEST)
Zitat SprinterSB:"Mit Optimierungen ist BASCOM sehr zurückhaltend;" Ich finde, das hast du schön gesagt. Ich hab den Optimizer von Bascom zwar nicht erforscht, aber ich hätte gerne mal ein Beispiel gesehen, wo von dem Optimzer auch nur ein Pieps zu sehen wäre. Kennt wer sowas ?
--PicNick 09:00, 22. Aug 2006 (CEST)
Ich habe mir mal erlaubt, die Beispiele im Keil für Arm 7 CPUs unter die AVR Codeschnipsel zu setzen. Auch die Fassung für ARM 7 mit dem gcc wären hier interssant - ich habe aber grade keinen WinARM zur Hand. Das gcc und Keil den Code wesentlich effizienter als Bascom übersetzen und auch besser optimieren, liegt auf der Hand. Das sollte aber keinen Bascom Anwender wirklich bekümmern so lange sie mit ihren Werkzeugen zu passablen Ergebnissen kommen. Das ist schließlich das wichtigste. Man sieht hier auch, das ein RISC CPU-Kern potentiell etwas mehr Befehle benötigt als ein CISC, jedoch dürften dafür die die verbrauchten Taktcycels etwas geringer sein, was sich besonders bei Loops positiv auf die Geschwindigkeit auswirkt. --Rolf D. 07:45, 2. Jul 2007 (CEST)