Forum: Mikrocontroller und Digitale Elektronik Schnelle Bitmaske in AVR-Assembler


von Jim Beam (Gast)


Lesenswert?

Tach allerseits.

Leider haben ATTINY keinen Barrel-Shifter.
Aus dem Wert der untersten 3 Bits von COUNT soll eine Bitmaske in DATA 
erstellt werden, count darf nicht geändert werden, eventuelle 
Hilfsregister müssen erhalten bleiben.
1
;-----------------
2
;Bisher:
3
;-----------------
4
BITMASK:     .DB 128,64,32,16,8,4,2,1
5
push   zh
6
push   zl
7
push   count
8
ldi    zl,LOW(2*bitmask)
9
ldi    zh,HIGH(2*bitmask)
10
andi   count,7
11
add    zl,count
12
adc    zh,NULL     ;NULL ist vordefiniertes Register
13
lpm    data,z
14
pop    count
15
pop    zl
16
pop    zh
17
18
;-----------------
19
;Nach etwas Kaffee:
20
;-----------------
21
ldi    data,128
22
sbrc   count,1
23
ldi    data,32
24
sbrc   count,0
25
lsr    data
26
sbrc   count,2
27
swap   data


Hat jemand noch eine "schnellere" Idee?
Danke fürs lesen!

von Karl M. (Gast)


Lesenswert?

Hallo,
basierend auf dieser C-Beschreibung
1
result = 128;
2
while (count > 0)
3
{
4
  result >>= 1;
5
  count --;
6
}

Ergibt sich:
1
ldi  result,128
2
tst  count
3
breq  exit
4
5
loop:
6
LSR  result
7
dec  count
8
brne  loop
9
10
exit:

von Jim Beam (Gast)


Lesenswert?

Karl, Danke.

Ja, so mag C das machen,
leider UM LÄNGEN langsamer als die bisherige Idee.
Wobei C da sicher zuvor noch eine Stack-Orgie feiert, die auch noch 
mitgerechnet werden müsste.

Zudem wird COUNT verändert und es wird nicht berücksichtigt,
dass die oberen COUNT-Bits ev. <>0 sind.

Da ich für jeden gesparten Takt dankbar bin,
noch mal die Frage nach besseren Ideen.

von spess53 (Gast)


Lesenswert?

Hi

>leider UM LÄNGEN langsamer als die bisherige Idee.

Woher kommt die Forderung nach 'möglichst schnell'?

MfG Spess

von Jim Beam (Gast)


Lesenswert?

spess53 schrieb:
> Woher kommt die Forderung nach 'möglichst schnell'?

Spielt das für die Fragestellung eine Rolle?
Um Takte zu sparen.

In einem komplexen System JEDE-EINZELNE Routine zu hinterfragen und 
vielleicht bei 20 Vorgängen jeweils paar Takte zu sparen kann "in Summe" 
sehr relevant sein wenn es z.B. um I/O-Timing geht und man bestehende 
Hardware möglichst optimal nutzen möchte.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Jim Beam schrieb:
>> Woher kommt die Forderung nach 'möglichst schnell'?
>
> Spielt das für die Fragestellung eine Rolle?

Ja.

Schnell und platzsparend sind in der Regel zwei sehr gegensätzliche 
Optimierungskriterien.

OK, die Forderung nach "schnell" stand im Eingangsposting. Nein, 
schneller als mit einer lookup table wird es nicht zu haben sein.

: Bearbeitet durch Moderator
von Jim Beam (Gast)


Lesenswert?

Jörg W. schrieb:
> Schnell und platzsparend sind

"Platzsparend" war ja in der Fragestellung GARNICHT erwähnt.
Auch nicht im Reply von Karl. Mit keinem Wort.
Wie kommst Du völlig grundlos auf diesen Nebenkriegsschauplatz?

von Jim Beam (Gast)


Lesenswert?

Jörg W. schrieb:
> schneller als mit einer lookup table wird es nicht zu haben sein.

DOCH:
Meine ZWEITE Idee IST ja schon schneller als die per Tabelle, wenn die 
Registersicherung eingerechnet wird, egal ob per push/pop oder Reg-Copy.

Ich fragte halt, ob jemand eine geniale Idee hat, dies weiter zu 
beschleunigen...

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Jim Beam schrieb:
> Wie kommst Du völlig grundlos auf diesen Nebenkriegsschauplatz?

Wie kommst du völlig grundlos zu dieser Aggressivität in der Sprache?

Ich hatte mein Posting mittlerweile schon abgeändert.

von Jim Beam (Gast)


Lesenswert?

Jörg W. schrieb:
> Wie kommst du völlig grundlos zu dieser Aggressivität in der Sprache?

Ich bitte um Verzeihung, hat sich überschnitten, wie an den Timestamps 
ja ablesbar ist...
War wirklich nicht böse gemeint, kommt hier halt ständig vor, dass auf 
Features geantwortet wird, die garnicht Teil der Frage sind.

