Aus RN-Wissen.de
Wechseln zu: Navigation, Suche


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)