PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : AVR/Bascom geschwindigkeit grundrechenarten



mycroc
20.04.2009, 21:00
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.

Michael
21.04.2009, 00:36
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

PicNick
21.04.2009, 07:05
Shift , D1 , Right , 3 '1


Ein Zyklus ??

oberallgeier
21.04.2009, 07: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, 16: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, 18: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, 19: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.

PicNick
22.04.2009, 10:22
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, 21:56
Sehr interessante Analyse.
Wieviele Zyklen benötigen eigentlich Sinus und Cosinus?

ikarus_177
29.04.2009, 12: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, 17: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, 17: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, 21: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.

PicNick
30.04.2009, 08:38
*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

Ceos
30.04.2009, 08:58
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

PicNick
30.04.2009, 09:19
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 :-)

Ceos
30.04.2009, 09:47
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, 11: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, 08: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, 19: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.