Forum: Mikrocontroller und Digitale Elektronik Laufzeit einer Division variiert


von Matthias H. (streno)


Lesenswert?

Hallo,

ich programmiere einen STM32F0 mit CooCox und erhalte ein (für mich) 
merkwürdiges Verhalten.

Da meine Anwendung sehr zeitbegrenzt ist und ich auf jede µs achten muss 
bin ich gerade dabei meinen Code zu Optimieren.

Das merkwürdige Verhalten zeigt sich bei einer Rechnung:
1
 v=((Buffer[0]*K)/Buffer[1]);

Diese errechnet mir eine Spannung aus den Werten meines AD-Wandlers.

Die Datentypen sind uint16_t Buffer[2], uint32_t K=5029, int16_t v;

Was ist das merkwürdige Verhalten: Sobald meine Spannung über 1 Volt 
geht, so dauert die Berechnung 0,5µs länger und ich verstehe einfach 
nicht warum.
Das gleiche Verhalten zeigt sich, wenn ich den Nenner konstant halte und 
nur den Zähler ändere.
Konkret bedeutet das: Spannung<1V Buffer[0]= 1296
                                  Spannung>1V Buffer[0]=1810

Setze ich diese beiden zahlen ein und halte den Nenner konstant so 
erhalte ich auch dieses Verhalten. Ich kann es mir einfach nicht 
erklären. Ein Überlauf passiert ja nicht, da K als unsigned long int 
deklariert wurde.