von H.Joachim S. (crazyhorse)


Lesenswert?

"ATTiny" impliziert das schon irgendwie...
Reicht die aktuelle Lösung nicht? Oder willst du nur aus Prinzip eine 
schnellere Lösung? Man kann sehr viel Zeit in Optimierungen versenken, 
sehr oft ist es rein akademisch und wird dann in irgendeiner 
Warteschleife wieder verbraten.
Bekommt man das ganze nur mit ausgefeilten Tricks und Kniffen noch ins 
erforderliche Zeitraster, hat man schon viel früher was falsch gemacht.

Beitrag #5633290 wurde von einem Moderator gelöscht.
von Karl M. (Gast)


Lesenswert?

Hallo Jim Beam,

die iterative Lösung hatte ich nur der Vollständigkeit angegeben, ohne 
die notwendigen Register (Daten) zu sichern und ohne weitere Bedingungen 
zu kennen.

In meinen Programmen wähle ich im allg. auch den Zugriff auf eine 
Tabelle im Flashspeicher.
Also soweit eine gute Lösung.

Man könnte noch Daten im statisch im SRAM platzieren und über LD 
Rd,[Y,Z] oder LD Rd,X+ zugreifen.
Nur der Vollständigkeit halber, müsste man noch den Pointer [X,Y,Z] vor 
jedem Zugriff auf die Adresse des statischen Speicherbereichs setzen und 
den ihn einmalig mit {128,64,32,16,8,4,2,1} initialisieren.
Es ist aber nicht der allg. Weg, wie geschrieben.

Beitrag #5633332 wurde von einem Moderator gelöscht.
von Horst M. (horst)


Lesenswert?

Nochmal zurück zur Ausgangssituation:

Die zweite Lösung ist unter den gegebenen Umständen wohl nicht zu 
toppen, aber falls bei beim Drumherum noch etwas optimiert werden 
könnte...

Die Basis, wenn der Platz im Flash keine Rolle spielt:
1
  push ZL
2
  push ZH
3
  ldi ZH,HIGH(bitmask<<1)
4
  mov ZL,count
5
  lpm data,Z
6
  pop ZH
7
  pop ZL
8
9
  .org (PC+$7f) & $ff80 ;damit's an einer 256 Byte-Grenze losgeht
10
bitmask:
11
  .db 128,64,32,16,8,4,2,1  ;1
12
  .db 128,64,32,16,8,4,2,1  ;2
13
.
14
.
15
.
16
  .db 128,64,32,16,8,4,2,1  ;32
17
;die Tabelle kann ggf. optimiert werden, falls count niemals einen eingeschränkten Wertebereich überschreitet

Das sind 12 Takte.
Falls das alles in einer Schleife oder unrolled hintereinander liefe, 
könnte man vllt. auf das wiederholte Sichern der Z-Register verzichten.
Damit bliebe pro Umwandlung nur noch übrig
1
  mov ZL,count
2
  lpm data,Z
und wir landen bei 4 Takten, sind damit also schneller als mit der 
zweiten Lösung.

Wenn count gleich in ZL plaziert werden kann, spart man sich das mov 
ZL,count und somit noch einen Takt.
Und falls die Tabelle im RAM gehalten werden könnte (beim Tiny dann 
nicht mehr so wahrscheinlich), käme man mit
1
  ld data,X
beim ultimativen 2-Takter raus.

: Bearbeitet durch User
von A. S. (Gast)


Lesenswert?

Also, der TO hat hier eine geniale Lösung für eine typische 
Aufgabenstellung gefunden. Respekt.

Das es Assembler ist, dass es kein Durchbruch ist, dass es mit hohen 
Einmalkosten auch schneller gehen könnte, geschenkt.

Aber das hier vom TO als echte Suche nach realer Optimierung zu 
verkaufen, ist durchsichtig. Wenn Du nicht gerade einen webserver ins 
poor man's Oszilloskop einbauen willst.

Mit poor man's meine ich das hier:

http://www.dos4ever.com/uscope/uscope_e.html

Ein Oszilloskop mit on-screen Menü und Video-ausgang in einem kleinen 
Bruder der tinys

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


Lesenswert?

A. S. schrieb:
> Also, der TO hat hier eine geniale Lösung für eine typische
> Aufgabenstellung gefunden.

Nein, hat er nicht. Lookup-Tabellen gibt es seit ... schon immer? Und 
daß man den Zeiger auf so eine Tabelle nach Möglichkeit wiederverwendet, 
ist auch der Normalfall  (ok, dazu braucht man genug Register, aber die 
hat ein AVR ja). Daß man die Tabelle auf eine Seitengrenze legt und 
dadurch die Indizierung vereinfacht, habe ich schon vor >25 Jahren 
gesehen. Ebenfalls ein Standard-Konstrukt.

> Respekt.

