Forum: Mikrocontroller und Digitale Elektronik ASM-Nachoptimierer


von Stefan Helmert (Gast)


Lesenswert?

Hallo,

ich habe im PICmicro-Hochsprachenbuch gelesen, dass Compiler einen
nicht optimalen Code erstellen (obwohl manche 2300 Euro kosten).
Gibt es da so etwas wie einen Nachoptimmierer, der das erstellte
ASM-File in seiner Funktionsweise analysiert und anschließend das
optimale ASM-File generiert?
So könnte man jeden billigen Compiler und jede Hochsprache verwenden
und hätte immer die maximal mögliche Performance.

von OldBug (Gast)


Lesenswert?

Märchenstunde?

von Fritz Ganter (Gast)


Lesenswert?

Es ist genau umgekehrt:

Ein C-Compiler kann seinen Code mehr oder weniger optimieren, aber nur
aus dem C-Code raus.
Ein gcc optimiert z.B. deutlich besser als ich es in Assembler könnte -
was allerdings nicht viel heisst :-)
Allerdings baut er auch etwas an Overhead ein, den man beim händischen
programmieren einsparen könnte.
Du solltest dir mal eine Assemblerausgabe von einem C-Compiler ansehen,
da ist nicht mehr viel rauszuholen.

von Pimmelmann (Gast)


Lesenswert?

Hallo Stefan

Das gute an einer Hochsprache ist doch dass Sie einfach ist und man
sehr schnell Programmieren kann. Das hat natürlich den Nachteil dass
der Code nicht immer optimal ist.
Du willst den Fünfer und das Brötchen.

Anders gefragt braucht man einen Opimierten code?
Ich sage mal relativ selten wenn es um geschwindigkeit geseht eherer
wenn es um Speicherplatz geht.

Das einfachste ist du schreibst den Code mit einer Hochsprache und
lässt in in Assebler übersetzen und dann optimiert du den code von
Hand.

Gruss

Pimmelmann

von Thorsten (Gast)


Lesenswert?

Hallo,

ergänzend hierzu hätte ich folgende Frage bzw. Handoptimierung.
Angenommen ich habe folgene Funktion mit inlines:

void Test(void)
{
   bla...
   bla...

   asm("ldi r10, 10")
   ...
}

Ist das denn zulässig ? Sprich, kann ich davon ausgehen, daß r10
benutzt werden darf ? Oder muß ich vorher r10 sichern, oder erkennt das
mein C-Compiler ? Wie macht man das am besten ?

Thorsten

von Stefan Helmert (Gast)


Lesenswert?

Eigentlich wäre es doch gut wenn der Optimiere den vollen Speicherplatz
ausnutzen würde und die Geschwindigkeit optiemiert, damit man einen
langsameren Quartz nehmen kann um Strom zu sparen.
Dabei sollte der Optimierer aber intelligent vorgehen z.B. die selten
genutzten Routienen Speicherplatzoptimieren und die oft benötigten
geschwindigkeitsoptimieren. Aber er soll genau analysieren, d.h. wenn
in der Hochsprache ein float definiert wurde, der aber nur die Werte 1
und 2 annimmt und diese Variable aber auch noch mit einer anderen
multipliziert wird, so wird stattdessen ein bit verwendet und zum
multiplizieren der rlf-Befehl genutzt.

von Peter D. (peda)


Lesenswert?

Ich hatte auch irgendwo mal Werbung für einen 8051-Optimierer gesehen
mit märchenhaften Kompressionsraten und Geschwindigkeitszuwächsen.

Ist warscheinlich sowas wie die RAM-Doubler:
Man will das Geld von Leuten abgreifen, die sich nicht so auskennen.
Und dann verschwindet diese Firma sang und klanglos schnell wieder.


Die beste Optimierung kann nur der Programmierer und der Compiler
selber machen, da er noch alle Informationen hat.

Ist erstmal Assembler draus entstanden und sind alle Adreßbezüge
aufgelöst, ist alles vorbei. Zu erkennen, wie man nach Rausstreichen
oder Umstellen einer Codesequenz alle Adreßbezüge korrigieren muß, kann
kein noch so intelligentes Programm der Welt schaffen.


Peter

von Stefan Helmert (Gast)


Lesenswert?

