360 likes | 489 Views
Code-Optimierung. Philipp Bergener Seminar „Übersetzung künstlicher Sprachen“. Agenda. Einführung Grundlagen Datenflussanalyse Methoden zur Code-Optimierung Zusammenfassung. Ziele und Anforderungen. Verbesserung des Codes, der bei direkter, „naiver“ Übersetzung entsteht
E N D
Code-Optimierung Philipp Bergener Seminar „Übersetzung künstlicher Sprachen“
Agenda • Einführung • Grundlagen • Datenflussanalyse • Methoden zur Code-Optimierung • Zusammenfassung
Ziele und Anforderungen • Verbesserung des Codes, der bei direkter, „naiver“ Übersetzung entsteht • Optimierungsmöglichkeiten • Verbesserung der Ausführgeschwindigkeit • Verringerung des Speicherbedarfs zur Laufzeit • Verkleinerung des erzeugten Codes • Anforderungen • Semantik des Programms muss erhalten bleiben • Aufwand für Optimierung muss sich lohnen
Systematisierung • Nach Zeitpunkt für Code-Optimierung: • Nach Zwischen-Codeerzeugung: Eher allgemeine Methoden • Nach Maschinen-Codeerzeugung: Eher maschinenspezifisch • Globale vs. lokale Optimierung • Lokal: Nur Ausschnitt (Basisblock) wird betrachtet • Global: Mehrere Basisblöcke werden betrachtet
Agenda • Einführung • Grundlagen • Datenflussanalyse • Methoden zur Code-Optimierung • Zusammenfassung
Drei-Adress-Code • Zur Darstellung des Zwischencodes • Maximal 3 Adressen pro Befehl • Typen von Befehlen • Zuweisung: x = y op z op: arithmetisch (+,-,*,/) oder logisch (AND, OR) • Kopieranweisungen: x = y • Unbedingte Sprünge: goto L (L Sprungmarke) • Bedingte Sprünge: if x goto L bzw. ifFalse x goto L oder if x relop y bzw. ifFalse x relop y (relop: <, ==, >, etc.) • Indizierte Kopieranweisungen: x = a[i] bzw. a[i] = xa[i]: Speicherstelle i Einheiten hinter a
Basisblöcke und Flussgraphen Basisblöcke • Strukturieren den Code • Eigenschaften eines Basisblocks: • Kann nur durch die erste Anweisung betreten werden • Wird ohne Sprünge durchlaufen. Sprünge nur in der letzten Anweisung • Ein Basisblock beginnt • Bei der ersten Codezeile • Bei Zeilen, die Ziel von Sprüngen sind • Bei Zeilen, die auf Sprünge folgen Flussgraphen • stellen Kontrollfluss grafisch dar • Basisblöcke sind Knoten • 2 zusätzliche Knoten Eingang und Ausgang
Beispiel: Binäre Suche Eingang publicIngeger binsearch(Integer k) { Integer m,l,r; //Anfang l = 0; r = N-1; do{ m = (l+r)/2; if(k < a[m]) r = m-1; else l = m+1; while((k != a[m])||(l<r)); //Ende ... end B1 l = 0 r = N-1 B2 t1 = l+r m = t1/2 t2 = 8*m t3 = a[t2] ifFalse k<t3 goto B4 B3 r = m-1 goto B5 B4 l = m+1 B5 t4 = 8*m t5 = a[t4] ifFalse k==t5 goto B2 B6 if l<r goto B2 Ausgang
Agenda • Einführung • Grundlagen • Datenflussanalyse • Methoden zur Code-Optimierung • Zusammenfassung
Datenflussanalyse • Viele Optimierungsansätze brauchen Informationen über Datenfluss im Programm • Programm ist eine Folge von Zuständen • Zustand: unter anderem aktuelle Werte der Variablen • Anweisungen sind Zustandsübergänge • Durch Sprünge sind Zustände nicht immer eindeutig • Nur der für ein Problem relevante Teil des Zustandes betrachtet • Es wird nicht unterschieden auf welchem Pfad ein Programmpunkt erreicht wird
Bestandteile eines Datenflussproblems • Datenflussproblem ist 4-Tupel (D, V, ^, F) • Datenflussrichtung D {Vorwärts, Rückwärts} • Gibt an ob Informationen mit oder gegen Informationsfluss fließen • Domäne V • relevanter Teil des Zustandes • Konfluenzoperator ^ (z.B. ) • Zum Übergang zwischen Blöcken • Menge von Transferfunktionen F • F: V V • Für ganze Basisblöcke
Verfügbare Definitionen • Fragestellung: Welche Definition für eine Variable ist an einem bestimmten Programmpunkt gültig • Datenflussrichtung: D = Vorwärts • Domäne: Potenzmenge der Menge aller Definitionen • Konfluenzoperator: , also IN[B] = P Vorgänger von BOUT[P] • Transferfunktion: • genB: Menge generierter Definitionen in Block B • killB: Menge gelöschter Definitionen in Block B • Dann OUT[B] = genB (IN[B] - killB)
Verfügbare Definitionen ... gen[B1]={d1, d2} kill[B1]={d7,d8} B1 l = 0 (d1) r = N-1 (d2) B2 ... B3 gen[B3]={d7} kill[B3]={d2} r = m-1 (d7) goto B5 B4 gen[B4]={d8} kill[B4]={d1} l = m+1 (d8) ...
Aktive Variablen • Eine Variable x ist aktiv an Punkt p, wenn der Wert von x in p entlang eines Pfades von p an benutzt kann und wird. Andernfalls ist sie passiv. • Datenflussrichtung: D = Rückwärts • Domäne: Potenzmenge der Menge aller Variablen • Konfluenzoperator: , also OUT[B] = P Nachfolger von BIN[P] • Transferfunktion: • defB: Menge der Variablen, die in Block B definiert werden • useB: Menge der Variablen, die in B benutzt werden (bevor sie neu definiert werden) • Dann: IN[B] = useB (OUT[B] - defB)
Verfügbare Ausdrücke • Ein Ausdruck der Form x op y ist an einem Punkt pverfügbar, wenn der Ausdruck auf jedem Pfad zu p berechnet wird und weder x noch y danach überschrieben werden • Datenflussrichtung: D= Vorwärts • Domäne: Potenzmenge der Menge aller Ausdrücke • Konfluenzoperator: also IN[B] = P Vorgänger von BOUT[P] • Transferfunktion: • e_genB: Menge aller Ausdrücke, die in B (neu) berechnet werden • e_killB: Menge aller Ausdrücke, die in B ungültig werden • Dann: OUT[B] = e_genB (IN[B] - e_killB)
Halbverband • Ein Datenflussproblem bildet einen Halbverband • Eigenschaften eines Halbverbands: • x ^ x = x • x ^ y = y ^ x • x ^ (y ^ z) = (x ^ y) ^ z x, y, z V • Halbverbände haben oberstes Element T mit • T ^ x = x x V • Für: T = • Für : T = Basismenge der Potenzmenge
Algorithmus zum Lösen von Datenflussproblemen • Zusätzlich noch Startmenge v (meist ) OUT[Eingang]=vEingang; for(jeden Block B außer Eingang) OUT[B] = T; while(min. ein OUT wird geändert) for(jeden Block B außer Eingang) { IN[B]=^P Vorgänger von pOUT[P]; OUT[B]=fB(IN[B]); } • Für D = Rückwärts • IN und OUT vertauschen • Ausgang statt Eingang
Agenda • Einführung • Grundlagen • Datenflussanalyse • Methoden zur Code-Optimierung • Zusammenfassung
Eliminierung gemeinsamer Teilausdrücke • Gemeinsamer Teilausdruck: Vorkommen von Ausdruck E der schon einmal berechnet wurde und sich nicht mehr geändert hat. • Ziel: wiederholte Berechnungen des gleichen Ausdrucks vermeiden • Ermitteln mit Verfügbaren Ausdrücken • Eliminierung • Teilausdruck in Hilfsvariable speichern • Dann statt Teilausdruck Hilfsvariable verwenden
Eliminierung gemeinsamer Teilausdrücke ... B2 ... t1 = l+r m = t1/2 h1 = 8*m t2 = h1 t3 = a[t2] ifFalse k<t3 goto B4 B2 t1 = l+r m = t1/2 t2 = 8*m t3 = a[t2] ifFalse k<t3 goto B4 B3 B4 B3 B4 r = m-1 goto B5 r = m-1 goto B5 l = m+1 l = m+1 B5 B5 t4 = 8*m t5 = a[t4] ifFalse k==t5 goto B2 t4 = h1 t5 = a[t4] ifFalse k==t5 goto B2 ... ...
Weitergabe von Kopien • Durch andere Optimierungsverfahren entstehen Kopien der Form x = y • Ziel: y anstelle von x benutzen • Dadurch kann die Kopieranweisung überflüssig werden • Lösung mit Verfügbaren Definitionen: • Ersetzung ist in p möglich,wenn x und y noch gleichen Wert besitzen • Dazu müssen sowohl die Definition x = yals auch die Definition für y, diein x = y galt, gültig in p sein • Außerdem dürfen keine anderen Definitionen x und yp erreichen
Weitergabe von Kopien ... ... B2 B2 t1 = l+r m = t1/2 h1 = 8*m t2 = h1 t3 = a[t2] ifFalse k<t3 goto B4 t1 = l+r m = t1/2 h1 = 8*m t2 = h1 t3 = a[h1] ifFalse k<t3 goto B4 B3 B3 B4 B4 r = m-1 goto B5 r = m-1 goto B5 l = m+1 l = m+1 B5 B5 t4 = h1 t5 = a[t4] ifFalse k==t5 goto B2 t4 = h1 t5 = a[h1] ifFalse k==t5 goto B2 ... ...
Eliminierung von totem Code • Toter Code: • Anweisungen, die nie erreicht werden • Zuweisungen, die nie benutzt werden • Entsteht durch andere Optimierungen oder (selten) durch Programmierfehler • Identifizierung über aktive Variablen • Beispiel: x = true ... //Hier keine Zuweisungen an x if x goto L1 a = b + c//Toter Code L1: b = 2 * c
Optimierung von Schleifen • Programme verbringen einen Großteil ihrer Zeit in Schleifen • Optimierung von Schleifen daher besonders wichtig • 2 Ansätze zur Optimierung von Schleifen • Export schleifeninvarianter Variablen • Eliminierung von Induktionsvariablen
Optimierung von Schleifen Identifikation von Schleifen • Dominator: Knoten d dominiert Knoten n (d dom n), wenn jeder Pfad im Flussgraph von Eingang zu n über d führt • Rückwärtskante: Kante n → d mit d dom n • Natürliche Schleife einer Rückwärtskante n → d:Alle Knoten von denen n erreicht werden kann ohne durch d zu laufen
Optimierung von Schleifen Eingang B1 l = 0 r = N-1 B2 t1 = l+r m = t1/2 t2 = 8*m t3 = a[t2] ifFalse k<t3 goto B4 B3 r = m-1 goto B5 B4 l = m+1 B5 t4 = 8*m t5 = a[t4] ifFalse k==t5 goto B2 B6 if l<r goto B2 Ausgang
Optimierung von Schleifen Eingang B1 l = 0 r = N-1 B2 t1 = l+r m = t1/2 t2 = 8*m t3 = a[t2] ifFalse k<t3 goto B4 B3 r = m-1 goto B5 B4 l = m+1 B5 t4 = 8*m t5 = a[t4] ifFalse k==t5 goto B2 B6 if l<r goto B2 Ausgang
Optimierung von Schleifen Eingang B1 l = 0 r = N-1 B2 t1 = l+r m = t1/2 t2 = 8*m t3 = a[t2] ifFalse k<t3 goto B4 B3 r = m-1 goto B5 B4 l = m+1 B5 t4 = 8*m t5 = a[t4] ifFalse k==t5 goto B2 B6 if l<r goto B2 Ausgang
Optimierung von Schleifen Export schleifeninvarianter Variablen: • Variable ist schleifeninvariant, wenn in Schleife keine Zuweisung an sie erfolgt • Ein Ausdruck ist schleifeninvariant, wenn er nur aus schleifeninvarianten Variablen besteht • Diese sollen aus der Schleife herausgezogen werden while (i < max) do x := 3*y//3*y ist schleifeninvariant z := x+j j := i*2 i := i+1 end
Optimierung von Schleifen Eliminierung von Induktionsvariablen • Induktionsvariable, die sich bei jedem Schleifendurchlauf um den gleichen, konstanten Wert ändert • Diese sollen (bis auf eine) eliminiert werden x := 3*y x := 3*y while (i < max) doj := 2*i z := x+j while (j < 2*max) do j := i*2 z := x+j i := i+1j := j+2 endend i := max
Konstantenfaltung • Konstanten verbessern die Lesbarkeit von Programmen • Auf Ebenen des Zwischencodes nicht nötig • Daher: Variablen, die an einer Stelle immer den gleichen Wert haben durch diesen ersetzen • Ausdrücke, die nur aus Konstanten bestehen, können dann ebenfalls berechnet werden • Ermittlung von Konstanten auch mit Datenflussanalyse möglich
Konstantenfaltung • Beispiel: N = 10 //Konstante ... x = 2 //Wert in nächster Anweisung immer gleich y = N*x //N und x können ersetzt werden N = 10 ... x = 2 y = 10*2 //Ausdruck kann jetzt berechnet werden
Algebraische Umformungen • Ersetzen von langsameren Befehlen durch schnellere • Ausnutzen von mathematischen Gesetzen • Beispiele • x*2n→ x shiftl n • x + 0 oder x * 1 → x • x2→ x*x • 2*x → x+x • x/4 → x * 0,25
Agenda • Einführung • Grundlagen • Datenflussanalyse • Methoden zur Code-Optimierung • Zusammenfassung
Zusammenfassung • Direkt übersetzter Code kann noch weiter optimiert werden • Es gibt verschiedene Ansätze zur Optimierung • Datenflussanalyse bildet eine wichtige Grundlage für viele dieser Ansätze • Es gibt weitere Optimierungsmethoden, z.B. • für Maschinen-Code • für OO-Sprachen
Fragen Vielen Dank für die Aufmerksamkeit Fragen?