Dafür nicht.

von Justin S. (Gast)


Lesenswert?

Jim Beam schrieb:
1
 ldi    data, 128
2
 sbrc   count, 1
3
 ldi    data, 32
4
 sbrc   count, 0
5
 lsr    data
6
 sbrc   count, 2
7
 swap   data

Genau nach so einer Lösung habe ich gesucht und finde, dass sie mehr 
Aufmerksamkeit verdient hat. Vor allem in Anbetracht dessen, dass der 
Standard-Compiler nur so etwas zustande bringt:
1
  unsigned char data = 0x01u << ( count & 0x07u );
2
     788:  98 2f         mov  r25, r24
3
     78a:  97 70         andi  r25, 0x07
4
     78c:  21 e0         ldi  r18, 0x01
5
     78e:  30 e0         ldi  r19, 0x00
6
     790:  01 c0         rjmp  .+2        ; 0x794
7
     792:  22 0f         add  r18, r18
8
     794:  9a 95         dec  r25
9
     796:  ea f7         brpl  .-6        ; 0x792

Konstant 7 Takte auf einem Atmega328 sind bei etwas zeitkritischeren 
Dingen besser als die Standardlösung mit 8 + 4 * count, also zwischen 8 
und 28, im Durschnitt 18, Ersparniss bei 16MHz etwa 0.7µs pro Vorkommen.

Klingt wenig und ist normalerweise unbedeutend, aber wenn es häufig 
vorkommt und dabei z.B. Latenz wichtig ist, dann gibt es andere Stellen 
im Code, deren Optimierung deutlich weniger als eine Verdopplung bringt.

von Justin S. (Gast)


Lesenswert?

Toll ist z.B. auch, dass einfach nur die drei "sbrc" durch "sbrs" 
ersetzt werden müssen, damit die von mir gewünschten Masken (Reihenfolge 
anders herum) erzeugt werden.

von H.Joachim S. (crazyhorse)


Lesenswert?

Naja, die Taktzählerei...
Klar, manchmal macht es Sinn, aber äusserst selten. Es gab Zeiten da war 
es fast unerlässlich weil es einfach nichts besseres/schnelleres gab.

Wer anfängt mit einem ATTiny und dann merkt dass er um einzelne Takte 
kämpfen muss hat im Vorfeld was falsch gemacht (Aufgabe nicht 
ausreichend beleuchtet und falschen MC gewählt) und jetzt versucht durch 
die Hintertür das noch zu retten.
Das wäre noch die Variante wo man geneigt sein könnte: der lernt noch, 
hilf ihm und zeige wie es besser geht (wenn nicht daraus die Erkenntnis 
entsteht: es gibt immer einen Kniff)

Der weit häufigere Fall ist der des Selbstdarstellers: ich kann es 
besser als alle anderen, ich kann es besser als der Compiler x, ich bin 
schon irgendwie toll.
Ja, in einzelnen kleinen Abschnitten kann man mit Optimierung was 
rausholen. In einem gar nicht mal so grossen Programm gewinnt dennoch so 
gut wie immer der Compiler wenn man nicht wirklich genial und auch noch 
sehr viel Zeit hat (dann sollte man aber eher dem Compilerbauer unter 
die Arme greifen als sich in gesparten Takten zu sonnen).

Die ganz grosse Frage: was macht man mit gesparten Takten? Im Grenzfall 
können sie das System retten, im allgemeinen aber werden sie dann in 
Warteschleifen irgendeiner Art wieder verbrannt. Nutzen Null, 
Erkenntnisgewinn klein bis kontraproduktiv.

Ja, ich komme noch aus einer Zeit wo man ein Minibetriebssystem in 2 
oder 4k quetschen konnte. Und mehr oder weniger sinnvolle Software in 
wenige kB passten. Ich gebe zu ich war auch stolz dass ging wo andere 
passen mussten. Aber es hat Zeit und Gehirnschmalz gekostet, die mit 
mehr Speicher und/oder mehr Rechenleistung mit weniger als 10% (eher 
noch viel weniger) des Aufwands das gleiche Resultat erzielt hätten.

Am Limit laufende Systeme sind immer Mist, jede weitere kleine Änderung 
kann zum Kollaps führen, und diese Gefahr wird i.a. nicht mal erkannt. 
Also will ich gar keine am Limit laufenden Projekte. Bei 50% zeitlicher 
Auslastung werde ich schon hellhörig. Da muss ich noch nicht tricksen, 
habe aber noch Luft.

von Justin S. (Gast)


Lesenswert?

H.Joachim S. schrieb:
> Am Limit laufende Systeme sind immer Mist

Bei schnellen Systemen kommt man gar nicht mehr auf die Idee, dass sie 
am Limit laufen könnten. Eigentlich nur wenn die Kühlung länger 
unangenehm laut wird, steigt manchmal noch die Aufmerksamkeit, dass es 
die verwendeten Algorithmen vielleicht doch auch in effizient gibt.