Ich meine das so:
Es ist doch egal wie ein Programm geschrieben ist ASM, C, Basic. Es
kommt theoretisch nur auf die Funktion an und diese soll der
Nachoptimierer herausfinden und ein Programm generieren, dass
effizienter arbeitet. Und wenn das Programm in C geschrieben und
Compiliert wurde hat es doch die gleich Funktion nur der genaue Ablauf
ist anders. Der Nachoptimierer soll das Programm nicht umschachteln,
sondern im ersten Schritt herausfinden wie das Programm reagiert und
anschließend weg von diesem Programm einen völlig anderen (optimalen)
Code erstellen.

von Fritz Ganter (Gast)


Lesenswert?

Sagen wir es mal so:

Ich habe mir grad was ausgerechnet, es kommt 23 heraus.
Kannst du bitte meine Rechnung optimieren?

von OldBug (Gast)


Lesenswert?

Ist das ne Fangfrage?
Kommen jetzt die Illuminaten? g

 :-)

von Stefan Helmert (Gast)


Lesenswert?

23 ist hier ja schon das Optimum. Wenn aber der ASM-Programmierer oder
Compiler den µC stattdessen immer deine berechnung macht z.B.
x = x + 20
x = x + 3
anstelle von:
x = x + 23
ist das schon mist.
oder wenn man in der Hochsprache schreibt:
konst = 23

schleife
x = x + konst
Befehle...
goto schleife

und die Variable konst wird nur einmal gesetz und nie verändert, so das
man auch schreiben könnte:

x = x + 23

anstelle von:

x = x + konst

so müsste der µC für x = x + konst mehr rechnen als für x = x + 23
weil konst ja erst beim PIC ins W-Register geladen werden musst.
Und genau solche Unsinnigkeiten (die ja auch ein Mensch lösen könnte
obwohl dieser selbst das Programm nicht erfunden hat) soll der
Nachoptimierer finden.

von Matthias (Gast)


Lesenswert?

Hi

der SDCC ermöglicht tatsächlich soetwas. Die nennen das peep hole
optimizer. Hier wird nach der Kompilation des C-Quelltextes der
ASM-Coder (vor dem eigentlichen Assemblieren) nochmal geparst und
irrige Konstruktionen wie

mov R1,#40
orl R1,#30

zu einer Instruktion zusammengefasst. Das ganze ist regelbasiert und
kann beliebig erweitert werden.

Matthias

von Gerhard Gunzelmann (Gast)


Lesenswert?

Hallo Beisammen

ich meine daß man bei einem Hochsprachenprogramm unterscheiden muß in
die reinen Hochsprachenbefehle wie if, while, for und die Benutzung der
Bibliotheken. Bei ersteren kann der Compiler optimieren, bei der
Verwendung der Bibliothek nicht. Natürlich könnte der Compiler
optimieren wenn da steht
x = x + 20
x = x + 3

aber ich denke daß der Compiler-Hersteller (ganz oben) mit nicht
optimal meint, daß die Verwendung der Bibliothek unvermeidlich einen
Funktions-Overhead mit sich bringt, den der Compiler gar nicht
beeinflussen kann. Ich meine z.B. wenn ich rechen a = 5 + 7, so wird
hier die gleiche Funktion verwendet, als wen ich rechne a = 2300 +
4500. Ersteres könnte man mit einem einzigen Assemblerbefehl
erschlagen, das 2. Beispiel kann ein 8-Bitter nicht mehr mit einem
Befehl erschlagen und hier ist die Bibliothek auch für ausgelegt, also
für Integer, LONG Integer oder was noch schlimmer ist: float oder
double. Das ist nur ein Beispiel, das trifft für andere
Bibliotheksfunktionen - nicht nur für mathematische - zu.

Gerhard

von Fritz Ganter (Gast)


Lesenswert?

Das bringt doch alles nix.
Wenn jemand z.B. für den gcc Verbesserungsmöglichkeiten sieht, dann
soll er das doch den Entwicklern mitteilen.
Dann haben alle was davon.

Mir sind noch keine offensichtliche Dummheiten (mit eingeschalteter
Optimierung) aufgefallen, aber ich hab auch keine Übung im Assembler.

von Gerhard Gunzelmann (Gast)


Lesenswert?

Hallo Fritz