Vielleicht hat jemand einen Tipp.
Meinen Assembler-Code poste ich noch:
1
137               v=((1810*K)/Buffer[1]); //Berechnete Spannung im Q3.12 Format
2
080005ac:   ldr r3, [pc, #204]      ; (0x800067c <DMA1_Channel1_IRQHandler+236>)
3
080005ae:   ldr r3, [r3, #0]
4
080005b0:   ldr r2, [pc, #204]      ; (0x8000680 <DMA1_Channel1_IRQHandler+240>)
5
080005b2:   muls r2, r3
6
080005b4:   ldr r3, [pc, #204]      ; (0x8000684 <DMA1_Channel1_IRQHandler+244>)
7
080005b6:   ldrh r3, [r3, #2]
8
080005b8:   movs r1, r3
9
080005ba:   movs r0, r2
10
080005bc:   bl 0x8001b88 <__udivsi3>
11
080005c0:   movs r3, r0
12
080005c2:   movs r2, r3
13
080005c4:   ldr r3, [pc, #192]      ; (0x8000688 <DMA1_Channel1_IRQHandler+248>)
14
080005c6:   str r2, [r3, #0]

von (prx) A. K. (prx)


Lesenswert?

Der Cortext M0 hat keine Hardware-Division. Weshalb sie wie hier gezeigt 
in der Library-Routine "__udivsi3" stattfindet. Interessant ist also 
dessen Code.

Dass die Laufzeit von Divisionen generell von den zu dividierenden 
Werten abhängen kann ist nicht neu.

von Rene K. (xdraconix)


Lesenswert?

Versuche mal als festen Wert nicht die 1810 sondern die 1812 ;-)

Btw..: DMA1_Channel1_IRQHandler - in einer ISR macht man solche 
Berechnungen nicht. Der STM32F0 hat kein HW Divisor - richtig?!

von Oliver S. (oliverso)


Lesenswert?

> bl 0x8001b88 <__udivsi3>

Hast du dir denn mal __udivsi3 angeschaut, was das so macht?

Oliver

: Bearbeitet durch User
von Matthias H. (streno)


Lesenswert?

A. K. schrieb:
> Dass die Laufzeit von Divisionen generell von den zu dividierenden
> Werten abhängen kann ist nicht neu.

Okay, aber wie ist das verhalten zu erklären?
Weil der Compiler, bei bestimmten Werten die Division nicht mit einer 
Schiebeoperation ersetzen kann?

Rene K. schrieb:
> Versuche mal als festen Wert nicht die 1810 sondern die 1812 ;-)

Die 1810 habe ich nur zu Testzwecken eingesetzt, damit ich zwei Werte 
habe die unterschiedliches Verhalten zeigen

Oliver S. schrieb:
> Hast du dir denn mal __udivsi3 angeschaut, was das so macht?

Habe es noch nicht gefunden. Muss ich erstmal suchen nach der Funktion

von m.n. (Gast)


Lesenswert?

Matthias H. schrieb:
> so dauert die Berechnung 0,5µs länger

Das ist aber nun keine Zeit, die ins Gewicht fallen sollte.
Wenn es Dir nicht gelingt, durch etwas geschicktere Programmierung die 
Laufzeit zu optimieren, takte den µC einen Tick schneller.
Vielleicht bringt ein anderer Compiler bessere Ergebnisse.
Wenn das Programm kurz ist, könnte eine Demoversion von IAR oder Keil 
ausreichen.

von Rene K. (xdraconix)


Lesenswert?

Matthias H. schrieb:
> Rene K. schrieb:
>> Versuche mal als festen Wert nicht die 1810 sondern die 1812 ;-)
>
> Die 1810 habe ich nur zu Testzwecken eingesetzt, damit ich zwei Werte
> habe die unterschiedliches Verhalten zeigen

Ja, dann setze mal die 1812 ein - und schau nach deiner Zeit. Überleg 
einfach mal was die "0" in irgendeiner Berechnung macht - und was ein 
Compiler aus solch einer Berechnung anstellt.

von Matthias H. (streno)


Lesenswert?

Rene K. schrieb:
> Ja, dann setze mal die 1812 ein - und schau nach deiner Zeit. Überleg
> einfach mal was die "0" in irgendeiner Berechnung macht - und was ein
> Compiler aus solch einer Berechnung anstellt.

Hat sich trotzdem nichts verändert.

Bei <1296 ist es immernoch 0,5µs kürzer als bei >1810

von Rene K. (xdraconix)


Lesenswert?

Und was macht 1816? Kann es sein das Werte über 1296 einfach in einen 
anderen Wertebereich fallen und somit die Berechnung halt länger dauert? 
Aber wie gesagt - ohne zu wissen was __udivsi3 macht - alles 
Rätselraten.

von (prx) A. K. (prx)


Lesenswert?

Matthias H. schrieb:
> Weil der Compiler, bei bestimmten Werten die Division nicht mit einer
> Schiebeoperation ersetzen kann?

Nur Divisionen durch konstante Zweierpotenzen sind durch eine 
Schiebeoperation ersetzbar. Ist hier aber keine Zweierpotenz, im 
Original nicht einmal eine Konstante, weshalb keine Schiebeoperation 
möglich ist.

Wenn man durch eine Konstante dividiert, kann man u.U. mit einer 
Multiplikation und etwas Zusatzkram auskommen. Das macht ein Compiler 
aber nicht zwangsläufig selber, zumal dabei eine Rundung unter die 
Räder kommen kann.

Übrigens ist auch die Laufzeit der ab Cortex M3 vorhandenen 
Hardware-Division von den Werten abhängig.

von Matthias H. (streno)


Lesenswert?

Rene K. schrieb:
> ohne zu wissen was __udivsi3 macht - alles
> Rätselraten.

Wo kann ich denn udivsi3 finden?

Im internet steht dazu nur
"These functions return the negation of a.
Runtime Function: unsigned int __udivsi3 (unsigned int a, unsigned int 
b)"

von (prx) A. K. (prx)


Lesenswert?

Matthias H. schrieb:
> Wo kann ich denn udivsi3 finden?

Im erzeugten Code an Adresse 0x8001b88.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Wenn ich eine Funktion mit unterschiedlichen Werten aufrufe, dann 
erwarte ich idR. auch ein unterschiedliches Ergebnis. Dass zum Ermitteln 
des Ergebnisses unterschiedliche Abläufe greifen, erschient mir dabei 
als normales Verhalten.

Schreibe (und optimiere hinterher) einfach selber mal so eine 
Divisionsroutine, dass wirst du sehen, dass du abhängig vom Bitmuster 
andere Strategien und Wege gehen musst.

Matthias H. schrieb:
> Weil der Compiler, bei bestimmten Werten die Division nicht mit einer
> Schiebeoperation ersetzen kann?
Er kann nur in ganz, ganz seltenen Fällen die Division durch ein simples 
Schieben ersetzen. Lass die Division (die ist doch unsigned, oder?) mal 
so laufen 1810/510 und mal so 1810/512. Was kommt dabei raus?

Matthias H. schrieb:
> Bei <1296 ist es immernoch 0,5µs kürzer als bei >1810
Und was ist daran schlimm?
Du kannst dich niemals drauf verlassen, dass ein Funktionsaufruf in der 
Zeit 0 oder wenigstens immer gleich schnell abläuft.

von (prx) A. K. (prx)


Lesenswert?

Matthias H. schrieb:
> Im internet steht dazu nur
> "These functions return the negation of a.

Nö. Darunter gucken, nicht darüber. "These functions return the quotient 
of the unsigned division of a and b."

von Dumdi D. (dumdidum)


Lesenswert?

Welchen Compiler verwendest Du mit Coocox?

Hier mal als Beispiel die llvm source:

https://llvm.org/svn/llvm-project/compiler-rt/branches/release_30/lib/udivsi3.c

von Theor (Gast)


Lesenswert?

Versuche mal folgende Suchanfrage "Sourcecode __udivsi3".

Beitrag #5099187 wurde vom Autor gelöscht.
von Matthias H. (streno)


Lesenswert?

1
          __udivsi3:
2
08001b88:   movs r2, #0
3
08001b8a:   lsrs r3, r0, #1
4
08001b8c:   cmp r3, r1
5
08001b8e:   bcc.n 0x8001c7a <__udivsi3+242>
6
08001b90:   lsrs r3, r0, #4
7
08001b92:   cmp r3, r1
8
08001b94:   bcc.n 0x8001c56 <__udivsi3+206>
9
08001b96:   lsrs r3, r0, #8
10
08001b98:   cmp r3, r1
11
08001b9a:   bcc.n 0x8001c26 <__udivsi3+158>
12
08001b9c:   lsrs r3, r0, #12
13
08001b9e:   cmp r3, r1
14
08001ba0:   bcc.n 0x8001bf4 <__udivsi3+108>
15
08001ba2:   lsrs r3, r0, #16
16
08001ba4:   cmp r3, r1
17
08001ba6:   bcc.n 0x8001bc4 <__udivsi3+60>
18
08001ba8:   movs r2, #255   ; 0xff
19
08001baa:   lsls r1, r1, #8
20
08001bac:   rev r2, r2
21
08001bae:   lsrs r3, r0, #16
22
08001bb0:   cmp r3, r1
23
08001bb2:   bcc.n 0x8001bba <__udivsi3+50>
24
08001bb4:   asrs r2, r2, #8
25
08001bb6:   lsls r1, r1, #8
26
08001bb8:   beq.n 0x8001c86 <__udivsi3+254>
27
08001bba:   lsrs r3, r0, #12
28
08001bbc:   cmp r3, r1
29
08001bbe:   bcc.n 0x8001bf4 <__udivsi3+108>
30
08001bc0:   b.n 0x8001bc4 <__udivsi3+60>
31
08001bc2:   lsrs r1, r1, #8
32
08001bc4:   lsrs r3, r0, #15
33
08001bc6:   cmp r3, r1
34
08001bc8:   bcc.n 0x8001bce <__udivsi3+70>
35
08001bca:   lsls r3, r1, #15
36
08001bcc:   subs r0, r0, r3
37
08001bce:   adcs r2, r2
38
08001bd0:   lsrs r3, r0, #14
39
08001bd2:   cmp r3, r1
40
08001bd4:   bcc.n 0x8001bda <__udivsi3+82>
41
08001bd6:   lsls r3, r1, #14
42
08001bd8:   subs r0, r0, r3
43
08001bda:   adcs r2, r2
44
08001bdc:   lsrs r3, r0, #13
45
08001bde:   cmp r3, r1
46
08001be0:   bcc.n 0x8001be6 <__udivsi3+94>
47
08001be2:   lsls r3, r1, #13
48
08001be4:   subs r0, r0, r3
49
08001be6:   adcs r2, r2
50
08001be8:   lsrs r3, r0, #12
51
08001bea:   cmp r3, r1
52
08001bec:   bcc.n 0x8001bf2 <__udivsi3+106>
53
08001bee:   lsls r3, r1, #12
54
08001bf0:   subs r0, r0, r3
55
08001bf2:   adcs r2, r2
56
08001bf4:   lsrs r3, r0, #11
57
08001bf6:   cmp r3, r1
58
08001bf8:   bcc.n 0x8001bfe <__udivsi3+118>
59
08001bfa:   lsls r3, r1, #11
60
08001bfc:   subs r0, r0, r3
61
08001bfe:   adcs r2, r2
62
08001c00:   lsrs r3, r0, #10
63
08001c02:   cmp r3, r1
64
08001c04:   bcc.n 0x8001c0a <__udivsi3+130>
65
08001c06:   lsls r3, r1, #10
66
08001c08:   subs r0, r0, r3
67
08001c0a:   adcs r2, r2
68
08001c0c:   lsrs r3, r0, #9
69
08001c0e:   cmp r3, r1
70
08001c10:   bcc.n 0x8001c16 <__udivsi3+142>
71
08001c12:   lsls r3, r1, #9
72
08001c14:   subs r0, r0, r3
73
08001c16:   adcs r2, r2
74
08001c18:   lsrs r3, r0, #8
75
08001c1a:   cmp r3, r1
76
08001c1c:   bcc.n 0x8001c22 <__udivsi3+154>
77
08001c1e:   lsls r3, r1, #8
78
08001c20:   subs r0, r0, r3
79
08001c22:   adcs r2, r2
80
08001c24:   bcs.n 0x8001bc2 <__udivsi3+58>
81
08001c26:   lsrs r3, r0, #7
82
08001c28:   cmp r3, r1
83
08001c2a:   bcc.n 0x8001c30 <__udivsi3+168>
84
08001c2c:   lsls r3, r1, #7
85
08001c2e:   subs r0, r0, r3
86
08001c30:   adcs r2, r2
87
08001c32:   lsrs r3, r0, #6
88
08001c34:   cmp r3, r1
89
08001c36:   bcc.n 0x8001c3c <__udivsi3+180>
90
08001c38:   lsls r3, r1, #6
91
08001c3a:   subs r0, r0, r3
92
08001c3c:   adcs r2, r2
93
08001c3e:   lsrs r3, r0, #5
94
08001c40:   cmp r3, r1
95
08001c42:   bcc.n 0x8001c48 <__udivsi3+192>
96
08001c44:   lsls r3, r1, #5
97
08001c46:   subs r0, r0, r3
98
08001c48:   adcs r2, r2
99
08001c4a:   lsrs r3, r0, #4
100
08001c4c:   cmp r3, r1
101
08001c4e:   bcc.n 0x8001c54 <__udivsi3+204>
102
08001c50:   lsls r3, r1, #4
103
08001c52:   subs r0, r0, r3
104
08001c54:   adcs r2, r2
105
08001c56:   lsrs r3, r0, #3
106
08001c58:   cmp r3, r1
107
08001c5a:   bcc.n 0x8001c60 <__udivsi3+216>
108
08001c5c:   lsls r3, r1, #3
109
08001c5e:   subs r0, r0, r3
110
08001c60:   adcs r2, r2
111
08001c62:   lsrs r3, r0, #2
112
08001c64:   cmp r3, r1
113
08001c66:   bcc.n 0x8001c6c <__udivsi3+228>
114
08001c68:   lsls r3, r1, #2
115
08001c6a:   subs r0, r0, r3
116
08001c6c:   adcs r2, r2
117
08001c6e:   lsrs r3, r0, #1
118
08001c70:   cmp r3, r1
119
08001c72:   bcc.n 0x8001c78 <__udivsi3+240>
120
08001c74:   lsls r3, r1, #1
121
08001c76:   subs r0, r0, r3
122
08001c78:   adcs r2, r2
123
08001c7a:   subs r1, r0, r1
124
08001c7c:   bcs.n 0x8001c80 <__udivsi3+248>
125
08001c7e:   mov r1, r0
126
08001c80:   adcs r2, r2
127
08001c82:   mov r0, r2
128
08001c84:   bx lr
129
08001c86:   b.n 0x8001c88 <__udivsi3+256>
130
08001c88:   push {r0, lr}
131
08001c8a:   movs r0, #0
132
08001c8c:   bl 0x8001c9c <__aeabi_ldiv0>
133
08001c90:   pop {r1, pc}
134
08001c92:   nop ; (mov r8, r8)

von Amateur (Gast)


Lesenswert?

Alle mir bekannten Divisionsroutinen waren als Schleife ausgeführt.
Subsequent wurde da, abhängig davon ob ein Bit gesetzt ist, gearbeitet, 
oder ein Teil übersprungen.
Das hat natürlich zur Folge, dass je nach "Bitmuster" die Zeit, die zum 
Grübeln benötigt wird, variiert.

Ist alles logisch, wenn man sich mal daran erinnert, wie man ohne 
Taschenrechner dividiert. Ham' wir alle mal - anno Tobak - gelernt.

von Stefan F. (Gast)


Lesenswert?

> Okay, aber wie ist das verhalten zu erklären?

Dividiere doch mal handschriftlich auf Papier. Du wirst sehen, daß der 
Zeitaufwand auch hier von den konkreten Werten abhängt.

Beim Computer ist der Grund prinzipiell der selbe.

von (prx) A. K. (prx)


Lesenswert?

Amateur schrieb:
> Alle mir bekannten Divisionsroutinen waren als Schleife ausgeführt.

Da kenne ich noch andere Varianten. Wobei in diesem Fall die Schleife 
entrollt ist.

von Matthias H. (streno)


Lesenswert?

Amateur schrieb:
> Alle mir bekannten Divisionsroutinen waren als Schleife ausgeführt.
> Subsequent wurde da, abhängig davon ob ein Bit gesetzt ist, gearbeitet,
> oder ein Teil übersprungen.
> Das hat natürlich zur Folge, dass je nach "Bitmuster" die Zeit, die zum
> Grübeln benötigt wird, variiert.
>
> Ist alles logisch, wenn man sich mal daran erinnert, wie man ohne
> Taschenrechner dividiert. Ham' wir alle mal - anno Tobak - gelernt.

Stefan U. schrieb:
> Dividiere doch mal handschriftlich auf Papier. Du wirst sehen, daß der
> Zeitaufwand auch hier von den konkreten Werten abhängt.
>
> Beim Computer ist der Grund prinzipiell der selbe.

Ja das würde ich ja verstehen.
Was ich aber nicht verstehe, dass es unter einem bestimmten Wert weniger 
lang dauert wie über einem bestimmten wert. Eurer theorie nach mpsste es 
ja in beiden bereichen(drüber,drunter) unterschiedlich lange dauern...

von Stefan K. (stefan64)


Lesenswert?

Welche Genauigkeit brauchst Du?

Ggf. kannst Du die Division durch eine Lookup-Tabelle ersetzen und damit 
um ein Vielfaches schneller werden. Natürlich brauchst Du dafür - je 
nach Genauigkeit - Einiges an Flash und es sollten nicht mehrere solche 
Berechnungen optimiert werden müssen.

Der Faktor k / Buffer[1] würde dabei in eine Tabelle ausgelagert.
Als Berechnung hast Du dann nur noch:

v = Buffer[0] * lookupTable[Buffer[1]];

Viele Grüße, Stefan

von (prx) A. K. (prx)


Lesenswert?

Matthias H. schrieb:
> Was ich aber nicht verstehe, dass es unter einem bestimmten Wert weniger
> lang dauert wie über einem bestimmten wert.

Die oben gezeigte Routine überspringt abhängig von den Werten einige 
Iterationen der entrollten Schleife.

von Stefan K. (stefan64)


Lesenswert?

Matthias H. schrieb:
> Ja das würde ich ja verstehen.
> Was ich aber nicht verstehe, dass es unter einem bestimmten Wert weniger
> lang dauert wie über einem bestimmten wert. Eurer theorie nach mpsste es
> ja in beiden bereichen(drüber,drunter) unterschiedlich lange dauern...

Dividiere mal von Hand 10.000 / 10 und 10.000 / 11 und messe nach, wie 
lange Du für jede dieser Divisionen brauchst.
Egal ob Du eine Division per Hand oder per Software ausführst: die Dauer 
wird massgeblich von den Werten abhängen.

Viele Grüße, Stefan

von (prx) A. K. (prx)


Lesenswert?

PS: Geh doch einfach mal per single step durch die Divisionsroutine 
durch. Einmal mit den schnellen und einmal mit den langsamen Werten. Und 
verfolge dabei, welchen Weg die diversen bedingten Sprünge darin nehmen.

: Bearbeitet durch User
von Matthias H. (streno)


Lesenswert?

Stefan K. schrieb:
> Der Faktor k / Buffer[1] würde dabei in eine Tabelle ausgelagert.
> Als Berechnung hast Du dann nur noch:
>
> v = Buffer[0] * lookupTable[Buffer[1]];

Stefan, das ist eine super Idee.

Allerdings hängt mein Faktor K von der Kalibrierung des AD-Wandlers zu 
Beginn ab und kann sich somit ändern. Eine Berechnung der LookupTable 
würde zu lange dauern, da Buffer[1] um 100 variiert...

von Theor (Gast)


Lesenswert?

Nimm als Beispiel die schriftliche Division wie sie in Europa in den 
Schulen gelehrt wird.

Dann tritt es auf, dass der Rest eines Zwischenschrittes, kleiner als 
der Divisor ist. Man nimmt also eine weitere Stelle hinzu um wieder eine 
Zahl zu erhalten, die grösser/gleich dem Divisor ist. Manchmal reicht 
das nicht und noch eine Ziffer muss dazu genommen werden. Dieses 
vergleichen von Rest und Divisor geht recht schnell und im Kopf, auch 
das hinzunehmen von weiteren Ziffern. Es geht aber langsamer als die 
eigentliche Division der soweit betrachteten Zahl.
Bei diesem Verfahren hängt die Geschwindigkeit nicht davon ab, wie groß 
der Divisor absolut ist oder wie groß er im Vergleich zum Dividend ist.

Klar, soweit?

Das ist nur mal eine prinzipielle Betrachtung. Wie das bei der 
Implementierung ist, hängt vom Verfahren ab. Auf dem Rechner gibt es 
mehrere Möglichkeiten eine Division effizient zu implementieren.


Nun noch eine Bemerkung zu diesen beiden Sätzen, wenn Du gestattest:

> ...dass es unter einem bestimmten Wert weniger
lang dauert wie über einem bestimmten wert.

> Eurer theorie nach müsste es ja in beiden Bereichen (drüber,drunter)
> unterschiedlich lange dauern ...

sagen das selbe aus, widersprechen sich gegenseitig nicht.

von Stefan K. (stefan64)


Lesenswert?

Matthias H. schrieb:
> Allerdings hängt mein Faktor K von der Kalibrierung des AD-Wandlers zu
> Beginn ab und kann sich somit ändern. Eine Berechnung der LookupTable
> würde zu lange dauern, da Buffer[1] um 100 variiert...

Du musst die Lookup-Table doch nur einmal nach der Kalibrierung rechnen. 
Und wenn Buffer[1] sich nur um den Faktor 100 ändert, dann bleibt die 
Lookup-Table im RAM auch überschaubar klein.

Viele Grüße, Stefan

von Amateur (Gast)


Lesenswert?

Dezimal Binär
529     1000010001     (3 Einser)
415     0110011111     (7 Einser)
Die Laufzeit der Division hat nur beschränkt etwas mit der Größe der 
Zahlen zu tun.
Bei der schrittweisen Abarbeitung von 529 wird nur Dreimal gearbeitet 
und Neunmal ein Teil übersprungen.
Bei 415 sieht das Ganze etwas anders aus. Obwohl, als Zahl gesehen 
dieser Wert kleiner ist als der Vorherige, wird hier Siebenmal 
gearbeitet und nur Dreimal etwas übersprungen.

Das Ganze ist zwar etwas vereinfacht, aber wenn du im oberen Falle mal 
die Laufzeiten ins Auge fasst, wirst Du sehen, dass die "größere" Zahl 
schneller bearbeitet wird wie die kleinere.
Oder Umgekehrt: Die Annahme, das größere Zahlen länger brauchen, wie 
kleinere, ist nicht richtig.

Der Mensch ist halt auf das dezimale System getrimmt.
Für ihn ist 500 eine gerade Zahl. Computy sieht das aber ganz anders. 
Für ihn ist 512 sehr gerade. 0111110100 (500d) : 1000000000 (512d).

von (prx) A. K. (prx)


Lesenswert?

Amateur schrieb:
> Bei der schrittweisen Abarbeitung von 529 wird nur Dreimal gearbeitet
> und Neunmal ein Teil übersprungen.

In
  08001c5a:   bcc.n 0x8001c60 <__udivsi3+216>
  08001c5c:   lsls r3, r1, #3
  08001c5e:   subs r0, r0, r3
wird entweder gesprungen oder es werden 2 Eintakter ausgeführt. Je nach 
Tempo des nichtsequentiellen Zugriffs kann das deutlich oder 
laufzeitneutral ausfallen. Der Unterschied kann daher auch davon 
abhängen, ob Code in RAM oder Flash.

Matthias H. schrieb:
> so dauert die Berechnung 0,5µs länger

Wieviel Takte sind das?

: Bearbeitet durch User
Beitrag #5099271 wurde vom Autor gelöscht.
von Volle22 (Gast)


Lesenswert?

Solche Division sind in der Regel nur nötig wenn du den Wert anzeigen 
willst.
Rechne einfach mit der Auflösung des ADC weiter.

Es ist ja schon eine Spannung in Volt. Halt mit einer unschönen 
Auflösung.

Deine anderen Spannungswerte z.B Grenzen  für Signal Range Check musst 
du halt auch in dieser Auflösung angeben.

Veränderung der Auflösung innerhalb des Programms ist nur ein Komfort 
für den Programmierer. Wenn Laufzeit kritisch ist sollte man sie nicht 
dafür verschwenden.

Umgerechnet wird beim Senden oder Ausgeben.

von Matthias H. (streno)


Lesenswert?

Ja solangsam dämmerts mir.

Auch der Schritt für Schritt durchgang von udivsi hat licht ins dunkle 
gebracht. Ich werden jetzt erstmal mit einer Lookup Table arbeiten, was 
mir wirklich Zeit einspart.

Danke für die vielen Erklärungen und Tipps!
Wirklich klasse Diskussion hier

von Axel S. (a-za-z0-9)


Lesenswert?

Matthias H. schrieb:
> Stefan U. schrieb:
>> Dividiere doch mal handschriftlich auf Papier. Du wirst sehen, daß der
>> Zeitaufwand auch hier von den konkreten Werten abhängt.
>> Beim Computer ist der Grund prinzipiell der selbe.
>
> Ja das würde ich ja verstehen.
> Was ich aber nicht verstehe, dass es unter einem bestimmten Wert weniger
> lang dauert wie über einem bestimmten wert. Eurer theorie nach mpsste es
> ja in beiden bereichen(drüber,drunter) unterschiedlich lange dauern...

Das ist auch so. Tatsächlich hängt die Laufzeit der Division nicht nur 
vom Divisor ab (weil du dich so an verschiedene Werte des Divisors 
klammerst) sondern sowohl vom Divisor als auch vom Dividenden.

Das ist ganz normal und sollte jedem Programmierer auch bekannt sein. 
Wenn dein Programm nur dann funktioniert, wenn die Laufzeit der Division 
immer gleich ist, dann funktioniert es nicht. Schreib es anders.

von Matthias H. (streno)


Lesenswert?

Volle22 schrieb:
> Solche Division sind in der Regel nur nötig wenn du den Wert anzeigen
> willst.
> Rechne einfach mit der Auflösung des ADC weiter.
>
> Es ist ja schon eine Spannung in Volt. Halt mit einer unschönen
> Auflösung.
>
> Deine anderen Spannungswerte z.B Grenzen  für Signal Range Check musst
> du halt auch in dieser Auflösung angeben.
>
> Veränderung der Auflösung innerhalb des Programms ist nur ein Komfort
> für den Programmierer. Wenn Laufzeit kritisch ist sollte man sie nicht
> dafür verschwenden.
>
> Umgerechnet wird beim Senden oder Ausgeben.

Ja aber ich habe eine Offsetkorrektur. Z.B will ich von dem Wert 2,5V 
abziehen. Woher weiß ich jetzt welcher digitale Zahlenwert 2,5V 
entspricht?

Mein VDDA, sprich die Referenz, varriert nämlich

von (prx) A. K. (prx)


Lesenswert?

Axel S. schrieb:
> Das ist ganz normal und sollte jedem Programmierer auch bekannt sein.
> Wenn dein Programm nur dann funktioniert, wenn die Laufzeit der Division
> immer gleich ist, dann funktioniert es nicht. Schreib es anders.

Wenn man in obigem __udivsi Code die Anfangstests weglässt, das also auf 
die klassische Divisions-Iteration reduziert, und ihn auf einem Cortex 
M0 ohne Waitstates ausführt (z.B. im RAM), ist der Code laut ARM-Doku 
laufzeitneutral. Der ausgeführte Sprung der entrollten Iteration dauert 
genauso lang wie der nicht ausgeführte Sprung plus die beiden Eintakter.

Eine sichere Bank für alle Lebenslagen ist das natürlich nicht.

: Bearbeitet durch User
von Amateur (Gast)


Lesenswert?

>Ich werden jetzt erstmal mit einer Lookup Table arbeiten, was
>mir wirklich Zeit einspart.

Vorsicht bissiger Hund!

Sind der FLASH-Speicher oder das RAM beschränkt, so bekommst Du bei der 
Verwendung von Tabellen sehr schnell dicke Backen (sehr Platzintensiv). 
Vor allem, wenn Du den Wertebereich nicht ordentlich einschränken 
kannst.

von (prx) A. K. (prx)


Lesenswert?

Amateur schrieb:
> Vorsicht bissiger Hund!

Und wenn man diesen Code dann auf einem ARM mit (Flash-, oder CPU-) 
Cache für diese Tabellenzugriffe laufen lässt, dann ists wieder Essig 
mit der exakten Reproduzierbarkeit der Laufzeit. Nur hängt das dann 
nicht nur von den Werten selbst ab, sondern auch noch von Wasserstand 
und Luftdruck.

: Bearbeitet durch User
von Josef G. (bome) Benutzerseite


Lesenswert?

Man kann eine Division auch so programmieren,
dass die Dauer unabhängig von den Daten wird.
Siehe mein 8bit-Computer-Projekt.

von Roland E. (roland0815)


Lesenswert?

> Ja das würde ich ja verstehen.
> Was ich aber nicht verstehe, dass es unter einem bestimmten Wert weniger
> lang dauert wie über einem bestimmten wert. Eurer theorie nach mpsste es
> ja in beiden bereichen(drüber,drunter) unterschiedlich lange dauern...

Programmiere doch mal selber eine Division in Assembler. Hinweis: 
Schriftliche Division wie in der 3. Klasse. Dann wird klar, warum die 
Regenzeit der Division unterschiedlich lang wird.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.