Bei modernen CPUs ist jede gesparte Instruktion gesparter Strom. 
Besonders auffällig bei Smartphones, wo manche Apps, ohne großartig 
etwas zu tun, den Akku in kürzester Zeit leersaugen (dahinter stecken 
oft Entwicklerkits, die den gleichen Code auf vielen Plattformen laufen 
lassen können, mit Tricks, dass sich einem die Haare sträuben) und 
andere Apps (oft native programmiert und sehr klein) tun viel, ohne dass 
man es am Akku oder der Wärme des Geräts merkt.

Ich denke, es ist gut, eine CPU oder MCU oder SOC zu verwenden, die zur 
Aufgabe passt, natürlich nicht unterdimensioniert ist (so dass man mehr 
optimiert als weiterentwickelt), aber eben auch nicht überdimensioniert 
(dann merkt man im schlimmsten Fall nicht, was man da gerade 
zusammenschraubt, Beispiel Suchalgorithmen: lineare Suche ist auf 
>3GHz-Maschienen genauso schnell wie ein effizienter Suchalgorithmus, 
wenn man nicht gerade Riesendaten bearbeitet, aber er saugt viel mehr 
Strom).

von Egon D. (Gast)


Lesenswert?

H.Joachim S. schrieb:

> Die ganz grosse Frage: was macht man mit gesparten Takten?

Nix. Man legt das System schlafen.

Die schnellste und energieeffizienteste Anweisung ist die,
die aufgrund der Optimierung des Algorithmus überflüssig
geworden und deshalb aus dem Programm herausgeflogen ist.

von Bitfrickler (Gast)


Lesenswert?

Jim Beam schrieb:
1
> BITMASK:     .DB 128,64,32,16,8,4,2,1
2
> push   zh
3
> push   zl
4
> push   count
5
> ldi    zl,LOW(2*bitmask)
6
> ldi    zh,HIGH(2*bitmask)
7
> andi   count,7
8
> add    zl,count
9
> adc    zh,NULL     ;NULL ist vordefiniertes Register
10
> lpm    data,z
11
> pop    count
12
> pop    zl
13
> pop    zh

Falls du ZL und ZH programmweit für diese eine Aufgabe reservieren 
kannst:
1
; BITMASK auf RAM-Adresse 0x100 legen oder auf irgendetwas anderes, wo die unteren 8 Bit immer 0x00 sind:
2
.dseg
3
.org 0x100
4
BITMASK:     .DB 128,64,32,16,8,4,2,1

Dann bleibt von dem ganzen Lookup-Code nur noch:
1
.cseg
2
push   count
3
andi   count,7
4
mov    zl,count
5
lpm    data,z
6
pop    count

ZH musst du natürlich einmalig in einer init-Routine initialisieren auf 
hi8(BITMASK).
Wenn du jetzt count noch programmweit fest immer in ZL halten kannst, 
dann fällt auch noch das mov weg.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Bitfrickler schrieb:
> Jim Beam schrieb:

Das schrieb er vor mehr als 3 Jahren.

Einfach erstmal alles lesen und nicht nur schnell noch auf den Zug 
aufspringen.

von Bitfrickler (Gast)


Lesenswert?

Jörg W. schrieb:
> Das schrieb er vor mehr als 3 Jahren.

Ja, und?
Ist mein Vorschlag deshalb falsch?

> Einfach erstmal alles lesen und nicht nur schnell noch auf den Zug
> aufspringen.

Warum musst du mich hier anpissen, Herr Oberlehrer-Moderator?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Bitfrickler schrieb:
> Jörg W. schrieb:
>> Das schrieb er vor mehr als 3 Jahren.
>
> Ja, und?
> Ist mein Vorschlag deshalb falsch?

Er wird keinen mehr interessieren.

Aber ja, bei dir wundert mich gerade gar nichts.

von Bitfrickler (Gast)


Lesenswert?

Jörg W. schrieb:
> Er wird keinen mehr interessieren.

Aha. Das legst du also einfach so fest und dann ist das so. Alles klar.
Da fragt man sich doch glatt, warum Justin schrieb:

Justin S. schrieb:
> Genau nach so einer Lösung habe ich gesucht und finde, dass sie mehr
> Aufmerksamkeit verdient hat.

komisch, wo es ihn doch laut deiner Aussage nicht interessiert.

Abgesehen davon habe ich den Thread vor meiner Antwort komplett 
gelesen. Deine Unterstellung, ich hätte das nicht getan, ist einfach nur 
unredlich.

von Peter D. (peda)


Lesenswert?

H.Joachim S. schrieb:
> Naja, die Taktzählerei...
> Klar, manchmal macht es Sinn, aber äusserst selten.