ich habe schon einige Steuerungen mit uC gebaut (PIC), ich verwende
dabei immer Assembler. Mein Arbeitskollege hat sich vor kurzem mit nem
ST62 und C rumgeärgert. Soll heissen er mußte eine Änderung an einem
bestehenden Programm durchführen, aber nach der Änderung hat das Timing
einiger Funktionen nicht mehr gepasst. Da genau ist der eigentliche
Vorteil des Assemblers. Gerade wenn sich ein Compiler "anmaßt" zu
optimieren kann bei Steuerungen Mist rauskommen. Das ist was mir zum
Thema Optimieren einfällt. Für mich gilt: Wenns nur um Bit-Klemptnerei
geht nimm ich nur Assembler - da weiß ich genaus wann was passiert. Nur
wenn's Rechen-intensiev wird, kommt für mich ein C-Compiler in Frage.

Gerhard

von OldBug (Gast)


Lesenswert?

Wie schon erwähnt: Märchenstunde...

Und ich behaupte sowas nicht ienfach so, sondern ich bin auch in der
Lage das zu beweisen *g*:

int
main(void)
{
  int a = 23;
  int b = 0;
  int c = 0;

  return (c = a + b);
}

Wird von avr-gcc übersetzt mit (aus dem Listfile):

    ldi r24,lo8(23)
    ldi r25,hi8(23)

Gute Compiler können das also ;)

von Stefan Helmert (Gast)


Lesenswert?

Nein total schlecht machen die Compiler das nicht, aber es könnte besser
gehen. Genau so wie man ein Gleichung auflösen (vereinfachen) kann, so
müsste doch das auc mit dem Programmcode gehen z.B. wenn man ein float
verwendet anstelle des eigentlich auch geeigneten Bytes oder wenn ein
aufwendige Divisionsroutine in den Programmspeicher geschrieben und
dann ausgeführt wird auch wenn nur die ein Division x=x/2 im Programm
steht wofür nur ein rrf-Befehl nötig ist.

von Gerhard Gunzelmann (Gast)


Lesenswert?

Hallo oldbug

das ist eben kein guter Compiler. Aber sowas erfordert auch Intelligenz
und das fehlt Computern und Software eben. Es gibt aber und dies
bereits schon länger, oprimierende Compiler. Borland C++ - Fans läßt
die Optimierfreudigkeit dieses Compilers oft die Haare zu Berge stehen.
So hat "optimiert" dieser Compiler:

for( i = 0; i < 10000; i++ );

zu nix.

Der erkennt, daß da keine Funktion in der for-Schleife steht, schmeißt
sie raus und futsch ist die "schöne" Verzögerung. Aber es gibt auch
so Superteile wie für den Intel-Risc-Prozessor I860. da optimiert der
Compiler sogar die Registerverwendung.

Gerhard

von OldBug (Gast)


Lesenswert?

Nana, deklarier i volatile und die Schleife bleibt :-)

Unsinnige (und das ist diese Schleife für den Compiler nun mal, da sie
nichts macht, ausser Zeit zu verschwenden) Schleifen werden
wegoptimiert.

von OldBug (Gast)


Lesenswert?

btw: würd mich wundern, wenn der avr-gcc die Registerverwendung nicht
optimieren würde...

von Fritz Ganter (Gast)


Lesenswert?

Kann mir mal jemand einen vollständigen C-Code aufschreiben, der
optimiert werden soll?

