vorlesung, wintersemester 2009/10m. schölzel 1 optimierungstechniken in modernen compilern...
Post on 05-Apr-2015
115 Views
Preview:
TRANSCRIPT
1
Vorlesung, Wintersemester 2009/10 M. Schölzel
Optimierungstechniken in modernen Compilern
Einführung
2Optimierungstechniken in modernen Compilern Einführung
Klassifizierung von Computersystemen
Klassifizierung nach Flynn:
Single Instruction Single Data (SISD): Ein einzelner Prozessor führt einen Befehlsstrom auf Daten in einem
Datenspeicher aus.
Multiple Instruction Single Data (MISD): Eine Sequenz von Daten aus
demselben Speicher wird von mehreren Ausführungseinheiten bearbeitet, von denen jede einen
eigenen Steuerfluss besitzt.
Single Instruction Multiple Data (SIMD): Ein einzelner Befehl steuert mehrere Ausführungseinheiten, von denen jede Zugriff auf einen lokalen
Speicher hat.
Multiple Instruction Multiple Data (MIMD): Mehrere Steuerflüsse
steuern mehrere Ausführungseinheiten, von denen jede
Zugriff auf einen lokalen Speicher hat.
Es
exis
tier
t ei
n
Ste
uer
flu
ss i
m
Pro
gra
mm
.
Es
exis
tier
en
meh
rere
S
teu
erfl
üss
e im
P
rog
ram
m.
Alle Ausführungseinheiten greifen auf denselben
Datenspeicher zu.
Jede Ausführungseinheiten besitzt einen lokalen
Datenspeicher (z.B. einen Cache).
3Optimierungstechniken in modernen Compilern Einführung
Einordnung von Prozessorarchitekturen
Prozessororganisation
SISD SIMD MISD MIMD
Shared Memory
Distributed Memory
ClusterSymmetric Multiprocessor
(SMP)
Non-UniformMemory Access
(NUMA)
Vector Processor
Array Processor
Uniprocessor
Scalar Superscalar
Dynamic Scheduled
Static Scheduled
Static AllocationDynamic Allocation
i386, i486
Pentium
VLIW: Transmeta Crusoe, Philips
TriMedia
GeclusterteVLIWs
Dual-Core Pentium
Itanium
TMS320C62x
4Optimierungstechniken in modernen Compilern Einführung
Skalarer Prozessor ohne Pipeline
Typische Optimierungen des Compilers: Registerdruck minimieren Geeigneten Zielcode auswählen
Ste
ue
rwe
rk
Registerbank
ALU
Speicher
ldm (r8) r0
ldm (r9) r1
ldm (r10) r2
add r0,r1 r0
add r0,r2 r0
…
use r8,r9,r10
ldm (r8) r0
ldm (r9) r1
add r0,r1 r0
ldm (r10) r1
add r0,r1 r0
…
use r8,r9,r10
ldc #8 r0
add r0,r8 r0
ldm (r0) r1
ldm (r8+8) r1
Speicher
Speicher
Schematischer Aufbau:Hoher Registerdruck: Geringer Registerdruck:
Schlechte Codeauswahl: Bessere Codeauswahl:
5Optimierungstechniken in modernen Compilern Einführung
Skalarer Prozessor mit Befehlspipeline
Typische Optimierungen des Compilers: Registerdruck minimieren Geeigneten Zielcode auswählen Pipeline-Hazards vermeiden
Registerbank
ALU
Speicher
FE/DE
Speicher
DE/EX
EX/MEM
Ste
ue
rwe
rk
MMU
MEM/WB
ldm (r8) r0
ldm (r9) r1
stall
stall
add r0,r1 r0
ldm (r10) r2
stall
stall
add r0,r2 r0
ldm (r8) r0
ldm (r9) r1
ldm (r10) r2
stall
add r0,r1 r0
stall
stall
add r0,r1 r0
Schematischer Aufbau:Schlechte Befehlsanordnung:
Bessere Befehlsanordnung:
6Optimierungstechniken in modernen Compilern Einführung
Superskalarer Prozessor
Typische Optimierungen des Compilers: Registerdruck minimieren Geeigneten Zielcode auswählen Umordnung der Operationen, um Abhängigkeiten zwischen Operationen im Befehlspuffer
zu minimieren.
Registerbank
ALU Speicher
DE1/DE2
DE2/EX
EX/MEM
Ste
ue
rwe
rk
MMUALU
FE/DE1
Speicher
Befehlspuffer
add r0,r1 r2
add r0,r2 r2
add r0,r3 r4
add r0,r4 r4
add r0,r1 r2
add r0,r3 r4
add r0,r2 r2
add r0,r4 r4
Schlechte Befehls-anordnung für Puffer mit Kapazität 2:
Schematischer Aufbau:
Bessere Befehls-anordnung für Puffer mit Kapazität 2:
7Optimierungstechniken in modernen Compilern Einführung
VLIW
Typische Optimierungen des Compilers: Registerdruck minimieren Geeigneten Zielcode auswählen Pipeline-Hazards vermeiden Feingranulare Parallelität erkennen und Operationen statisch
parallelisieren.
Registerbank
ALU Speicher
FE/DE
DE/EX
EX/MEM
Ste
ue
rwe
rk
MMUALU
Speicheradd r0,r1 r2
add r0,r2 r2
add r0,r3 r4
add r0,r4 r4
add r0,r1 r2 | add r0,r3 r4
add r0,r2 r2 | add r0,r4 r4
Schematischer Aufbau: Sequentieller Programmcode:
Parallelisierter Programmcode:
8Optimierungstechniken in modernen Compilern Einführung
SMP
Typische Optimierungen: Grobgranulare Parallelität erkennen und das Programm in Threads aufteilen,
so dass wenig Synchronisation zwischen den Threads erforderlich ist. Registerdruck minimieren Geeigneten Zielcode auswählen Pipeline-Hazards vermeiden
call f
call gcall f
Registerbank
ALU
FE/DESpeicher
DE/EX
EX/MEM
Steu
erw
erk
MMU
MEM/WB
Registerbank
ALU
FE/DESpeicher
DE/EX
EX/MEM
Steu
erw
erk
MMU
MEM/WB
Bus
ldc #100 r0
loopHead:
…
dec r0
cmp r0,#0
jnz loopHead
ldc 100 r0
loopHead:
…
dec r0
cmp r0,#50
jg loopHead
ldc 50 r0
loopHead:
…
dec r0
cmp r0,#0
jnz r0 loopHead
Cache Cache
gemeinsamer Speicher
Schematischer Aufbau: Sequentieller Code: Code Proc1: Code Proc2:
call g
Sequentieller Code: Code Proc1: Code Proc2:
9Optimierungstechniken in modernen Compilern Einführung
Warum soll der Compiler optimieren?
Optimierungen auf Quelltextebene (z.B. durch den Programmierer) sind möglich, machen es aber erforderlich, dass der Quelltext für jede Zielarchitektur optimiert wird.
Verschiedene Optimierungen, die auf Zielcodeebene erforderlich sind, lassen sich in einer Hochsprache nicht formulieren (z.B. Registerplanung):
Statisch geplante Architekturen:• Compiler führt Scheduling und Allokation durch – auf Quelltextebene in der
Regel nicht ausdrückbar Dynamisch geplante bzw. skalare Architekturen
• Compiler kann Hazards vermeiden helfen und Pipeline besser füllen• Nur ein kleines Fenster für die Optimierung in Hardware; Compiler kann
optimierbaren Code in dieses Fenster schieben SMP
• Compiler verteilt Programmcode und macht Parallelisierung möglich Programmiersprachen besitzen sequentielle Semantik; Geeignete
Form der Parallelität muss durch den Compiler erkannt werden: feingranular, grobgranular.
10Optimierungstechniken in modernen Compilern Einführung
Demonstration dieser Problematik an einem Beispiel: Matrixmultiplikation
Optimal für eine skalare Architektur. Ergebnis der Multiplikation wird im Register für t akkumuliert.
for i = 1 to 100 step 1 do for j = 1 to 100 step 1 do t = 0.0; for k = 1 to 100 step 1 do t = t + a[j][k] * b[k][i] od c[j][i] = t; odod
i = 1iLoop: j = 1jLoop: t = 0.0; k = 1kLoop: t = t + a[j][k] * b[k][i] k = k + 1 if k <= 100 then goto kLoop c[j][i] = t; j = j + 1 if j <= 100 then goto jLoop i = i + 1; if i <= 100 then goto iLoop
11Optimierungstechniken in modernen Compilern Einführung
Was kann parallel ausgeführt werden?
for i = 1 to 100 step 1 do for j = 1 to 100 step 1 do t = 0.0; for k = 1 to 100 step 1 do t = t + a[j][k] * b[k][i] od c[j][i] = t; odod
Basisblock enthält keine nennenswerte Parallelität
Iterationen der k-Schleife können nicht parallel ausgeführt werden: Iteration k+1 benötigt Wert von t aus Iteration k.
Iterationen der j-Schleife können nicht parallel ausgeführt werden: Benutzung derselben Variablen t.
Iterationen der i-Schleife können nicht parallel ausgeführt werden: Benutzung derselben Variablen t.
b
a cj
k
k
i
t
12Optimierungstechniken in modernen Compilern Einführung
t
Scalar Expansion
Basisblock enthält keine nennenswerte Parallelität
Iterationen der k-Schleife können nicht parallel ausgeführt werden: Iteration k+1 benötigt Wert t[j] aus Iteration k.
Iterationen der j-Schleife können parallel ausgeführt werden, weil a und b nur gelesen werden und Akkumulation in unterschiedliche Elemente von t erfolgt.
Iterationen der i-Schleife können nicht parallel ausgeführt werden: Benutzung derselben Variablen t.
b
a cj
k
k
i
for i = 1 to 100 step 1 do for j = 1 to 100 step 1 do t[j] = 0.0; for k = 1 to 100 step 1 do t[j] = t[j]+a[j][k]*b[k][i] od c[j][i] = t[j]; odod
t[j]
for i = 1 to 100 step 1 do for j = 1 to 100 step 1 do t = 0.0; for k = 1 to 100 step 1 do t = t + a[j][k] * b[k][i] od c[j][i] = t; odod
13Optimierungstechniken in modernen Compilern Einführung
Loop-Distribution
for i = 1 to 100 step 1 do for j = 1 to 100 step 1 do t[j] = 0.0; od for j = 1 to 100 step 1 do for k = 1 to 100 step 1 do t[j] = t[j]+a[j][k]*b[k][i] od od for j = 1 to 100 step 1 do c[j][i] = t[j]; odod
t
b
a cj
k
k
i
for i = 1 to 100 step 1 do for j = 1 to 100 step 1 do t[j] = 0.0; for k = 1 to 100 step 1 do t[j] = t[j]+a[j][k]*b[k][i] od c[j][i] = t[j]; odod
Zuerst Vektor t initialisieren.
Werte in t berechnen.
Werte aus t nach c zurück schreiben.
Initialisieren, Berechnen und Zurückschreiben wurde separiert; kann aber nicht parallel ausgeführt werden.
Initialisieren, Berechnen und Zurückschreiben geschieht Elementweise in t.
14Optimierungstechniken in modernen Compilern Einführung
Loop-Interchange
for i = 1 to 100 step 1 do for j = 1 to 100 step 1 do t[j] = 0.0; od for k = 1 to 100 step 1 do for j = 1 to 100 step 1 do t[j] = t[j]+a[j][k]*b[k][i] od od for j = 1 to 100 step 1 do c[j][i] = t[j]; odod
t
b
a cj
k
k
i
Iterationen der inneren Schleife können parallel ausgeführt werden.
for i = 1 to 100 step 1 do for j = 1 to 100 step 1 do t[j] = 0.0; od for j = 1 to 100 step 1 do for k = 1 to 100 step 1 do t[j] = t[j]+a[j][k]*b[k][i] od od for j = 1 to 100 step 1 do c[j][i] = t[j]; odod
Keine nennenswerte Parallelität in der inneren Schleife, da jede Iteration den Wert t[j] aus der vorigen Iteration benötigt.
15Optimierungstechniken in modernen Compilern Einführung
Möglichkeit der Vektorisierung (idealisiert)
for i = 1 to 100 step 1 do t[1..100] = 0.0; for k = 1 to 100 step 1 do t[1..100] = t[1..100]+a[1..100][k]*b[k][i] od c[1..100][i] = t[1..100];od
for i = 1 to 100 step 1 do for j = 1 to 100 step 1 do t[j] = 0.0; od for k = 1 to 100 step 1 do for j = 1 to 100 step 1 do t[j] = t[j]+a[j][k]*b[k][i] od od for j = 1 to 100 step 1 do c[j][i] = t[j]; odod
t
b
a cj
k
k
i
Vektoroperation erfordert die parallele Ausführbarkeit der Operationen auf den Elemente des Vektors. Damit sind diese Operationen parallel auf einem superskalaren nicht-Vektor Prozessor ausführbar.
16Optimierungstechniken in modernen Compilern Einführung
Möglichkeit der Vektorisierung (praktisch)
for i = 1 to 100 step 1 do t[1..100] = 0.0; for k = 1 to 100 step 1 do t[1..100] = t[1..100]+a[1..100][k]*b[k][i] od c[1..100][i] = t[1..100];od
for i = 1 to 100 step 1 do for j = 1 to 100 step 32 do t[j..j+31] = 0.0; od for k = 1 to 100 step 1 do for j = 1 to 100 step 32 do t[j..j+31] = t[j..j+31]+a[j..j+31][k]*b[k][i] od od for j = 1 to 100 step 32 do c[j..j+31][i] = t[j..j+31]; odod
t
b
a cj
k
k
i
17Optimierungstechniken in modernen Compilern Einführung
Ausführung auf VLIW-Prozessor mit N Ausführungseinheiten
for i = 1 to 100 step 1 do for j = 1 to 100 step N do t[j] = 0.0; ... ; t[j+N-1] = 0.0; od for k = 1 to 100 step 1 do for j = 1 to 100 step N do t[j] = t[j]+a[j][k]*b[k][i]; ... t[j+N-1] = t[j+N-1]+a[j+N-1][k]*b[k][i]; od od for j = 1 to 100 step N do c[j][i] = t[j]; ... c[j+N-1][i] = t[j+N-1]; odod
N Multiplikationen und Additionen können parallel ausgeführt werden.
for i = 1 to 100 step 1 do for j = 1 to 100 step 32 do t[j..j+31] = 0.0; od for k = 1 to 100 step 1 do for j = 1 to 100 step 32 do t[j..j+31] = t[j..j+31]+a[j..j+31][k]*b[k][i] od od for j = 1 to 100 step 32 do c[j..j+31][i] = t[j..j+31]; odod
18Optimierungstechniken in modernen Compilern Einführung
t1
Matrixmultiplikation für SMP mit zwei Prozessoren
for i = 1 to 100 step 1 do for j = 1 to 100 step 1 do t = 0.0; for k = 1 to 100 step 1 do t = t + a[j][k] * b[k][i] od c[j][i] = t; odod
for i = 1 to 50 step 1 do for j = 1 to 100 step 1 do t0 = 0.0; for k = 1 to 100 step 1 do t0 = t0 + a[j][k] * b[k][i] od c[j][i] = t0; odod
for i = 51 to 100 step 1 do for j = 1 to 100 step 1 do t1 = 0.0; for k = 1 to 100 step 1 do t1 = t1 + a[j][k] * b[k][i] od c[j][i] = t1; odod
t0
b
aj
k
k
i
19Optimierungstechniken in modernen Compilern Einführung
Grob- vs. feingranulare Parallelität
for i = 1 to 100 step 1 do for j = 1 to 100 step 1 do t[j] = 0.0; od for j = 1 to 100 step 1 do for k = 1 to 100 step 1 do t[j] = t[j]+a[j][k]*b[k][i] od od for j = 1 to 100 step 1 do c[j][i] = t[j]; odod t
b
a cj
k
k
iMatrixmultiplikation vor Loop-Interchange (keine feingranulare Parallelität in der inneren Schleife):
for j = 1 to 50 step 1 do for k = 1 to 100 step 1 do t[j] = t[j]+a[j][k]*b[k][i] odod
for j = 51 to 100 step 1 do for k = 1 to 100 step 1 do t[j] = t[j]+a[j][k]*b[k][i] odod
Aber äußere Schleife kann auf zwei verschiedenen Prozessoren verteilt werden.
t[j]
20Optimierungstechniken in modernen Compilern Einführung
Grob- vs. feingranulare Parallelität
for i = 1 to 100 step 1 do for j = 1 to 100 step 1 do t[j] = 0.0; od for k = 1 to 100 step 1 do for j = 1 to 100 step 1 do t[j] = t[j]+a[j][k]*b[k][i] od od for j = 1 to 100 step 1 do c[j][i] = t[j]; odod t
b
a cj
k
k
i
Aufteilen der äußeren Schleife auf verschiedene Prozessoren führt zu gleichzeitigem Schreiben derselben Elemente in t. Ist also nicht möglich.
Matrixmultiplikation nach Loop-Interchange (viel feingranulare Parallelität in der inneren Schleife):
21Optimierungstechniken in modernen Compilern Einführung
Aufgeworfene Fragen
Unter welchen Umständen ist eine bestimmte Transformation zulässig?
Welche Transformationen erzeugen fein granulare Parallelität, grob granulare Parallelität?
Wie kann die erzeugte fein granulare Parallelität in superskalaren Prozessorarchitekturen genutzt werden?
Wie kann die erzeugte grob granulare Parallelität in SMP Architekturen genutzt werden?
22Optimierungstechniken in modernen Compilern Einführung
Aufbau der Vorlesung
Einleitung Grundlagen
Aufbau eines Compilers Überblick über die Analysephase Vorgehen bei einfacher Synthesephase Zwischencodeformate Datenflussanalyseschema Modellierung von Datenabhängigkeiten Abhängigkeitsanalyse
23Optimierungstechniken in modernen Compilern Einführung
Aufbau der Vorlesung
Optimierungstechniken für DSPs und Mikrocontroller Globale Registerallokation Scheduling-Techniken Code-Selektion
Optimierunbgstechniken für superskalare Prozessoren Erzeugung fein granularer Parallelität Trade-Off Registerallokation/ILP Statische/Dynamische Parallelisierung HW-Support für bessere statische Parallelisierung Region-Based-Scheduling Global Code Motion Traces, Superblöcke, Hyperblöcke Modulo Scheduling IF-Conversion
24Optimierungstechniken in modernen Compilern Einführung
Aufbau der Vorlesung
Optimierungstechniken für SMPs Erzeugung grob granularer Parallelität Parallelisierung ohne Synchronisierung Parallelisierung mit Synchronisierung (OpenMP)
top related