Das erinnert mich an meine ersten Schritte mit dem AT89C2051. Da hatte 
ich auch jeden Zyklus gezählt.
Später auf Arbeit hatte ich dann Zugriff auf den Keil C51. Ich hab das 
Programm umgeschrieben, es wurde kleiner und deutlich schneller. 
Zeitkritisch war durch die Umstellung des Ablaufs nichts mehr und ich 
konnte sogar weitere Funktionen hinzufügen.

Fazit:
Die beste Optimierung erreicht man mit Durchdenken des Ablaufs und nicht 
durch Mikrooptimierung einzelner Instruktionen.
Nebeneffekt: Bessere und kürzere Algorithmen sind oft auch besser 
lesbar, wenn man das Programm nach Jahren mal wieder betrachtet.
Mikrooptimierung muß dagegen immer auch kommentiert werden, warum man 
was und wie gemacht hatte.

https://devnarrative.com/mikrooptimierung/

"Premature optimization is the root of all evil."

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

Die Zeiten ändern sich halt. Früher(tm) galt sich selbst modifizierender 
Assemblercode oder sowas wie Duff's Device als hohe Programmierkunst.

Trotzdem ist die Lösung des TO für die Aufgabenstellung elegant und 
vollständig. Mit passenden Kommentaren gibt es keinen Grund, die nicht 
einzusetzen.

Oliver

Beitrag #6932093 wurde von einem Moderator gelöscht.
von Martin (Gast)


Lesenswert?

> "Premature optimization is the root of all evil."

Ist nur eine belanglose Meinung ohne jeglichen Tiefgang, da es in der 
Praxis der Programmierung auf die Umstände ankommt  (Speicher sparen, 
Zeit sparen, etc.).

> ... elegant und vollständig ...

So ist es.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Martin schrieb:
>> "Premature optimization is the root of all evil."
>
> Ist nur eine belanglose Meinung ohne jeglichen Tiefgang

Keineswegs.

Du hast das "premature" offenbar überlesen.

von (prx) A. K. (prx)


Lesenswert?

Oliver S. schrieb:
> Die Zeiten ändern sich halt. Früher(tm) galt sich selbst modifizierender
> Assemblercode oder sowas wie Duff's Device als hohe Programmierkunst.

Selbstmodifizierender Assemblercode ist bei AVRs immer noch hohe 
Programmierkunst. ;-)

von Peter D. (peda)


Lesenswert?

Oliver S. schrieb:
> Früher(tm) galt sich selbst modifizierender
> Assemblercode oder sowas wie Duff's Device als hohe Programmierkunst.

Ich kenne selbst modifizierenden Code nur zur Verschleierung als 
Kopierschutz, um das Debuggen zu erschweren. Ein Beispiel für einen 
wirklich sinnvollen Einsatz habe ich noch nie gesehen.

von (prx) A. K. (prx)


Lesenswert?

Peter D. schrieb:
> Ein Beispiel für einen
> wirklich sinnvollen Einsatz habe ich noch nie gesehen.

Fehlende Befehle. Intels 8080 hatte keine Befehle für indirekte I/O. 
Adressierung über (C) kam erst mit Z80.

Die 1-Chip-Implementierung des früher sehr erfolgreichen 
Microcontrollers Fairchild F8 hatte 64 Bytes ausführbares RAM am Ende 
des ROM-Adressraums. Sicherlich nicht ohne Grund.

Mangels Return-Stack bestand eine Methode von Unterprogrammaufrufen auf 
Computern der ersten Jahrzehnte darin, den passenden Rücksprungbefehl an 
den Anfang des Unterprogramms zu schreiben und dann an die darauf 
folgende Adresse zu springen.

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

(prx) A. K. schrieb:
> Selbstmodifizierender Assemblercode ist bei AVRs immer noch hohe
> Programmierkunst. ;-)

Wo ist das Problem ? ;)

Oliver

von Martin C. (Gast)


Lesenswert?

Peter D. schrieb:
> Ein Beispiel für einen
> wirklich sinnvollen Einsatz habe ich noch nie gesehen.

Im C64 gab es eine selbstmodifizierende Routine in der Zeropage, die das 
nächste Zeichen aus dem Basic-Programm holte. Dadurch konnte statt 
indirekter Adressierung die schnellere absolute Adressierung verwendet 
werden.

https://www.c64-wiki.de/wiki/CHRGET

von Jobst M. (jobstens-de)


Lesenswert?

Bitfrickler schrieb:
> Falls du ZL und ZH programmweit für diese eine Aufgabe reservieren
> kannst:

> Dann bleibt von dem ganzen Lookup-Code nur noch:
> .cseg
> push   count
> andi   count,7
> mov    zl,count
> lpm    data,z
> pop    count

Die Lösung vom TO ist schneller, Platzsparender und hat nicht die 
Einschränkungen (ZH fest belegt, Tabelle an einer festen Grenze im 
kleinen Speicher)


Jörg W. schrieb:
> Bitfrickler schrieb:
>> Jim Beam schrieb:
>
> Das schrieb er vor mehr als 3 Jahren.