Bei meinen Kurz-Testprogramm optimiert er immer alles weg, bzw. rechnet
es schon zur compilierzeit aus :-(

von OldBug (Gast)


Lesenswert?

Fritz: meiner ist doch vollständig...!?

von Matthias (Gast)


Lesenswert?

Hi

@Stefan
x=x/2; optimiert der gcc durchaus zu x>>=1;

Matthias

von Fritz Ganter (Gast)


Lesenswert?

@AlterKäfer:

Ich meinte einen Code, wo man mehr sieht. Bei mir macht er z.B. aus
int test(int x)
{
    return (x/2);
}

int
main(void)
{
  int a = 23;
  int b = 8;
  int c,i;

  c=test(b);

  for (i=0;i<a;i++)
    c=c+b+i;

  a=a+b;
  a=a/2;

  return (a);

}


diesen Code (sogar der Funktionsaufruf wird wegoptimiert):

   1:x.c           **** int test(int x)
   2:x.c           **** {
  42                 .LM1:
  43                 /* prologue: frame size=0 */
  44                 /* prologue end (size=0) */
   3:x.c           ****     return (x/2);
  46                 .LM2:
  47 0000 97FD          sbrc r25,7
  49                 .LM3:
  50 0002 0196          adiw r24,1
  51                 .L2:
   4:x.c           **** }
  53                 .LM4:
  54 0004 9595          asr r25
  55 0006 8795          ror r24
  56                 /* epilogue: frame size=0 */
  57 0008 0895          ret
  58                 /* epilogue end (size=1) */
  59                 /* function test size 6 (5) */
  61                 .Lscope0:
  64                 .global  main
  66                 main:
   5:x.c           ****
   6:x.c           **** int
   7:x.c           **** main(void)
   8:x.c           **** {
  68                 .LM5:
  69                 /* prologue: frame size=0 */
  70 000a C0E0          ldi r28,lo8(__stack - 0)
  71 000c D0E0          ldi r29,hi8(__stack - 0)
  72 000e DEBF          out _SP_H_,r29
  73 0010 CDBF          out _SP_L_,r28
  74                 /* prologue end (size=4) */
   9:x.c           ****   int a = 23;
  10:x.c           ****   int b = 8;
  11:x.c           ****   int c,i;
  12:x.c           ****
  13:x.c           ****   c=test(b);
  14:x.c           ****
  15:x.c           ****   for (i=0;i<a;i++)
  76                 .LM6:
  77 0012 80E0          ldi r24,lo8(0)
  78 0014 90E0          ldi r25,hi8(0)
  79                 .L8:
  81                 .LM7:
  82 0016 0196          adiw r24,1
  83 0018 8731          cpi r24,23
  84 001a 9105          cpc r25,__zero_reg__
  85 001c E4F3          brlt .L8
  16:x.c           ****     c=c+b+i;
  17:x.c           ****
  18:x.c           ****   a=a+b;
  19:x.c           ****   a=a/2;
  20:x.c           ****
  21:x.c           ****   return (a);
  22:x.c           ****
  23:x.c           **** }
  87                 .LM8:
  88 001e 8FE0          ldi r24,lo8(15)
  89 0020 90E0          ldi r25,hi8(15)
  90                 /* epilogue: frame size=0 */
  91 0022 00C0          rjmp exit
  92                 /* epilogue end (size=1) */
  93                 /* function main size 13 (8) */

von Stefan Helmert (Gast)


Lesenswert?

gibt es auch compiler, die die Verwendung von Interrupts, Timer und
andere Peripherie verändern? Und Unterprogramme völlig auseinander
reißen und nicht in Unterprogrammen stehende Teile in Unterprogramme
schreiben und entscheiden ob etwas im µC berechnet wird oder in der
Tabelle nachgeschaut wird?

von Fritz Ganter (Gast)


Lesenswert?

Ja, sowas gibts, nennt sich "Programmierer" :-)

von Fritz Ganter (Gast)


Lesenswert?

Eine Übersicht, was der gcc3.4 optimiert findet man auf:

http://gcc.gnu.org/onlinedocs/gcc-3.4.0/gcc/Optimize-Options.html#Optimize%20Options

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?

@Gerhard:
> Hallo oldbug
>
> das ist eben kein guter Compiler

Warum denn nicht? Es wurde doch alles auf minimale Codegröße reduziert.
Und dass der Compiler eine sinnlose Schleife wegoptimiert ist doch nur
verständlich.

Optimierung auf Maschinencode-Ebene macht der GCC, nennt sich Peephole
Optimizer.

@Stefan Helmert:
> gibt es auch compiler, die die Verwendung von Interrupts, Timer und
> andere Peripherie verändern?

Mit Sicherheit nicht, ich bezweifle dass ein Programm hier bessere
Lösungen finden könnte als ein Mensch.

von Hagen (Gast)


Lesenswert?

