Archiv verlassen und diese Seite im Standarddesign anzeigen : AVR/Bascom geschwindigkeit grundrechenarten
ich hatte einige umstimigkeiten mit der geschwindigkeit in der die grundrechenarten in kombination mit verschiedenen Datentypen ausgefürt werden. Ich habe daswegen alles mal im Bascom Simulator getestet(ohne Gewähr)
$regfile = "m32def.dat"
$crystal = 11059200
Dim B1 As Byte
Dim B2 As Byte
Dim B3 As Byte
Dim I1 As Integer
Dim I2 As Integer
Dim I3 As Integer
Dim W1 As Word
Dim W2 As Word
Dim W3 As Word
Dim L1 As Long
Dim L2 As Long
Dim L3 As Long
Dim S1 As Single
Dim S2 As Single
Dim S3 As Single
Dim D1 As Double
Dim D2 As Double
Dim D3 As Double
B1 = 80
I1 = 80
W1 = 80
L1 = 80
S1 = 80
D1 = 80
B2 = 8
I2 = 8
W2 = 8
L2 = 8
S2 = 8
D2 = 8
Do
B3 = B1 + B2 '9
B3 = B1 - B2 '9
B3 = B1 * B2 '10
B3 = B1 / B2 '112
Shift , B1 , Right , 3 '27
I3 = I1 + I2 '20
I3 = I1 - I2 '20
I3 = I1 * I2 '39
I3 = I1 / I2 '291
Shift , I1 , Right , 3 '34
W3 = W1 + W2 '20
W3 = W1 - W2 '20
W3 = W1 * W2 '39
W3 = W1 / W2 '255 '
Shift , W1 , Right , 3 '34
L3 = L1 + L2 '41
L3 = L1 - L2 '41
L3 = L1 * L2 '87
L3 = L1 / L2 '787
Shift , L1 , Right , 3 '48
S3 = S1 + S2 '162
S3 = S1 - S2 '171
S3 = S1 * S2 '420
S3 = S1 / S2 '527
Shift , S1 , Right , 3 '73
D3 = D1 + D2 '315
D3 = D1 - D2 '378
D3 = D1 * D2 '455
D3 = D1 / D2 '1760
Shift , D1 , Right , 3 '1
Loop
End 'end program
Die Komentare in der jeweiligen zeile sind nach Simulator die benötigten zyklen die für die berechnung benötigt werden. Mir hat es geholfen und deswegen wolte ich die ganze mühe nicht für mich behalten.
Hallo mycroc,
schöne Liste, man glaubt ja nicht, wieviel so eine Division frißt.
Kannst du auch mal Words und Bytes miteinander verrechnen?
Ich hab mal Wurzeln aus Long Variablen gezogen, fast immer 2126 Takte:
http://www.team-iwan.de/technik/software/lcd03.php
Gruß, Michael
Shift , D1 , Right , 3 '1
Ein Zyklus ??
oberallgeier
21.04.2009, 08:31
Hallo, Alle,
aus meinen Anfängen zur Aerodynamik auf eher gefestigten theoretischen Grundlagen habe ich einen Hang zur praktischen Überprüfung von Simulationen. Manchmal reicht mir dann auch die praktische Prüfung. Daher hatte ich vor einiger Zeit folgendes gemacht:
- Schalte LED1 ein (meist nur bei "Handstoppung")
- Schleifenanfang (z.B. für 1000 Schleifen oder bei Oszilloskopmessungen auch unbegrenzt)
- Schalte LED2 ein
- Rechne eine bestimmte Rechnung
- Schalte LED2 aus
- Warte eine GENAU definierte Zeit (z.B. 1000 nops)
- Schleifenende
- Schalte LED1 aus (meist nur bei "Handstoppung")
Die benötigte Zeit für die bestimmte Rechnung hatte ich anhand der Schaltvorgänge der LED´s mit dem Oszilloskop gemessen (dann reichen auch weniger nops). Die andere, bei mir ebenfalls erfolgreiche, Möglichkeit war einen bestimmte Anzahl Rechnungen durchzuführen und die LED1 vor Eintritt in die Schleife einzuschalten und nach Austritt aus der Schleife auszuschalten - die Zeit wird händisch gestoppt. Die ähnliche Messung aber OHNE Rechnung liefert dann den Zeitbedarf für den Schleifen-Overhead ohne Rechnung. Aus dem Vergleich der beiden Werte errechne ich mir den Zeitbedarf für die entsprechende Rechnung bzw. Subroutine.
Auf diese Weise hatte ich auch ganze Subroutinen ausgemessen. Das Ergebnis scheint durch die meist recht gute Funktion meiner ziemlich Interrupt-durchseuchten Projektchen bestätigt zu werden.
Besserwessi
21.04.2009, 17:54
Das mit der Kurzen Zeit für ein shift bei Flißkomma zahlen kann schon sein. Schließlich braicht man dazu nur den Exponenten (1 Byte ?) zu erniedriegen. Wenn die zahl schon in Registen ist reicht dazu tatsachlich ein subi r?, 3. Im Vergleich zu Byte + Byte, würde ich aber sagen da ist ein Tippfehler drin.
Viele Compiler haben keine extra Routinen für gemischte Operationen (z.B. Word + byte), statt desen wird erst auf den größeren (oder vom Ziel ?) Typ erweitert. Da wäre noch etwas Raum für Optimierungen.
Bei Bascom ist die Zeit ja noch einigermaßen eindeutig (nur eine Operation je Zeile), bei anderen Compilern kann die Zeit ja nach Context etwas variieren.
sechsrad
21.04.2009, 19:33
bei anderen Compilern kann die Zeit ja nach Context etwas variieren.
Ja, bei WinAVR wir es noch schneller und der Compilercode ist kürzer.
Probier es selber mal aus.
mfg
Besserwessi
21.04.2009, 20:37
WinAVR ist so ein Beispiel, wo die Werte nicht jedes mal aus dem RAM gehohlt werden. Das kann je Byte etwa 3-6 Zyklen sparen, dazu noch ein Bisschen durch mehr inline Code. Gerade die langsamen Dinge sollten aber ähnlich sein. Nur bei der Wurzel gibte es ganz verschiedene Methoden die zu berechnen und entsprechend möglicherweise große unterschiede.
Wenn es noch jemanden interessiert, ich konnte das Mysterium mit diesem "1-cycle-shift right" klären:
Bascom 1.11.8.1 meckert den Shift -Befehl an (weil ihm die Variable nicht passt)
Bascom 1.11.9.2 meckert NICHT, sondern lässt den Befehl einfach aus !
Bei "Shift , D1 , Right , 3"
lädt er zwar noch r25 mit "3"
( LDI r25,0x03 --> 1 Cycle )
aber das wars's dann auch.
//----------D3 = D1 / D2 '1760
LDI XL,0x9F D3
LDI XH,0x00
ST -Y,XH
ST -Y,XL
LDI XL,0x97 D2
LDI XH,0x00
LDI ZL,0x87 D1
LDI ZH,0x00
CALL L_0x06A4
ADIW YL,0x0002
//------------------Shift , D1 , Right , 3 '1
LDI r25,0x03
?????
JMP L_0x011E //LOOP:
.DB 0xF8, 0x94 ;..
L_0x03FE: //END
RJMP L_0x03FE
.DB 0x09, 0xD0, 0x0E, 0x94, 0xE5, 0x04 ;......
Ich habe mir das Programm von @mycroc übersetzt und disassembliert, weil mir die Sache seltsam vorkam, der relevante Ausschnitt ist oben.
Wenn jemand das komplette Assembler-listen sehen will, --> sprechen
StefanHambur
28.04.2009, 22:56
Sehr interessante Analyse.
Wieviele Zyklen benötigen eigentlich Sinus und Cosinus?
ikarus_177
29.04.2009, 13:56
Habs grad ausprobiert: laut meinem Simulator (Bascom 1.11.9.3) benötigt ein Sinus-Aufruf 85 Zyklen, beim Cosinus bzw. Tangens sinds 85 bzw. 115 Zyklen.
Mich würde ja interessieren, wie Bascom das übersetzt, sprich, wie der Controller die Funktionswerte dann auch berechnet. Wird ihm da eine feste Tabelle mitgegeben, oder errechnet er sich die Werte mit Reihenentwicklungen o.ä.?
Viele Grüße
Besserwessi
29.04.2009, 18:12
Bei nur 85 Zyklen kann man kaum eine richtige Reihenentwicklung mehr machen. Das kommt mir auch ausgesprochen kurz vor. ZU erwarten wären eigentlich Zeiten etwas langsamer als eine Division.
Kann es sein das das nur für einen Speziellen Wert (z.B. 0) gilt, oder für Integer als Ergebnis ?
ikarus_177
29.04.2009, 18:23
Das ist schon sehr seltsam:
bei
dim C as single
C = sin(3.1415) braucht man die ca. 85 Zyklen.
bei
dim C as single
C = 3.1415
C = sin(C) sinds schon über 2500 Zyklen...
Hat jemand vielleicht eine Erklärung dafür?
Alle Angaben wie immer ohne Gewähr 8-[
Besserwessi
29.04.2009, 22:01
Bei dem ersten Beispiel wird der Sinus während des Compilierens ausgerechenet. Wofür dann noch 85 Zyklen gebraucht werden weiss ich nicht. Das Kopieren einer Konstante solle nicht so lange dauern.
Beim 2 ten Beispiel scheint der Compiler das nicht so weit zu optimieren wie im ersten Beispiel. Da wird dann der Sinus wohl wirklich ausgerechent. Die Zeit sieht auch irgendwie realistisch aus für eine Reihentwicklung. Noch etwas langsamer ist meistens log und der Arcus-Tangens.
*lol* Wenn man beide Varianten in EINE source schreibt, ersparte er sich die ganze Rechnerei und nimmt nur Literale.
(müsste man gucken, ob das nur für PI gilt oder für alle werte
//----------------------------------------
C = Sin(3.1415)
//----------------------------------------
LDI XL,0x60
LDI XH,0x00
LDI ZL,0x8C
LDI ZH,0x03
CALL L_0x0276 // 4 Byte Literal 0x38C --> C-single
//----------------------------------------
C = 3.1415
//----------------------------------------
LDI XL,0x60
LDI XH,0x00
LDI ZL,0x90
LDI ZH,0x03
CALL L_0x0276 // 4 Byte Literal 0x390 --> C-single
//----------------------------------------
C = Sin(c)
//----------------------------------------
LDI ZL,0x60
LDI ZH,0x00
CALL L_0x036E // C-single --> R13, 14, 15, 16
LDI ZL,0x0D
LDI ZH,0x00
LDI XL,0x60
LDI XH,0x00
CALL L_0x0286 // R13, 14, 15, 16 --> C-single
//----------------------------------------
CLI // "END"
L_0x00B8:
RJMP L_0x00B8
Literal-0x038c
.DB 0x04, 0x4F, 0xC2, 0x38
Literal-0x0390
.DB 0x56, 0x0E, 0x49, 0x40 ;.O.8V.I@
jetzt schau ich mir noch an, was er macht, wenn ich ihn zum Rechnen zwinge
mich würde mal ein arcus sinus interessieren, wie unterschiedlich verhalten sich eigentlich C und bascom bei solchen sachen ... in C verwende ich die math.h von winavr
Du, das ist Arbeit.
Ich stell gerne den disassemblierten Basic für ASIN hier rein, und wenn mir einer irgendeinen Code als .HEX File gibt, kann ich den auch disassemblieren und herzeigen.
Aber das durchhacken und kommentieren muss ich fleissigen Leuten überlassen :-)
achso, ich dachte du hast dir nen debugger o.ä. zur brust genommen und einfach gemessen ^^
ich hab atm auch zu viel um die ohren um mir das auszurechnen oder nachzulesen, aber n kollege von mir müsste u.a. mit asin arbeiten und das könnte eventuell zeitkritisch werden, da wollt ich einfach mal nachfragen
screwdriver
04.10.2009, 12:23
Sorry, wenn ich diesen "alten" Thread auspacke. Ich bin auf ihn gestossen, weil ich zur Laufzeitoptimierung meiner DS1820-Temperatur-Auswertung ein paar Anregungen gesucht habe.
ich hatte einige umstimigkeiten mit der geschwindigkeit in der die grundrechenarten in kombination mit verschiedenen Datentypen ausgefürt werden. Ich habe daswegen alles mal im Bascom Simulator getestet(ohne Gewähr)
$regfile = "m32def.dat"
$crystal = 11059200
Dim B1 As Byte
Dim B2 As Byte
Dim B3 As Byte
Dim I1 As Integer
Dim I2 As Integer
Dim I3 As Integer
Dim W1 As Word
Dim W2 As Word
Dim W3 As Word
Dim L1 As Long
Dim L2 As Long
Dim L3 As Long
Dim S1 As Single
Dim S2 As Single
Dim S3 As Single
Dim D1 As Double
Dim D2 As Double
Dim D3 As Double
B1 = 80
I1 = 80
W1 = 80
L1 = 80
S1 = 80
D1 = 80
B2 = 8
I2 = 8
W2 = 8
L2 = 8
S2 = 8
D2 = 8
Do
B3 = B1 + B2 '9
B3 = B1 - B2 '9
B3 = B1 * B2 '10
B3 = B1 / B2 '112
Shift , B1 , Right , 3 '27
I3 = I1 + I2 '20
I3 = I1 - I2 '20
I3 = I1 * I2 '39
I3 = I1 / I2 '291
Shift , I1 , Right , 3 '34
W3 = W1 + W2 '20
W3 = W1 - W2 '20
W3 = W1 * W2 '39
W3 = W1 / W2 '255 '
Shift , W1 , Right , 3 '34
L3 = L1 + L2 '41
L3 = L1 - L2 '41
L3 = L1 * L2 '87
L3 = L1 / L2 '787
Shift , L1 , Right , 3 '48
S3 = S1 + S2 '162
S3 = S1 - S2 '171
S3 = S1 * S2 '420
S3 = S1 / S2 '527
Shift , S1 , Right , 3 '73
D3 = D1 + D2 '315
D3 = D1 - D2 '378
D3 = D1 * D2 '455
D3 = D1 / D2 '1760
Shift , D1 , Right , 3 '1
Loop
End 'end program
Die Komentare in der jeweiligen zeile sind nach Simulator die benötigten zyklen die für die berechnung benötigt werden. Mir hat es geholfen und deswegen wolte ich die ganze mühe nicht für mich behalten.
Ich möchte anmerken, das Schiebeoperationen als Ersatz für Divisionen durch 2, 4, 8, ... nur für vorzeichenlose, ganzzahlige Variablentypen (Byte, Word, Long) äquivalent sind, da Bascom bei Schiebeoperationen nicht das Vorzeichen berücksichtigt. D.h. Rechtsschieben bei Integervariablen ist nur solange korrekt, wie die Integervariable größer/gleich Null ist.
In folgendem Programmbeispiel habe ich eine Integer-Division durch 2 mit Hilfe des Inline-Assemblers realisiert und mit der Integer-Division selbst, sowie dem einfachen Rechtsschieben verglichen.
Das Programmbeispiel kann direkt im Simulator ausgeführt werden.
Dim X0 As Byte , X1 As Byte
Dim X As Integer At X0 Overlay
Dim Y As Integer
Do
'************************************************* ******************************
'*** Integer Variable eingeben ***
'************************************************* ******************************
Input "x:" , X
nop
'************************************************* ******************************
'*** Integer-Division durch 2 ***
'************************************************* ******************************
Y = X / 2
nop
Print Y
'************************************************* ******************************
'*** Rechtsschieben Integer Variable ***
'************************************************* ******************************
Y = X
Shift Y , Right , 1
Print Y
nop
'************************************************* ******************************
'*** Rechtsschieben Integer Variable mit Vorzeichen ***
'************************************************* ******************************
push r24 'R24 retten
push r25 'R25 retten
lds r24,{x0} 'LSB Integer in R24
lds r25,{x1} 'MSB Integer in R25
sbrc r25,7 'Wenn Integer negativ..
adiw r24,1 '..dann Incrementiere Integer
asr r25 'Rechtsschieben MSB
ror r24 'Rechtsschieben LSB
sts {x0},r24 'R24 in LSB Integer
sts {x1},r25 'R25 in MSB Integer
pop r25 'R25 wiederherstellen
pop r24 'R24 wiederherstellen
nop
Print X
'************************************************* ******************************
Loop
End
MfG
screwdriver
edit:
Der Variablentyp LONG hat einen Wertebereich von -2147483648 bis 2147483647 und ist zwar ganzzahlig aber vorzeichenbehaftet. Für Long gilt also das gleiche wie für Integer.
Der im Codefenster vorgestellte Algorithmus ist direkt aus einem meiner Quelltexte kopiert. In etwas abgeänderter Form und als nettes Macro verpackt, ist er jedoch recht gut allgemein anwendbar. Wer möchte kann ja noch eine Zählschleife drumrum tun und kann somit Mehrfach-Shiften.
Dim Intvar As Integer 'Integervariable
Dim Y As Integer
'************************************************* ******************************
'*** Macro: Asr_Int16 ***
'************************************************* ******************************
'*** Aufruf mit Adresse der zu schiebenden Integervariable im Z-Register ***
'************************************************* ******************************
Macro Asr_int16
ldd r24,z+0 'Lowbyte Integer in R24
ldd r25,z+1 'Highbyte Integer in R25
sbrc r25,7 'Wenn Integer negativ, ..
adiw r24,1 '.. dann Intvar inkrementieren
asr r25 'Schiebe Highbyte rechts
ror r24 'Schiebe Lowbyte rechts
std z+0,r24 'R24 in Lowbyte Integer
std z+1,r25 'R25 in Highbyte Integer
End Macro
'************************************************* ******************************
'*** Hauptprogramm ***
'************************************************* ******************************
Do
Input "IntVar: " , Intvar
Y = Intvar / 2
Print Y
Loadadr Intvar , Z 'Adresse Integervariable in Z-Register laden
Asr_int16 'Integervariable Rechtsschieben
Print Intvar
Loop
End
Laufzeiten:
+1000 / 2 : 277 Zyklen, mit Asr_int16 nur 14 Zyklen
-1000 / 2 : 312 Zyklen, mit Asr_int16 nur 15 Zyklen
sechsrad
08.10.2009, 09:52
...Hat jemand vielleicht eine Erklärung dafür?....
Also ich zweifle eure Zeitmessungen sehr stark an, weil ihr euch mit dem ASM-Code von Bascom überhaupt noch nicht geschäftigt habt.
Werte mal die Zyklen vom ASM-Sourse aus, dann werdet ihr staunen, was da für Werte rauskommen. Und diese Werte sind auch die Zeiten, die der Atmega braucht.
Besserwessi
08.10.2009, 20:21
Die Zahlen sind schon plasibel. Wenn man das im Simulator durchläuft, nimmt einem der Simulator das zählen der Zyklen schon ab. Da muß man sich den Code auch gar nicht mehr anschauen. Nur wenn man wissen will wieso etwas besonders schnell oder langsam ist ist das nötig. Besonders bei Division und Multiplication kann die Zeit etwas von den Zahlen abhängen.
Die echte Division ist halt recht langsam, daher die rund 300 Zyklen auch für eine Division durch 2. Der extra inline ASM code ist dann entsprechend schneller. Ein guter Compiler hätte das .../2 auch gleich durch den ASM code oben, oder zumindest was ähnliches ersetzt.
Powered by vBulletin® Version 4.2.5 Copyright ©2024 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.