Wieso machst Du ihn an? Er hat das Ding nicht wieder hervorgekramt!

Jörg W. schrieb:
> Er wird keinen mehr interessieren.

Dann können wir die ganzen alten Threads doch löschen. Dann verschwindet 
die ganze Problematik mit den ständig ausgegrabenen Leichen auch.
Und Justin der Übeltäter hätte vielleicht eine nicht so elegante Lösung 
bekommen.


Oliver S. schrieb:
> Wo ist das Problem ? ;)

Das Flash franst dabei so schnell aus ...


Gruß
Jobst

von Martin C. (Gast)


Lesenswert?

Oliver S. schrieb:
> (prx) A. K. schrieb:
>> Selbstmodifizierender Assemblercode ist bei AVRs immer noch hohe
>> Programmierkunst. ;-)
>
> Wo ist das Problem ? ;)
>
> Oliver

AVR-Programme laufen nur aus dem Flash, nie aus dem RAM 
(Harvard-Architektur?).

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Peter D. schrieb:
> Ein Beispiel für einen wirklich sinnvollen Einsatz habe ich noch nie
> gesehen.

Beim Z80 konnte man separaten Datenspeicher sparen, indem man den 
Direktoperanden eines Befehls als Variable missbraucht hat. Sowas wurde 
meiner Erinnerung nach selbst im CP/M-BDOS gemacht.

von Jobst M. (jobstens-de)


Lesenswert?

Martin C. schrieb:
> AVR-Programme laufen nur aus dem Flash, nie aus dem RAM
> (Harvard-Architektur?).

Und? Den kann man zur Laufzeit doch beschreiben!? ;-)

Gruß
Jobst

von Oliver S. (oliverso)


Lesenswert?

Jobst M. schrieb:
> Martin C. schrieb:
>> AVR-Programme laufen nur aus dem Flash, nie aus dem RAM
>> (Harvard-Architektur?).
>
> Und? Den kann man zur Laufzeit doch beschreiben!? ;-)

Eben. Bringt zwar die Einschränkung mit, daß man nur ganze pages 
schreiben kann, aber ansonsten ist das doch peanuts ;)

Oliver

von Martin (Gast)


Lesenswert?

Jörg W. schrieb:
> Martin schrieb:
>>> "Premature optimization is the root of all evil."
>>
>> Ist nur eine belanglose Meinung ohne jeglichen Tiefgang
>
> Keineswegs.
>
> Du hast das "premature" offenbar überlesen.

Habe ich nicht überlesen.

von Jobst M. (jobstens-de)


Lesenswert?

Oliver S. schrieb:
> Eben. Bringt zwar die Einschränkung mit, daß man nur ganze pages
> schreiben kann, aber ansonsten ist das doch peanuts ;)

Naja, Nullen kann man immer hinzufügen. Nur auf 1 setzen geht 
Seitenweise.
Dann braucht man eben noch ein System, mit dem man ungültige Stellen 
markieren kann, eine Art Dateisystem. Vielleicht sogar etwas Wear 
Levelling.
Und dann einen kleinen Kompiler auf dem System, welcher selbst Code 
dafür erstellen kann. Evtl. Multitasking?
Vielleicht etwas größeres nehmen. Raspi!? Ach - der macht das ja schon 
... :-D

Gruß
Jobst

von Bitfrickler (Gast)


Lesenswert?

Jobst M. schrieb:
> Die Lösung vom TO ist schneller

Ist sie das? Ich habe jetzt nicht mehr alle Instruktionslaufzeiten im 
Kopf, aber hat die zweite Lösung vom Threadersteller nicht 7 Takte und 
mein Vorschlag nur 6 Takte und mit dem count-nach-ZL-Vorschlag sogar nur 
5 Takte.

Dass mein Vorschlag deutlich eingeschränkter ist, ist ja klar.

von Jobst M. (jobstens-de)


Lesenswert?

Bitfrickler schrieb:
> Ist sie das?

Bitfrickler schrieb:
> push   count
2
> andi   count,7
1
> mov    zl,count
1
> lpm    data,z
3
> pop    count
2

=9

Bitfrickler schrieb:
> Ich habe jetzt nicht mehr alle Instruktionslaufzeiten im
> Kopf

Hättest Du aber schnell selber nachsehen können.


Gruß
Jobst

von Bitfrickler (Gast)


Lesenswert?

Jobst M. schrieb:
> Hättest Du aber schnell selber nachsehen können.

Ja, nach der Logik hättest du es auch direkt sagen können. Du wusstest 
es ja offenbar.

Warum wird man hier im Forum ständig grundlos unfreundlich angepampt?

von Oliver S. (oliverso)


Lesenswert?

Jobst M. schrieb:
> Bitfrickler schrieb:
>> Ist sie das?
>
> Bitfrickler schrieb:
>> push   count
> 2