Nun, und ich würde behaupten das es technisch möglich ist
Compiler-Maschinen zu bauen die immer besseren Code erzeugen als es der
Mensch je könnte. Es gibt dazu schon einige Ansätze die zB. mit Hilfe
von genetischen Algorithmen mathematische Formeln optimieren oder sogar
den Programmcode für hocheffiziente Formeln ermitteln. Deren Resultate
könnte ein Mensch in der Geschwindigkeit nicht erreichen. Die erzielten
Resultate finden meistens auch ein Optimum in einen enorm großen
Suchraum. Angenommen man hat einen Assemblercode mit 126 Bytes Code so
könnte eine sehr sehr schlechte Maschine alle möglichen Kombinationen
von gültigen Code durchprobieren. So ähnlich kann man sich die
genetischen Algortihmen vorstellen, nur eben das sie den maximalen
Suchraum enorm effizient und schnell durch Selektion begrenzen und
durchsuchen können. Ein gleichgelagertes Problem ist das Schachspiel.
Auch hier wurde lange Zeit behauptet das ein Computer niemals ein
Großmeister schlagen kann. Nun heutzutage behauptet man schon das ein
Großmeister die Schachprogramme in naher Zukunft nicht mehr schlagen
kann. Auch beim Schach geht es darum aus einem rießigen Suchraum den
Besten Zug zu finden. Übertragen auf einen Optimierer hiese das das man
aus einem Suchraum von gültigen Maschinencode den Code finden soll der
am effizientesten ist.

Es stellt sich nun nur noch die Frage ob es schon Forschungen in dieser
Richtung gibt, ob ein solches Verfahren effizienter als der Mensch ist
und ob man bei der heutigen "Resourceverschwendung" überhaupt so ein
Verfahren benötigt.

Durchschnittlich gesehen übertreffen heutige Compiler schon jeden
Mesnchen. Sie sind umgerechnet auf den Zeit-/Kosten-/Wissensaufwand
heute schon effizienter.

Gruß Hagen

von Fritz Ganter (Gast)


Lesenswert?

Ich habe ja schon immer gesagt, dass nur ein Zufallsgenerator der den
Hauptspeicher mit zufälligen Bytes füllt reicht um alle möglichen
Programme zu schreiben, sogar total bug-frei. Das Problem ist nur, die
nicht-funktionierenden Programme auszuselektieren :-)

von OldBug (Gast)


Lesenswert?

Ich muss Hagen insofern zustimmen, daß es wohl möglich ist, die
Optimierer noch ganz wesentlich zu verbessern! Allerdings wird sich
wohl niemand daran aufhalten, einen "Nachoptimierer" zu
programmieren. Der effektivste weg wird immer noch der vom Sourcecode
bis zum Binary sein!

von Matthias (Gast)


Lesenswert?

Hi

@Fritz
Nicht nur das aussortieren ist das Problem. Wenn wir mal von 8 KByte
Speicher ausgehen (also eine angenehme Speichergröße die man zur Not
noch mit ASM füllen könnte) macht das 64KBit. Wir haben also 65536 Bits
die entweder '0' oder '1' sein können. Also haben wir 2^65536
mögliche Kombinationen. Diese Zahl hat 19729 Dezimalstellen. Das
würde ich als Problem bezeichenen :-)

Matthias

von Hagen (Gast)


Lesenswert?

@Matthias: korrekt, aber so unwahrscheinlich es klingen mag, die Natur,
sprich Evolution hat 22 Chromosomen mit Milliarden von Genen zu
optimieren und hat es in 1 Millionen Jahre geschafft günstige
Kombinationen zu finden die den Menschen ausmachen. Exakt das
"Herausfilteren" der potentesten = optimalen Kombinationen aus einem
schier unvorstellbar großen Suchraum = max. Kombinationen ist die
Stärke von genetischen Algorithmen und Evolutionsstrategieen. Sie
werden dann EIN mögliches Optimum als Lösung finden, diese Optimum muß
aber nicht zwangsläufig DAS Maximum darstellen. Viele Info's findet
man dafür beim sogenannten Problem des Handlungsreisenden
"Tavelsman/Salesman Problem".

Die genetischen Algos. zur Formelerzeugung mit denen ich experimentiert
habe enthielten in ihrem Genaufbau = Allelen die Regelwerke welche
Formeloperaoren, Konstanten und Variablen wie korrekt zusammenzubauen
sind. Dies ist 1 zu 1 übertragbar auf die Assembler-Befehle. Somit
würde man über das clevere Design der Gene die Kombinatonsvielfalt von
untauglichen Assemblerkombinationen von vornherein ausschließen. Das
einzigste was der Programmier bei solchen Verfahren eingeben muß ist
die Selektions-Funktion. Diese entscheidet zu welchem Progranm sich die
Zufallserzeugten Individuen entwickeln werden. Aber exakt die klare
Definition einer solchen Selektions-Funktion ist das schwierige
Problem.

Gruß Hagen

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.