oder 3, bei AVRs mit 3-Byte Programmzähler.

Oliver

von Bitfrickler (Gast)


Lesenswert?

Man muss count ja auch überhaupt gar nicht pushen, wenn man ZL/ZH 
sowieso reserviert:
1
.dseg
2
.org 0x100
3
BITMASK:     .DB 128,64,32,16,8,4,2,1
4
5
.cseg
6
mov    zl,count
7
andi   zl,7
8
lpm    data,z

5 Takte und damit schneller als die Lösung des Threaderstellers.

von Jobst M. (jobstens-de)


Lesenswert?

Bitfrickler schrieb:
> Man muss count ja auch überhaupt gar nicht pushen, wenn man ZL/ZH
> sowieso reserviert:
> .dseg
> .org 0x100
> BITMASK:     .DB 128,64,32,16,8,4,2,1
> .cseg
> mov    zl,count
> andi   zl,7
> lpm    data,z
>
> 5 Takte und damit schneller als die Lösung des Threaderstellers.

Und wenn man dann die Tabelle bei der Initialisierung ins RAM legt, 
spart man noch einen Takt.

Gruß
Jobst

von Yalu X. (yalu) (Moderator)


Lesenswert?

Bitfrickler schrieb:
> 5 Takte und damit schneller als die Lösung des Threaderstellers.

Dass man durch die Hinzunahme geeigneter Vorbedingungen einen Code
(fast) beliebig schnell machen kann, ist keine Überraschung. Allerdings
ist das auch oft mit Nachteilen verbunden. In deinem Fall sind dies die
folgenden:

Die globale Reservierung eines der drei wertvollen Registerpaare schlägt
bei einem nicht ganz trivialen Programm mit hoher Wahrscheinlichkeit an
anderer Stelle zurück, so dass sich deine "Optimierung" schnell ins
Gegenteil verkehrt.

Das 256-Byte-Alignment der Tabelle wird problematisch, wenn bspw. bei
einem ATtiny13 mehr als 75% des Flash für Programmcode genutzt werden.
Man kann dann den Code zwar in zwei Teile (vor und nach der Tabelle)
aufsplitten, allerdings ist er dadurch nur noch schwer wartbar.

Der Code des TE hingegen setzt überhaupt nichts voraus.

von Jobst M. (jobstens-de)


Lesenswert?

Yalu X. schrieb:
> Das 256-Byte-Alignment der Tabelle wird problematisch, wenn bspw. bei
> einem ATtiny13 mehr als 75% des Flash für Programmcode genutzt werden.

Man kann die Tabelle auch an das Ende eines Segments (Ende des ROMs) 
setzen und anstelle
1
andi   zl,7
1
ori    zl,248
schreiben.


> Der Code des TE hingegen setzt überhaupt nichts voraus.

Was ihn (den Code!) sehr sexy macht.


Gruß
Jobst

von Egon D. (Gast)


Lesenswert?

Peter D. schrieb:

> Das erinnert mich an meine ersten Schritte mit dem
> AT89C2051. Da hatte ich auch jeden Zyklus gezählt.
> [...]
> Die beste Optimierung erreicht man mit Durchdenken des
> Ablaufs und nicht durch Mikrooptimierung einzelner
> Instruktionen.

Ich finde, hier wird künstlich ein Widerspruch aufgebauscht,
der so gar nicht existiert.

Erstens spricht m.E. nichts dagegen, universelle Grund-
funktionen so weit als irgend möglich zu optimieren,
solange deren Universalität dadurch nicht leidet. Ob
eine eingesparte Mikrosekunde viel oder wenig ist, hängt
nämlich davon ab, wie lang die Routine insgesamt ist
und wie oft sie aufgerufen wird.

Zweitens kann Takte zählen gerade auf großen, modernen
Maschinen zu verblüffenden Einsichten führen, wenn man
ergänzend noch die reale Programmlaufzeit misst und
dann gründlich über die Diskrepanzen nachdenkt -- der
Datencache lässt grüßen...

von Bitfrickler (Gast)


Lesenswert?

Jobst M. schrieb:
> Und wenn man dann die Tabelle bei der Initialisierung ins RAM legt,
> spart man noch einen Takt.

Ok, ja. Mein Fehler. Bei meinem Beispiel liegt sie ja bereits im RAM. 
Also war LPM falsch.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Egon D. schrieb:
> Erstens spricht m.E. nichts dagegen, universelle Grund-
> funktionen so weit als irgend möglich zu optimieren

Wobei es fraglich ist, inwiefern man eine Funktion, die aus einem 
Eingangs-Wertebereich von exakt 0 bis 7 eine Bitmaske generiert (so cool 
die Lösung des TE durchaus ist), nun schon als "universelle 
Grundfunktion" durchgeht. ;-)

von c-hater (Gast)


Lesenswert?

Jörg W. schrieb:

> Wobei es fraglich ist, inwiefern man eine Funktion, die aus einem
> Eingangs-Wertebereich von exakt 0 bis 7 eine Bitmaske generiert (so cool
> die Lösung des TE durchaus ist), nun schon als "universelle
> Grundfunktion" durchgeht. ;-)

Du bist ja nicht mal in der Lage, die Funktion dieser einfachen Routine 
korrekt zu beschreiben und maßt dir an, Postings zu löschen. Mit welchem 
gottverdammten Recht? Fachliche Kompetenz kann es jedenfalls nicht sein!

Nein, natürlich macht die Routine deutlich mehr, da ihr 
Eingangs-Wertebereich 0..255 umfaßt.

Wäre anderweitig garantiert, dass tatsächlich nur Werte von 0..7 am 
Eingang vorkommen, gäbe es auch noch andere Lösungen, die die Effizienz 
der "Jim-Beam-Routine" erreichen.

Und Leute wie du sind wesentlich verantwortlich für den avr-gcc? Da 
wundert es echt niemanden, dass der so Scheiße ist, wie er halt ist...

von Egon D. (Gast)


Lesenswert?

Jörg W. schrieb:

> Egon D. schrieb:
>> Erstens spricht m.E. nichts dagegen, universelle Grund-
>> funktionen so weit als irgend möglich zu optimieren
>
> Wobei es fraglich ist, inwiefern man eine Funktion, die
> aus einem Eingangs-Wertebereich von exakt 0 bis 7 eine
> Bitmaske generiert (so cool die Lösung des TE durchaus ist),
> nun schon als "universelle Grundfunktion" durchgeht. ;-)

Gegenfrage: Wieso eigentlich nicht?

Ich bin nicht der Nabel der Welt; nicht alles, dessen
Nutzen ich nicht verstehe, muss deshalb gleich nutzlos
sein.
Andere finden es vielleicht nutzlos, alle geraden Bit-
positionen aus der X-Koordinate und die ungeraden aus
der Y-Koordinate zu gewinnen -- und doch berechnet man
den MORTON-Code genau so...


Davon abgesehen ging es mir eigentlich mehr um "Takte
zählen ist bäh!": Wie anders -- außer eben durch Takte
zählen, reale Laufzeit messen und beides vergleichen --
soll ich herausbekommen, wo sich die cache misses bei
den Datenzugriffen verstecken?
Wenn 10 Befehle in gemessenen 7 Taktzuständen ausgeführt
werden, dann ist auf dem uralten Athlon 64 das Ende der
Fahnenstange ziemlich nahe. Wenn aber 10 Befehle 20 Takte
dauern, gibt es noch Optimierungspotenzial.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

c-hater schrieb:
> Du bist ja nicht mal in der Lage, die Funktion dieser einfachen Routine
> korrekt zu beschreiben und maßt dir an, Postings zu löschen.

Gelöscht wird hier Moby, weil er Hausverbot hat.

Ansonsten hast du vom AVR-GCC und seiner Entwicklung offenbar keine 
Ahnung, aber das musst du auch nicht für solche Postings.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Egon D. schrieb:
> Ich bin nicht der Nabel der Welt; nicht alles, dessen
> Nutzen ich nicht verstehe, muss deshalb gleich nutzlos
> sein.

Ich habe ja auch nicht behauptet, dass hier irgendwas nutzlos wäre. 
Zwischen "nutzlos" und "universeller Grundfunktion" liegt aber ein recht 
großer Bereich.

(Übers Zählen von Takten habe ich mich eh nicht ausgelassen.)

von Egon D. (Gast)


Lesenswert?

Jörg W. schrieb:

> Egon D. schrieb:
>> Ich bin nicht der Nabel der Welt; nicht alles, dessen
>> Nutzen ich nicht verstehe, muss deshalb gleich nutzlos
>> sein.
>
> Ich habe ja auch nicht behauptet, dass hier irgendwas
> nutzlos wäre. Zwischen "nutzlos" und "universeller
> Grundfunktion" liegt aber ein recht großer Bereich.
>
> (Übers Zählen von Takten habe ich mich eh nicht ausgelassen.)

Ich meine mit "universell" nur "ist allgemeingültig".
Nicht universell ist, was nur in Spezialfällen klappt.

Wie oft man die konkrete Funktion praktisch benötigt,
ist mir erstmal egal. Eine Toilette braucht mach i.d.R.
auch nur selten -- dann aber u.U. dringend...

von mIstA (Gast)


Lesenswert?

Egon D. schrieb:
> Eine Toilette braucht mach i.d.R. auch nur selten

Also mehrmals täglich, an so gut wie jedem Tag Deines Lebens seit Du dem 
Kleinkindesalter entwachsen bist; also wirklich selten ist das jetzt 
nicht gerade.

Beitrag #6935490 wurde von einem Moderator gelöscht.
Beitrag #6935491 wurde von einem Moderator gelöscht.
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.