scieee Science in your language
[en] (orig)
Automatische Verteilung mehrsträngiger
Java-Programme
Dissertation
Schriftliche Arbeit zur Erlangung des akademischen Grades
„Doktor der Naturwissenschaften“
an der Fakultät für Elektrotechnik, Informatik und Mathematik
der Universität Paderborn
vorgelegt von
Dinh Khoi Le
Paderborn, April 2006
Datum der mündlichen Prüfung:
08. Juni 2006
Gutachter:
Prof. Dr. Uwe Kastens, Universität Paderborn
Prof. Dr. Odej Kao, Universität Paderborn
II
Inhaltsverzeichnis
1. Einleitung 1
2. Java und sein Modell zur verteilten Ausführung 7
2.1. Multithreading-Konzept und die parallele Programmierung . . 9
2.1.1. Prozesse und ihre Interaktion . . . . . . . . . . . . . . . . 9
2.1.2. Java-Threads und das Multithreading-Konzept . . . . . . 12
2.1.3. Unterstützung der parallelen Programmierung . . . . . 15
2.2. Kommunikation mittels Java Remote Method Invocation . . . . . . 18
2.2.1. Konzept von Java-RMI . . . . . . . . . . . . . . . . . . . . 18
2.2.2. Objekt-Serialisierung . . . . . . . . . . . . . . . . . . . . . 20
2.2.3. Semantische Unterscheidung zwischen verteiltem und
lokalem Objektmodell . . . . . . . . . . . . . . . . . . . . . 21
2.3. Multithreaded Java-Programme und ihre Ausführungsmodelle 23
2.3.1. Verzahntes und verteiltes Ausführungsmodell . . . . . . 24
2.3.2. Ausführungskontext im verteilten Modell . . . . . . . . 26
2.3.3. Verteilte Ausführungsstränge und einige relevante Ei-
genschaften . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3. Grundüberlegungen zur automatischen Verteilung 31
3.1. Manuelle Verteilung eines mehrsträngigen Java-Programms . . 33
3.1.1. Hand-Verteilung . . . . . . . . . . . . . . . . . . . . . . . . 33
3.1.2. Technische Umsetzung . . . . . . . . . . . . . . . . . . . . 35
3.2. Konzept zur automatischen Verteilung . . . . . . . . . . . . . . . 37
3.2.1. Das Szenario . . . . . . . . . . . . . . . . . . . . . . . . . . 37
3.2.2. Verteilungsentscheidungen . . . . . . . . . . . . . . . . . . 39
3.2.3. Automatische Transformation . . . . . . . . . . . . . . . . 42
3.2.4. Programmanalyse . . . . . . . . . . . . . . . . . . . . . . . 43
i
Inhaltsverzeichnis
4. Verwandte Arbeiten 47
4.1. Existierende Systeme, Frameworks und Bibliotheken . . . . . . 48
4.1.1. Systeme mit Sprachänderung . . . . . . . . . . . . . . . . 50
4.1.2. Systeme und Bibliotheken mit Semantik-Änderung . . . 52
4.1.3. Systeme mit statisch basierter Verteilungsentscheidung 55
4.1.4. Systeme mit dynamisch basiertem Ansatz . . . . . . . . 56
4.1.5. Andere Systeme . . . . . . . . . . . . . . . . . . . . . . . . 58
4.1.6. Systeme für High-Performance Java - Datenparallel . . 59
4.2. Kommunikationsmodelle und -techniken . . . . . . . . . . . . . 61
4.2.1. Sun-RMI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
4.2.2. KaRMI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
4.2.3. Distributed Threads . . . . . . . . . . . . . . . . . . . . . . 62
4.2.4. JavaSpaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
4.2.5. Andere Techniken . . . . . . . . . . . . . . . . . . . . . . . 63
4.3. Vergleiche - Überblicke . . . . . . . . . . . . . . . . . . . . . . . . . 64
5. Verteilungsstrategien und Verteilungsplan: Konzept und Model-
lierung 67
5.1. Modell zur automatischen Verteilung mittels Programmanalyse 69
5.1.1. Programmanalyse zur Berechnung der Programmei-
genschaften . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
5.1.2. Anforderungen vor der Laufzeit . . . . . . . . . . . . . . 75
5.1.3. Aufgaben zur Laufzeit . . . . . . . . . . . . . . . . . . . . 76
5.2. Verteilungsplan: die Komponenten, deren Modellierung und
verwendete Konzepte . . . . . . . . . . . . . . . . . . . . . . . . . 77
5.2.1. Modellierung von Verteilungsvorschriften als ODFs . . 79
5.2.2. Modellierung der Verteilungsstrategien . . . . . . . . . . 87
5.2.3. Modell eines Verteilungsplanes . . . . . . . . . . . . . . . 90
5.2.4. Andere Komponenten . . . . . . . . . . . . . . . . . . . . . 94
5.3. Grundlegende Objektverteilungsfunktionen . . . . . . . . . . . . 95
5.3.1. Statische Verteilungsfunktionen . . . . . . . . . . . . . . . 96
5.3.2. Basis-Verteilungsfunktionen . . . . . . . . . . . . . . . . . 99
5.3.3. Dynamische Verteilungsfunktionen . . . . . . . . . . . . 104
5.4. Spezielle Verteilungsstrategien . . . . . . . . . . . . . . . . . . . . 106
5.4.1. Statische Verteilungsstrategien . . . . . . . . . . . . . . . 106
5.4.2. Basis-Verteilungsstrategien . . . . . . . . . . . . . . . . . . 107
5.4.3. Strategien mit dynamischen Entscheidungen . . . . . . . 109
5.5. Konkrete Verteilungspläne . . . . . . . . . . . . . . . . . . . . . . 111
ii
Inhaltsverzeichnis
6. Analysetechniken zur automatischen Verteilung 115
6.1. Charakteristiken der statischen Programm- und Laufzeiteigen-
schaften.................................. 117
6.1.1. Statische Programmeigenschaften . . . . . . . . . . . . . 118
6.1.2. Architektur- und Laufzeiteigenschaften . . . . . . . . . . 121
6.2. Dynamische Programmeigenschaften und das Kostenmodell
zur Objektverteilung . . . . . . . . . . . . . . . . . . . . . . . . . . 123
6.2.1. Modellierung dynamischer Programmeigenschaften . . 123
6.2.2. Konzept zur hybriden Analyse . . . . . . . . . . . . . . . 130
6.3. Grundlegende statische Analysen . . . . . . . . . . . . . . . . . . 134
6.3.1. Aufrufgraph-Analyse zur Auflösung dynamischer Me-
thodenbindung . . . . . . . . . . . . . . . . . . . . . . . . . 135
6.3.2. Interprozedurale Def-Use-Analyse (GlobalDefUse-
Analyse)............................. 137
6.3.3. Analysetechniken zur Berechnung der Programmeigen-
schaften ............................. 141
6.4. Analyseverfahren zur stagewise-immutable Eigenschaft . . . . . . 145
6.4.1. Die stagewise-immutable Eigenschaft und ihre Anwen-
dung zur Objektreplikation . . . . . . . . . . . . . . . . . 146
6.4.2. Analysetechniken zur Berechnung der stagewise-
immutable Eigenschaft . . . . . . . . . . . . . . . . . . . . . 151
6.4.3. Analyse zur pure-immutable Eigenschaft . . . . . . . . . . 160
6.5. Verwandte Arbeiten . . . . . . . . . . . . . . . . . . . . . . . . . . 161
6.5.1. Immutable-Analysen . . . . . . . . . . . . . . . . . . . . . 161
6.5.2. Analysetechniken zur Objektverteilung . . . . . . . . . . 163
7. Infrastruktur und Realisierung des Systems JScatter 167
7.1. Die PAULI - Analyseumgebung . . . . . . . . . . . . . . . . . . . 169
7.1.1. Struktur der Analyseumgebung . . . . . . . . . . . . . . . 169
7.1.2. Vorteile durch den Einsatz der Analyseumgebung . . . 171
7.2. Programmtransformation . . . . . . . . . . . . . . . . . . . . . . . 172
7.2.1. Transformationskonzept . . . . . . . . . . . . . . . . . . . 172
7.2.2. Transformation . . . . . . . . . . . . . . . . . . . . . . . . . 175
7.3. Berechnung der Programmeigenschaften . . . . . . . . . . . . . . 176
7.3.1. Verteilungsberechnung der Verteilungspläne . . . . . . . 178
7.3.2. Programmanalyse . . . . . . . . . . . . . . . . . . . . . . . 179
7.4. Verteilte Laufzeitumgebung . . . . . . . . . . . . . . . . . . . . . . 180
7.4.1. Zusammenarbeit der Komponenten . . . . . . . . . . . . 180
7.4.2. Verwendung des Verteilungsplans . . . . . . . . . . . . . 181
iii
Inhaltsverzeichnis
7.5. Systemvoraussetzungen und Verbesserungen . . . . . . . . . . . 183
7.5.1. Systemanforderungen . . . . . . . . . . . . . . . . . . . . . 183
7.5.2. Verbesserungen . . . . . . . . . . . . . . . . . . . . . . . . . 184
8. Evaluation 185
8.1. Vorstellung der Anwendungen und Benchmarks - Vorberei-
tung zur Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
8.1.1. Ein Anwendungsgebiet: diskrete ereignisbasierte Simu-
lation............................... 187
8.1.2. Untersuchte Simulations- und Benchmark-Programme 190
8.1.3. Eingesetzte Hardware und Software . . . . . . . . . . . . 192
8.2. Empirische Untersuchung der stagewise-immutable Eigenschaften 194
8.2.1. Analyseergebnisse der Anwendungen . . . . . . . . . . . 196
8.2.2. Analyseergebnisse der Anwendungen zusammen mit
ihren transitiven Hüllen . . . . . . . . . . . . . . . . . . . . 199
8.3. Empirische Untersuchungen zur Performanz der verteilten
Anwendungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
8.3.1. Grocery-Simulation . . . . . . . . . . . . . . . . . . . . . . 203
8.3.2. Multithreaded Raytracer (mtrt) aus dem SPECjvm98 . . 212
8.3.3. Das Traveling Salesperson-Problem (TSP) . . . . . . . . . . 226
8.4. Die Erkenntnisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
9. Zusammenfassung 231
9.1. Wichtige Aspekte in Kürze . . . . . . . . . . . . . . . . . . . . . . 232
9.2. Ausblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
A. Verzeichnisse 241
Literaturverzeichnis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
Abbildungsverzeichnis . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
Tabellenverzeichnis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
iv
1. Einleitung
Motivation In der Welt des High-Performance Computing and Communications
findet Java immer mehr Anwendung. Die High-Performance Anwendungen
aus HP-Fortran, C, C++ mit message passing-Technik werden nach und nach in
Java übertragen. Neben den typischen Anwendungen aus den Bereichen der
geophysikalischen Verfahren und der Finanzmathematik, numerischen Be-
rechnungen etc. finden sich realistische irreguläre Java-Anwendungen im Be-
reich der Modellierung und Simulation, vor allem bei parallelen und verteil-
ten diskreten ereignisbasierten Simulationen. In diesen vielfältigen Anwen-
dungsgebieten wird Java häufiger als die Sprache für parallele Berechnun-
gen eingesetzt, die sich durch die Verwendung mehrerer leichtgewichtiger
Prozesse vom Benutzer ausdrücken lassen. Die verschiedenen Prozesse wer-
den vom Betriebssystem für den Benutzer völlig transparent miteinander ver-
zahnt. Auf speziellen Rechnern mit mehreren Prozessoren kann sogar durch
eine echte parallele Ausführung einzelner Prozesse Geschwindigkeitsgewinn
erzielt werden. Genau diese Idee, dass sich der Programmierer nur auf die
Formulierung seines parallelen Lösungsansatzes konzentriert und von den
technischen Details der tatsächlichen parallelen Ausführung seiner Anwen-
dung entlastet wird, soll auf das JScatter-System übertragen werden.
Um ein sequentielles multithreaded Java-Programm in eine verteilte Version
mit Remote Method Invocation (RMI) zur Kommunikation umzusetzen, muss
ein Programmierer viele aufwändige technische Schritte durchführen. Diese
können grob in zwei Phasen aufgeteilt werden: das Umprogrammieren des
ursprünglichen Programms in eine verteilte Version mit eincodierten Vertei-
lungshinweisen unter Verwendung von RMI und das verteilte Ausführen auf
mehreren Rechnern. Abgesehen davon, dass der Anwender sehr gute Kennt-
nisse über parallele Programmierung in Java mit der RMI-Technik besitzen
muss, sind diese Schritte sehr fehleranfällig und kosten sehr viel Aufwand.
Zur verteilten Ausführung auf mehreren Rechnern sind außerdem noch eini-
ge nicht triviale Handgriffe erforderlich. Im Fehlerfall ist es auch sehr müh-
sam, nach Fehlerursachen zu suchen.
1
KAPITEL 1. EINLEITUNG
Mit dem im JScatter-System verwendeten Konzept kann eine mehrsträngig
formulierte Java-Anwendung benutzertransparent automatisch verteilt wer-
den. All diese oben genannten notwendigen Schritte werden vollständig auto-
matisiert: Als Ausgangspunkt liegt ein mehrsträngiges Programm vollständig
in Form von Java-Bytecode vor. Die Programmanalyse wird eingesetzt, um re-
levante Eigenschaften des Eingabeprogramms für die automatische Transfor-
mation und die automatische Verteilung zu bestimmen. Beruhend auf dieser
Information wird anschließend eine verteilte Version des Programms durch
den Transformator des Systems automatisch generiert, die dann auf der ver-
teilten Laufzeitumgebung ausgeführt werden kann. Zur Ausführung des Pro-
gramms hat der Anwender nur noch die Aufgabe, die Rechnerknoten zu be-
stimmen, auf denen das Programm verteilt laufen soll. Somit wird die Trans-
parenz zwischen der nebenläufigen Formulierung eines Programms und des-
sen verteilter Ausführung völlig erreicht.
Bei der parallelen Ausführung in einer verteilten Laufzeitumgebung verur-
sachen die Zugriffe auf Programmobjekte über Rechnergrenzen hinweg er-
heblich größeren Aufwand als lokale Zugriffe. Dadurch kann eine parallele
Ausführung im ungünstigen Fall sogar viel langsamer als die verzahnte auf
einem einzigen Rechner werden. Daher sollen geschickte Strategien zur Ver-
teilung der Objekte so angewandt werden, dass die Parallelität beim verteil-
ten Rechnen maximal wird, die dabei entstehenden Kommunikationskosten
aber so gering wie möglich gehalten werden. Diese Aspekte bilden die Kern-
punkte meiner Arbeit.
Beiträge Mit dem Ziel, mehrsträngig formulierte Java-Anwendungen voll-
ständig automatisch zu verteilen und dabei gute Performanz zu erreichen,
wird das Konzept Verteilungsplan eingesetzt. Über einen Plan können alle stra-
tegischen Entscheidungen über Transformation und Verteilung der Objekte
getroffen werden. Es wird also entschieden, ob ein zu erzeugendes Objekt ein
remote oder ein lokales Objekt ist und, im Fall eines remote Objekts, auf welcher
JVM es erzeugt werden soll. Mit automatischen, tiefgehenden Programmana-
lysen können Programmeigenschaften bestimmt werden, die relevant für die
Verteilung sind. Für bestimmte Verteilungsstrategien werden bestimmte Pro-
grammeigenschaften benötigt, die vor der Laufzeit und zur Laufzeit von ent-
sprechenden statischen und dynamischen Analyseverfahren beschafft werden.
Diese Eigenschaften werden innerhalb spezieller Objektverteilungsfunkti-
onen (ODFs) zur Ermittlung der Platzierungsentscheidung verwendet. Be-
2
stimmte ODFs bilden zusammen eine Verteilungsstrategie und aus mehre-
ren passenden Strategien entsteht ein Verteilungsplan. Die Strategien werden
vor der Laufzeit soweit wie möglich berechnet und direkt in das Programm
in Form von Anfragen an den Verteilungsplan eincodiert. Zur Laufzeit wird
unmittelbar vor einer Objekterzeugung eine Entscheidung getroffen und das
Objekt wird nach diesem Hinweis entsprechend platziert. Mit diesem Modell
können die Programmeigenschaften, getrennt von ihrer Verwendung in den
ODFs, mittels geeigneter Analysetechniken berechnet werden. Die Güte der
Analyseergebnisse entscheidet über die Verteilungsgüte des Programms.
Mit diesem Konzept werden grundlegende ODFs formuliert, spezielle
Verteilungsstrategien definiert und somit Pläne entwickelt. Die dabei ver-
wendeten statischen und dynamischen Programmeigenschaften werden all-
gemein aus mehrsträngigen Java-Anwendungen heraus abstrahiert und cha-
rakterisieren relevante Verteilungsinformation. Mit dem angegebenen Modell
hat man außerdem den Vorteil, neue ODFs formulieren zu können, verschie-
dene Varianten der Strategien anzuwenden oder neue zu entwickeln und so-
mit unterschiedliche Pläne zur Verteilung eines Programms einzusetzen.
Zur Lokalitätsoptimierung bei der Verteilung habe ich insbesondere das Au-
genmerk auf die Programmeigenschaft, die die Objektreplikation legitimiert,
und die genauere Berechnung der dynamischen Workload-Eigenschaft ge-
richtet. So wurde zur Replikation eine neuartige Analyse entwickelt, die die
so genannte stagewise-immutable Eigenschaft der Objekte vor der Laufzeit er-
kennt. Objekte mit dieser Eigenschaft können direkt nach ihrer Initialisie-
rung (pure-immutable) oder ab einem späteren Zeitpunkt (postponed-immutable)
zur Laufzeit repliziert werden. Nach der Replikation finden alle Lesezugrif-
fe dann lokal statt, so dass viel entfernter Kommunikationsaufwand gespart
werden kann.
Zur Verbesserung der vor der Laufzeit abgeschätzten Workload sowie
Kommunikationskosten wird der Ansatz zur hybriden Analyse erprobt. Die
mittels dynamischer Analyse gewonnenen Laufzeitdaten werden mit den sta-
tischen Analyseergebnissen kombiniert. Dadurch können einige Laufzeitwer-
te bestimmt werden, die zur Verteilungsberechnung zukünftig zu erzeugen-
der Objekte eingesetzt werden.
Ergebnisse aus den empirischen Untersuchungen zeigen, dass bei den zu-
sammen mit den verwendeten Bibliothek-Klassen untersuchten Programmen
und Benchmarks viele Objektrepräsentanten (etwa 44% aller Erzeugungsstel-
len) die stagewise-immutable Eigenschaft, vorwiegend die pure-immutable Ei-
genschaft, erkannt werden. Durch die Replikation der Objekte, die aus die-
3
KAPITEL 1. EINLEITUNG
sen als stagewise-immutable erkannten Erzeugungsstellen entstehen, können
sehr viele Lesezugriffe auf diesen Objekten nach der Replikation (bis zu 15524
oder 6,97% als statisch erkannte Zugriffe auf den stagewise-immutable Objekt-
repräsentanten) lokal ausgeführt werden. Bei der verteilten Ausführung auf 9
miteinander über Standard-Ethernet-100MBit verbundenen Standard-PCs ei-
ner diskreten ereignisbasierten Simulation kann ein Speedup von etwa 3.8 er-
reicht werden. Auf einem speziellen Cluster des Paderborn Center for Paral-
lel Computing (PC2) konnte sehr viel Performanz beim mtrt-Raytracer erzielt
werden: Mit den geschickten Verteilungsstrategien, mehr Rechenleistung und
schnellere Verbindung konnte dieses Programm bis auf einen Faktor 3 schnel-
ler verteilt ausgeführt werden, als die verteilte Ausführung auf einem norma-
len Pool-Cluster.
Vor- und Nachteile Im Allgemeinen kann die Performanz durch verteilte
Ausführung auf mehreren Rechnern nur gut für Anwendungen erzielt wer-
den, die insgesamt viele aufwändige Berechnungen durchführen, aber wenig
Kommunikation verursachen. Beispiele dafür sind diskrete ereignisbasierte
Simulationen und Programme mit regulären Strukturen, wobei innerhalb ei-
nes Threads wesentlich mehr Rechenaufwand als Kommunikation verursacht
wird. Dieser Ansatz kann außerdem auch nicht mit denjenigen konkurrieren,
bei denen ein Experte im Algorithmen-Bereich eine spezielle algorithmische
Lösung für das im Programm verwendete Paradigma programmiert und da-
nach die Objekte verteilt.
Der Vorteil meines Ansatzes ist aber, dass alles vollständig automatisch
durchgeführt wird. Durch die verteilte Ausführung in einem Cluster stehen
einem auch die verteilten Ressourcen wie Speicher automatisch zur Verfü-
gung. Ein weiterer Vorteil der automatischen Verteilung ist die Skalierbar-
keit, die bei Mehrprozessor-Rechnern sehr beschränkt ist. Die automatische
Transformation und die verteilte Ausführung eines mehrsträngigen Java-
Programms können außerdem einem Programmierer dabei helfen, die echte
Parallelität seines Programms durch die verteilte Ausführung auf mehreren
Rechnern besser zu verstehen 1, wenn er noch nie solche multithreaded Java-
Programme verteilt geschrieben hat.
Da das JScatter-System komplett in Java realisiert wurde, kann das System
plattformunabhängig verwendet werden, wo eine Standard-JDK vorhanden
ist. So wurde JScatter zur Evaluation sowohl auf einem Cluster von Standard-
1Paralleler Ablauf der Threads (mit Ausgaben) auf verschiedenen Computern
4
PCs als auch auf einem speziellen Cluster (ARMINIUS-Cluster) des Pader-
born Center for Parallel Computing (PC2) eingesetzt.
Struktur der Arbeit Kapitel 2 beschreibt die wichtigen Aspekte zur ver-
teilten Ausführung eines mehrsträngig formulierten Java-Programms. Diese
Aspekte dienen als Grundlage für die Schritte zur automatischen Verteilung.
Darauf aufbauend werden die Grundüberlegungen zum gesamten Konzept
dargestellt, die im Kapitel 3 beschrieben werden. Vergleiche meines Konzep-
tes sowie des JScatter-Systems mit anderen verwandten Ansätzen und vor-
handenen Systemen finden sich im Kapitel 4.
Die beiden anschließenden Kapitel beschreiben die Kernpunkte der Arbeit:
Im Kapitel 5 werden das Konzept und die Modellierung der ODFs, der
Verteilungsstrategien und somit Verteilungspläne repräsentiert. Die Model-
lierung der Programmeigenschaften, die zur automatischen Verteilung rele-
vant sind und die Analysetechniken, die diese Eigenschaften berechnen, wer-
den im Kapitel 6 beschrieben. Hierbei werden der Ansatz zur hybriden Ana-
lyse und das originäre stagewise-immutable Analyseverfahren dargestellt. Eine
umfangreiche Evaluation zur Tauglichkeit dieser Programmeigenschaft und
empirische Untersuchung der Verteilungsgüte bei einigen Programmen und
Benchmarks durch Einsatz einiger vorgestellter Strategien beschreibe ich im
Kapitel 8. Zuvor werden im Kapitel 7 einige wichtige Entwurfsaspekte des
JScatter-Systems dargestellt.
Abschließend werden die Kernpunkte des Konzepts kurz zusammengefasst.
Besondere Aspekte über das Konzept zur hybriden Analyse und spezielle
Programmeigenschaften, die sehr interessant für die automatische Verteilung
sind, jedoch nicht untersucht werden konnten, werden ebenfalls als Erweite-
rungspunkte beschrieben.
5
.
2. Java und sein Modell zur verteilten
Ausführung
Inhalt
2.1. Multithreading-Konzept und die parallele Programmierung . 9
2.1.1. Prozesse und ihre Interaktion . . . . . . . . . . . . . . . . 9
2.1.2. Java-Threads und das Multithreading-Konzept . . . . . . 12
2.1.3. Unterstützung der parallelen Programmierung . . . . . 15
2.2. Kommunikation mittels Java Remote Method Invocation . . 18
2.2.1. Konzept von Java-RMI . . . . . . . . . . . . . . . . . . . . 18
2.2.2. Objekt-Serialisierung . . . . . . . . . . . . . . . . . . . . . 20
2.2.3. Semantische Unterscheidung zwischen verteiltem und
lokalem Objektmodell ..................... 21
2.3. Multithreaded Java-Programme und ihre Ausführungsmo-
delle ................................... 23
2.3.1. Verzahntes und verteiltes Ausführungsmodell . . . . . . 24
2.3.2. Ausführungskontext im verteilten Modell . . . . . . . . 26
2.3.3. Verteilte Ausführungsstränge und einige relevante Ei-
genschaften . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
7
KAPITEL 2. JAVA UND SEIN MODELL ZUR VERTEILTEN AUSFÜHRUNG
Java ist eine statisch typisierte, objektorientierte Sprache. Der Programm-
Quellcode wird in eine Plattform-unabhängige Repräsentation, auch als Java-
Bytecode bezeichnet, übersetzt. Die Ausführung erfolgt dadurch, dass die
Bytecode-Instruktionen von einer abstrakten Maschine, der so genannten Java
Virtual Machine (JVM), interpretiert werden. Im Vergleich zu anderen ähnli-
chen objektorientierten Sprachen wie C++ ist Java viel kleiner im Sprachum-
fang. Stattdessen werden soviel wie möglich die Merkmale vom Sprachkern
in die Java Standard-Bibliotheken verlagert. Trotzdem wurden aber funda-
mentale Datenstrukturen wie Zeichenkette und wichtige Konzepte wie Multi-
threading in die Sprache eingebaut.
In diesem Kapitel gehe ich auf die wichtigsten Aspekte ein, die auf das ge-
samte Thema Bezug nehmen und als Grundlage für die Schritte zur auto-
matischen Verteilung dienen. Die parallele Programmierung in Java stellt ei-
ne notwendige Voraussetzung dar. Mit Hilfe der in die Sprache integrierten
Merkmale und Mechanismen aus der Standard-Bibliothek kann die Formu-
lierung einer parallelen Problemlösung erfolgen. Dieses Programm dient als
Ausgangspunkt für die automatische Verteilung. Im Abschnitt 2.1 werden das
Konzept des Multithreading und einige Paradigmen zur parallelen Program-
mierung in Java vorgestellt.
Die Kommunikation der verteilten Berechnungen wird durch entfernte Me-
thodenaufrufe in Java realisiert. Mittels Java-RMI oder KaRMI, einer schnelle-
ren RMI-Bibliothek der Universität Karlsruhe, kann das grundlegende Kom-
munikationsmodell festgelegt werden. Die Eigenschaften dieses Modells ha-
ben einen starken Einfluss auf das Konzept der automatischen Transforma-
tion und Entwurfsentscheidungen für die verteilte Laufzeitumgebung. Auf
diesen Aspekt werde ich im Abschnitt 2.2 eingehen.
Eine der wichtigsten Voraussetzungen bei der automatischen Verteilung ei-
nes mehrsträngigen Java-Programms ist es zu gewährleisten, dass die Aus-
führungssemantik des verteilten Programms der Semantik des sequentiellen
Programms entspricht. In meinem gesamten Konzept wird ein mehrsträngi-
ges Java-Programm in eine Version transformiert, die dann automatisch ver-
teilt in einer speziellen Laufzeitumgebung ausgeführt werden kann. In dieser
Laufzeitumgebung sind mehrere normale JVMs auf verschiedenen Rechner-
knoten an einer parallelen Problemlösung beteiligt. Aus Benutzersicht verhält
sich diese verteilte Laufzeitumgebung semantikgleich wie eine normale JVM.
Dazu muss das transformierte Programm die gleichen semantischen Eigen-
schaften besitzen wie das Original und alle Funktionalitäten der JVM müssen
auf die verteilte Laufzeitumgebung abgebildet werden. Dafür wird tiefgehen-
8
2.1. MULTITHREADING-KONZEPT UND DIE PARALLELE
PROGRAMMIERUNG
des Wissen über das Modell zur Ausführung mehrsträngiger Programme im
sequentiellen sowie im verteilen Fall benötigt.
2.1. Multithreading-Konzept und die parallele
Programmierung
Das Konzept Ausführungsstrang (thread of control) wird in der Sprache Java di-
rekt unterstützt. Selbst die Eigenschaften eines mehrsträngigen Systems wie
thread scheduling finden sich in der Java Virtual Machine. Das Thread-Konzept
selbst ist nicht neu: Früher boten die Betriebssysteme Bibliotheken an, mit de-
ren Hilfe C-Programmierer Threads erzeugen konnten. In Sprachen wie Ada
sind Threads Teil der Sprachdefinition. Hier möchte ich eine kleine Einfüh-
rung in die Java-Threads und das Konzept Multithreading darstellen, begin-
nend mit einem kleinen Auszug aus der Grundlage über Prozesse und ihre
Interaktion in der Terminologie eines (Unix-artigen) Betriebssystems. Die fun-
damentalen Konzepte zur parallelen Programmierung, die Java unterstützt,
werden im Anschluss daran vorgestellt.
2.1.1. Prozesse und ihre Interaktion
Prozesse
In modernen Betriebssystemen ist die Abarbeitung von Programmen in Pro-
zessen organisiert. Ein Prozess besteht im Wesentlichen aus einem Stück Pro-
grammcode, einem oder mehrerer Datenbereiche, auf die er zugreift, und
dem aktuellen Zustand der Bearbeitung. Die virtuelle Speicherverwaltung
des Betriebssystems trennt die Adressräume der einzelnen Prozesse. So steht
jedem Prozess ein eigener Speicherraum zur Verfügung und die Konflikte
durch Speicherzugriffe zwischen den Prozessen können vermieden werden.
Grundsätzlich sind Adressräume unabhängig von Prozessen zu sehen. Je-
der Prozess benötigt zwar zu jedem Zeitpunkt einen Adressraum, es ist aber
möglich, dass ein Prozess zwischen mehreren Adressräumen wechselt, ge-
nau einen Adressraum besitzt (heavyweight process) oder mehrere Prozesse
sich einen Adressraum teilen (lightweight process). Ein Prozess ist repräsentiert
durch einen Prozesskontrollblock (PCB), der wichtige Informationen enthält:
9
KAPITEL 2. JAVA UND SEIN MODELL ZUR VERTEILTEN AUSFÜHRUNG
seine Charakteristika (Prozesskennung, Name des Programms), Zustandsin-
formation (Befehlszähler, Stackzeiger, Registerinhalte) und Verwaltungsdaten
(Priorität, Rechte, Statistikdaten).
Prozessumschaltung
In einem Betriebssystem existieren zu einem Zeitpunkt in der Regel viele Pro-
zesse. Ein Prozess, der gerade durch den Prozessor bearbeitet wird, heißt rech-
nend. Es kann in einem Rechner höchstens so viele rechnende Prozesse geben
wie Prozessoren vorhanden sind. Um einen anderen Prozess zu bearbeiten,
muss umgeschaltet werden: Die Zustandsinformation des rechnenden Pro-
zesses wird gerettet, danach wird die Zustandsinformation des neuen Prozes-
ses in den Prozessor geladen. Das Umschalten kann freiwillig erfolgen oder
erzwungen werden, z.B. ausgelöst durch eine Hardware-Unterbrechung (pre-
emption). Der Teil eines Betriebssystems, der die Prozessumschaltung über-
nimmt, heißt Scheduler. Der Umschaltungsvorgang geschieht ausschließlich
im Betriebssystemkern.
Da das Umschalten eines Prozesses, der einen eigenen Adressraum be-
sitzt, mit einem aufwändigen Adressraumwechsel verbunden ist, nennt man
solch einen Unix-artigen Prozess auch schwergewichtiger Prozess (heavyweight
process). Dagegen werden die Prozesse als leichtgewichtig (lightweight) oder
Threads bezeichnet, die in einem gemeinsamen Adressraum ablaufen. Die-
se Möglichkeit wird von modernen Betriebssystemen wie Linux angeboten.
Ein derartiger Prozess degeneriert dadurch zu einer Art Ablaufumgebung der
Threads.
Zustände eines Prozesses
Ein Prozess kann drei (aktive) Zustände besitzen: wartend, bereit und rech-
nend, wie in Abbildung 2.1 verdeutlicht wird. Sie können durch Übergän-
ge erreicht werden, die durch entsprechende Operationen des Betriebssys-
temkerns realisiert werden. Es gibt dennoch zwei weitere Zustände für einen
Prozess: nicht-aktiv und nicht-existent. Bei Java-Threads haben diese beiden
Zustände keine zentrale Bedeutung.
10
2.1. MULTITHREADING-KONZEPT UND DIE PARALLELE
PROGRAMMIERUNG
Nicht-
existent Nicht-
aktiv Bereit Rechnend
Wartend
schen
erzeugen
deaktivieren Prozessor
aufgeben
aktivieren
Prozessor
zuordnen
deblockieren blockieren
Scheduling
aktiv
existent
Abbildung 2.1.: Prozesszustandsdiagramm
Prozessinteraktion
In einem komplexen Programmsystem interagieren die Prozesse miteinander
unter dem zeitlichen und funktionalen Aspekt. Beim zeitlichen Aspekt geht es
um die Koordination zwischen den beteiligten Prozessen, die durch Synchro-
nisation miteinander abgestimmt werden können. Beim funktionalen Aspekt
kooperieren oder kommunizieren die Prozesse miteinander. Wenn mehrere
Prozesse auf gemeinsame Daten zugreifen, spricht man von einer Kooperation;
der explizite Datentransport wird unter dem Begriff Kommunikation verstan-
den. Diese beiden Formen können aufeinander abgebildet werden.
Parallelität und Nebenläufigkeit
In einem Multiprozessor-System können mehrere Prozesse entsprechend
der vorhandenen Prozessoren gleichzeitig parallel ausgeführt werden. Man
spricht von parallelen Prozessen. Im System mit einem einzigen Prozessor wer-
den mehrere Prozesse verzahnt ausgeführt. Das heißt, der Prozessor bearbeitet
die Prozesse stückweise abwechselnd. Der Umschaltungsvorgang wird vom
Prozessverwalter oder von den Prozessen selbst ausgelöst. Was man wahr-
nimmt, ist eine Quasiparallelität. Da das Umschalten sehr schnell und häu-
fig geschieht, vermittelt es den Eindruck, dass alle Prozesse gleichmäßig fort-
11
KAPITEL 2. JAVA UND SEIN MODELL ZUR VERTEILTEN AUSFÜHRUNG
schreiten. Im Folgenden verstehe ich unter nebenläufigen Prozessen solche, die
parallel oder verzahnt ausgeführt werden.
2.1.2. Java-Threads und das Multithreading-Konzept
Java-Threads
In Java hat man die Möglichkeit, seine Programme durch das Konzept
Threads nebenläufig zu formulieren. Die Java-Threads oder Ausführungs-
stränge sind leichtgewichtigte Prozesse, die parallel im gemeinsamen Adress-
raum (Speicher) ablaufen können und sehr schnell in der Umschaltung sind 1.
Dadurch können die Java-Threads untereinander auf ihre öffentlichen Daten
zugreifen. Im Vergleich zu Unix-Prozessen, die durch einen Prozesskontroll-
block repräsentiert sind, sind Java-Threads innerhalb eines Programms Ob-
jekte der Klasse java.lang.Thread. Um Thread-Objekte zu kreieren, müs-
sen die zugehörigen Klassen entweder das Interface java.lang.Runable
implementieren oder Unterklasse der Klasse java.lang.Thread sein. Die
Erzeugung eines Thread-Objekts, der Datenaustausch mit anderen über den
gemeinsamen Speicher und das Löschen eines Thread-Objekts mittels des
Garbage Collectors finden innerhalb der JVM statt.
Weiterhin kann sich jeder Java-Thread in der JVM in einem der folgenden
Zustände befinden:
Initial: Dieser Zustand tritt nach der Erzeugung des Thread-Objekts auf,
also nach einem Konstruktor-Aufruf, aber bevor die start()-Methode
aufgerufen wird.
Aktiv: Ein Thread ist in einem aktiven Zustand, wenn die start()-
Methode aufgerufen wurde. Dadurch wird die run()-Methode implizit
aufgerufen, was veranlasst, dass die JVM diesen Prozess abarbeiten kann.
Dieser Zustand ist vergleichbar mit dem Bereit-Zustand im Diagramm
2.1.
Laufend: Entsprechend dem Rechnend-Zustand in Abbildung 2.1 wird ei-
ner von den aktiven Threads nach einem Auswahlverfahren der JVM aus-
gewählt und seine run()-Methode wird abgearbeitet. Dies ist der Fall
1Eine Ausnahme sind Java-Applets, auf die aber in dieser Arbeit nicht näher eingegangen
wird.
12
2.1. MULTITHREADING-KONZEPT UND DIE PARALLELE
PROGRAMMIERUNG
eines Einprozessor-Systems, wobei nur ein Prozess vom Prozessor ab-
gearbeitet wird. In einem Multiprozessor-System können gleichzeitig so
viele Threads bearbeitet werden, wie es verfügbare Prozessoren gibt.
Blockiert: Der Thread wird blockiert, weil er auf ein Ereignis warten muss
und nicht laufen kann.
Beendet: Dieser Zustand wird erreicht, nachdem die run()-Methode bis
zum Ende ausgeführt wurde.
Multithreading-Konzept
Wie Unix-artige Prozesse können mehrere Java-Threads verzahnt ablaufen,
so dass sich für den Benutzer der Eindruck von Gleichzeitigkeit ergibt. Man
kann mehrere nebenläufige Java-Threads innerhalb einer JVM auf die gleiche
Weise betrachten, wie die Prozesse innerhalb eines Betriebssystems neben-
läufig ausgeführt werden. Abbildung 2.2 verdeutlicht dies. Man nennt diese
Umgebung, in der das Programm so abläuft, auch multithreaded (mehrsträn-
gig).
Java Virtual Machine
Thread 1
Gemeinsamer Speicher
Lokale Variablen
Thread 2 Thread 3
Lokale Variablen Lokale Variablen
Anwendung 2
Betriebssystem
Anwendung 1
Abbildung 2.2.: Das Multithreading-Konzept in Java
13
KAPITEL 2. JAVA UND SEIN MODELL ZUR VERTEILTEN AUSFÜHRUNG
Es ist häufig der Fall, dass sich mehrere Threads in einem Java-Programm im
aktiven Zustand befinden. In diesem Fall wählt die JVM einen Thread aus dem
Pool der aktiven Threads aus und bearbeitet ihn, d.h. er läuft. Die Auswahl-
strategie bestimmt der Scheduler, der ein Teil der JVM ist. Die Umschaltzeit
der Java-Threads durch diesen Scheduler ist viel kürzer.
Scheduling von Java-Threads
In der Java Spezifikation ist nicht definiert, welches Scheduling-Modell als
Auswahlverfahren zugrunde liegt. Das ist von Plattform zu Plattform unter-
schiedlich. Das am häufigsten verwendete Verfahren ist ein prioritätsbasiertes
Verfahren: Jedem Java-Thread wird eine bestimmte Priorität zugewiesen, al-
so eine positive ganze Zahl im festdefinierten Wertebereich. Wenn ein aktiver
Thread eine höhere Priorität als der laufende Thread hat, so schaltet die JVM
auf diesen Thread um. Es gibt zwei grundlegende Modelle zum Scheduling
von Java-Threads:
Das green-thread-Modell: Der Scheduler der JVM bestimmt selbst die Aus-
wahlstrategie für die Java-Threads. Dies ist das Originalmodell für die
JVMs der älteren Generation (bei JDK der Version 1.3 oder älter) und
wird heutzutage nicht mehr verwendet. Abhängig von der Implemen-
tierung der JVMs folgt es meist dem prioritätsbasierten Scheduling. In
der Praxis, wie es in der Spezifikation der JVM [T. 99] beschrieben wur-
de, wird nicht gewährleistet, wann und wie genau ein Java-Thread vom
Scheduler gewählt wird.
Das native-thread-Modell: In diesem Modell wird ein Java-Thread
auf einen Betriebssystem-Thread abgebildet, beispielsweise auf einen
pthread bei Linux. So übernimmt der Scheduler des Betriebssystems, auf
dem die JVM läuft, die Aufgabe, die Threads zu schedulen. Das Schedu-
ling ist in diesem Fall bei verschiedenen Betriebssystemen unterschied-
lich.
Der Umschaltvorgang beim green-thread-Modell dauert viel länger, da er in-
nerhalb der JVM geregelt werden muss. Die verschiedenen Java-Threads blei-
ben in diesem Fall nur innerhalb der JVM sichtbar. Die Parallelität wird von
der virtuellen Maschine simuliert, der Java-Interpreter regelt dann den Ab-
lauf, die Synchronisation und die verzahnte Ausführung. Dieses Modell wird
14
2.1. MULTITHREADING-KONZEPT UND DIE PARALLELE
PROGRAMMIERUNG
heutzutage nur in den JVMs implementiert, die auf Betriebssystemen ohne
Thread-Unterstützung oder nicht im Multi-Tasking laufen, z.B. bei einigen
PDAs oder Handys.
Beim nativen Thread-Modell regelt das Betriebssystem selbst alle Aufgaben
für die von Java-Threads abgebildeten pthreads. Im Falle eines Multiprozessor-
Systems werden die abgebildeten Threads vom Betriebssystem automatisch
auf die verschiedenen Prozessoren verteilt. Die Kommunikation kann in die-
sem Fall als lokal betrachtet werden. In meinem Ansatz wird diese Art der
Verteilung nicht in die Betrachtung einbezogen. Trotzdem kann diese Leis-
tungsfähigkeit auch bei der Lastberechnung des automatischen Verteilungs-
modells berücksichtigt werden.
Weitere ausführliche Diskussion über verschiedene Implementierungen für
obige Thread-Modelle findet sich im Kapitel 6 in [OW99].
2.1.3. Unterstützung der parallelen Programmierung
Für die parallele Programmierung bietet die Java-Quellsprache selbst not-
wendige Mechanismen für das Schreiben von Thread-sicherem Code an. In
diesem Multithreading-Modell (Programmiermodell gemeinsamer Speicher) fin-
det der Datenaustausch zwischen parallel ablaufenden Ausführungssträngen
durch Benutzung gemeinsamer, globaler Variablen oder Objekte statt. Diese
befinden sich auf dem Heap und sind daher für alle Threads zugreifbar. Allge-
mein gibt es zwei Synchronisationsmechanismen, die für das koordinierte Zu-
sammenarbeiten von Prozessen notwendig sind. Das sind gegenseitiger Aus-
schluss (mutual exclusion), wobei mehrere Prozesse die kritischen Abschnit-
te nicht gleichzeitig betreten dürfen, und Bedingungssynchronisation (condition
synchronization), bei der ein Prozess so lange warten muss, bis eine bestimmte
Bedingung von einem anderen Prozess erfüllt wird. Bei Java finden sich schon
in der Sprachdefinition Mechanismen wie synchronized und das Monitor-
Konzept2. Direkte Unterstützung für die Bedingungssynchronisation hat Java
nicht, aber sie kann durch einige Methoden der Klasse java.lang.Object
wie wait(),notify() nachgebildet werden.
2Monitore sind ein sehr wichtiges Betriebsystemkonzept, das in den frühen Siebzigerjahren
von Per Brinch Hansen und Tony Hoare erfunden wurde.
15
KAPITEL 2. JAVA UND SEIN MODELL ZUR VERTEILTEN AUSFÜHRUNG
Thread-Interaktion
Zur Synchronisation der Java-Threads verwendet Java Monitore. Das sind
Programmmodule (Objekte), die Daten (Objektvariablen) und Operationen
(Objektmethoden) darauf kapseln. Zu jedem Zeitpunkt darf höchstens ein
Java-Thread im Monitor sein. Dadurch kann das Konzept für gegenseitigen
Ausschluss und die Bedingungssynchronisation, wobei die Bedingungsvaria-
blen lokal im Monitor benutzt werden, realisiert werden. Die Objekte jeder
Klasse in Java können als Monitor verwendet werden. Methoden, die kriti-
sche Abschnitte auf Objektvariablen implementieren, werden als synchroni-
zed gekennzeichnet. Für ein Monitorobjekt werden diese Methoden unter ge-
genseitigem Ausschluss ausgeführt, wenn mehrere Ausführungsstränge sie
gleichzeitig aufrufen.
Die Synchronisation wird durch eine interne Synchronisationsvariable des
Objekts (lock) implementiert. Die Sprache hat keine direkte Unterstützung,
separate Lock- und Unlock-Aktionen auszuführen. Dies muss durch Verwen-
dung der Konstrukte auf höherer Ebene in geeigneter Weise geschehen. Zu
bemerken ist, dass die JVM spezielle Bytecode-Instruktionen für Lock- und
Unlock-Aktionen unterstützt. Das sind monitorenter und monitorexit. Wie der
Synchronisationsprozess genau abläuft, soll durch folgendes Beispiel ver-
deutlicht werden: Wenn ein synchronized Modifizierer beispielsweise auf
eine statische Methode einer Klasse angewandt wird, dann beschafft die Java
Virtual Machine das exklusive Lock auf diese Klasse, bevor ein Aufruf der
Methode ausgeführt wird. Ein Thread, der in diesen Codeblock einzutreten
versucht, muss das Lock holen, bevor der Code im synchronisierten Block
tatsächlich ausgeführt wird. Wenn ein anderer Thread gerade zu diesem Zeit-
punkt diesen kritischen Abschnitt betritt, wird dieser Thread so lange blo-
ckiert, bis der laufende Thread diesen Abschnitt verlässt und das Lock auf
diese Klasse freigegeben wird. Für Objektmethoden gilt Entsprechendes.
Die Verwaltung blockierter Threads für jedes Objekt wird durch elementare
Methoden, die in der Basisklasse java.lang.Object implementiert wer-
den, realisiert. Somit steht dieser Mechanismus automatisch für jede Java-
Klasse zur Verfügung. Die elementaren (nativen) Methoden zur Synchronisa-
tion wait() und notify()3dürfen nur innerhalb synchronized definier-
ter Methoden oder Blöcke verwendet werden, d.h. der Thread muss das Lock
auf das Objekt besitzen, deren wait() und notify() Methoden aufgeru-
fen werden. Sonst wird eine Ausnahme ausgelöst. notifyAll() ist ähnlich
3Siehe die Java API für mehr Details über diese Methoden.
16
2.1. MULTITHREADING-KONZEPT UND DIE PARALLELE
PROGRAMMIERUNG
wie notify(), bei der aber alle wartenden Threads aufgeweckt werden. Nur
dasjenige, das das Lock des Objekts gewinnt, darf dann laufen. Zu bemerken
ist, dass die JVM keine Mechanismen zum Entdecken und Vermeiden von
Verklemmungen hat. Das tritt dann auf, wenn ein Thread Ressourcen besitzt,
die von einem anderen Thread benötigt werden und so zyklisches Warten auf
die Ressourcen entsteht. Es ist dem Programmierer selbst überlassen, diese zu
vermeiden.
Thread-Gruppen
In Java besteht die Möglichkeit, die erzeugten Thread-Objekte zu einer Grup-
pe zusammenzufassen. Jeder erzeugte Thread gehört automatisch zu einer
Gruppe, die durch ein ThreadGroup-Objekt repräsentiert und von der JVM
zur Verfügung gestellt wird. Die Threadgruppen dienen in Java zwei Zwe-
cken: Man kann mehrere Threads innerhalb einer Gruppe durch eine ein-
zige Methode manipulieren; außerdem unterstützt es grundlegende Sicher-
heitsmechanismen für die Threadinteraktion. Die Beziehung zwischen den
Threadgruppen in einem Java-Programm ist eine Baumhierarchie. Die Wur-
zel des Baumes ist die System-Thread-Gruppe, in der einige System-Threads
wie Garbage-Collector-Thread, Clock-Thread usw. enthalten sind. Die Klasse
ThreadGroup stellt dem Programmierer einige nützliche Methoden zur Ver-
waltung und Manipulation zur Verfügung.
Speichermodell in Java
Nach dem Multithreading-Paradigma, wie es in Abbildung 2.2 dargestellt wur-
de, laufen die Ausführungsstränge in einer gemeinsamen JVM. Jeder Java-
Thread hat eigenen Arbeitsspeicher, in dem er Kopien der gemeinsam von al-
len Threads benutzten Variablen aus dem Heap hält und darauf arbeitet. Nach
der Sprachspezifikation für das Speichermodell muss die JVM die Daten kon-
sistent halten. Daher sorgt die JVM dafür, dass die von Threads zwischen-
gespeicherten Daten auf den Heap zurückgeschrieben werden, sobald es er-
forderlich ist. Der Vorgang läuft grob folgendermaßen ab: Um auf eine ge-
meinsame Variable zuzugreifen, beschafft ein Thread zunächst ein Lock und
leert seinen lokalen Speicher. Anschließend wird der Wert der Variable aus
dem Heap geholt und hier gespeichert. Wenn der Thread das Lock wieder
zurückgibt, muss er gewährleisten, dass der Wert in seinem lokalen Speicher
wieder auf den Heap zurückgeschrieben wurde.
17
KAPITEL 2. JAVA UND SEIN MODELL ZUR VERTEILTEN AUSFÜHRUNG
Die vom obigen Modell festgelegten Vorschriften gelten jedoch nicht ganz für
Variablen, die als volatile gekennzeichnet werden. Werte solcher Variablen
werden nicht zwischengespeichert, sondern immer aktuell gehalten. Die par-
allel ablaufenden Threads sehen immer den aktuellen Variablenwert, da sie
ihn vor der Verwendung der Variablen aus dem gemeinsamen Speicher le-
sen und nach einer Änderung sofort zurückschreiben müssen. Nur Objekt-
und Klassenvariablen können als volatile deklariert werden. Weitere tech-
nische Details können im Abschnitt 17 in [B. 00] nachgelesen werden.
2.2. Kommunikation mittels Java Remote Method
Invocation
Durch Java RMI ist es möglich, Berechnungen einer mehrsträngigen An-
wendung verteilt auf verschiedenen Rechnern durchführen zu können. Hier-
bei wird der Datenaustausch über Rechnergrenzen hinweg durch die ent-
fernten Methodenaufrufe realisiert. Meinem Gesamtkonzept zur automati-
schen Verteilung von Java-Anwendungen liegt dieses Kommunikationsmo-
dell zugrunde. Ich werde in diesem Abschnitt das Konzept der Java RMI
und die Technik der Objektserialisierung vorstellen, die bei der Datenüber-
tragung über das Netzwerk des RMI-Modells verwendet wird. Aus der se-
mantischen Unterscheidung zwischen lokalem und verteiltem Objektmodell
mit RMI ergeben sich einige Aspekte, die bei den Entwurfsentscheidungen
für das Transformations- und verteilte Laufzeitkonzept berücksichtigt wer-
den müssen. Darauf gehe ich abschließend ein.
2.2.1. Konzept von Java-RMI
Java-RMI [Mic], [Mic98], [MPA99], das ein Teil der Standard-Bibliothek ist,
bietet die Möglichkeit, Objekte über Rechnergrenzen hinweg eindeutig zu
identifizieren und deren Methoden entfernt ausführen zu lassen. Objekte ei-
ner Klasse, die die Schnittstelle java.rmi.Remote implementieren, werden
als remote-Objekte bezeichnet. Ihre Methoden können von anderen Objekten
entfernt aufgerufen werden.
Konzeptionell basiert Java-RMI auf dem Client-Server-Modell. Das remote-
Objekt befindet sich tatsächlich auf der Server-Seite. Über entfernte Metho-
denaufrufe kann man das Objekt nicht direkt ansprechen, sondern nur über
18
2.2. KOMMUNIKATION MITTELS JAVA REMOTE METHOD INVOCATION
das Skeleton. Das ist ein Stellvertreter-Objekt, das alle entfernten Aufrufe ent-
gegennimmt und sie schließlich lokal an die entsprechenden Methoden des
Objekts delegiert. Das Resultat eines Methodenaufrufs wird vom Skeleton
wieder an den entsprechenden Sender zurückgeschickt.
Rechner 1
lokales Objekt O1Stub O2
O2.method();
Rechner 2
entferntes Objekt O2
Skeleton O2
public void method()
{
...
...
...
}
Rechner 3
lokales Objekt O3Stub O2
O2.method();
Rechner 4
lokales Objekt O4
Stub O2
O2.method();
method() method()
method()
method()
Abbildung 2.3.: Das RMI-Konzept
Zu einem remote-Objekt können beliebig viele Stubs existieren. Auf den
Client-Seiten dienen diese Stubs (oder Proxies) als Vertreter des Objekts. Sie
sehen bezüglich der entfernt aufrufbaren Methoden genauso aus wie das ent-
sprechende remote-Objekt. Über die Stubs können Methoden eines remote-
Objekts entfernt aufgerufen werden. Der Transfer der Methodenparameter
und die Initialisierung des eigentlichen Methodenaufrufs in der virtuellen
Maschine auf der Client-Seite werden von den Stubs übernommen. Es ist
beim RMI-System möglich, mehrere Methodenaufrufe nebenläufig an einem
remote-Objekt durchführen zu können. Das Skeleton initialisiert dazu loka-
le Aufrufe, die dann jeweils in eigenen Threads ausgeführt werden. Stam-
men mehrere solcher entfernter Methodenaufrufe aus einer gleichen virtuel-
len Maschine, sorgt das RMI-System dafür, dass diese Aufrufe nacheinander
in einem einzigen Thread auf der Server-Seite ausgeführt werden.
19
KAPITEL 2. JAVA UND SEIN MODELL ZUR VERTEILTEN AUSFÜHRUNG
Mittels RMI lassen sich Remote-Objekte auf dem ganzen verteilten System ein-
deutig identifizieren. Zuständig dafür ist ein eigenständiger Prozess, ein so
genanntes RMI-Registry. Er übernimmt die Aufgabe der Objektverwaltung
und des Namensdienstes. Nachdem sich ein remote-Objekt auf der Server-
Seite beim RMI-Registry erfolgreich registriert hat, kann es Aufrufe der Cli-
ents entgegennehmen.
2.2.2. Objekt-Serialisierung
Durch die Technik der Serialisierung in Java können Kopien von Objekt-
en als Parameter oder Rückgabewerte über das Netzwerk gesendet oder
empfangen werden. Eine Klasse, deren Objekte serialisierbar sein sollen,
muss die Schnittstelle java.io.Serializable oder deren Subinterface
java.io.Externalizable implementieren. Bei der Serialisierung handelt
es sich um das Erstellen eines transportierbaren Abbildes eines Objekts, aus
dem durch Deserialisierung eine exakte Replik des Originals, also ein Objekt
mit identischem Zustand, wieder rekonstruiert werden kann.
Konzeptionell kann der Zustand eines Objekts aus zwei Teilen bestehen: per-
sistenter und nicht-persistenter Zustand. Zum erstgenannten gehören Ob-
jektbestandteile, die nach der Deserialisierung des Objekts wieder verfügbar
sind. Alle anderen Bestandteile bilden nur vorübergehend einen Teil des Ob-
jektzustandes oder können rekonstruiert werden und gehören deshalb zum
nicht-persistenten Zustand eines Objekts. Beispiel dafür sind Datenfelder, die
nur lokale Ressourcen wie Pfade zu Dateien oder temporäre Werte enthalten.
Diese werden auch als transient gekennzeichnet. Die Objektserialisierung
ist das Speichern des persistenten Zustands eines Objekts.
Bei der Standard Serialisierungstechnik von Sun wird der Zustand eines Ob-
jekts in einen Bytestrom gewandelt, zum Ziel durch das Netzwerk transpor-
tiert, dann aus dem Bytestrom durch Deserialisierung eine Kopie des Objekts
im selben Zustand rekonstruiert. Bei dieser Serialisierung werden alle nicht
transient und nicht static Felder eines Objekts in einen Bytestrom ge-
schrieben. Referenziert ein Feld ein Objekt, so muss es serialisierbar sein. Fel-
der von primitiven Typen sind immer serialisierbar. Wird ein Objekt seria-
lisiert, werden alle seiner Felder auch serialisiert. Auf diese Weise wird der
gesamte Objektgraph, der direkt oder indirekt mit einem zu serialisierenden
Objekt verbunden ist, mit serialisiert. Sind dabei zyklische Strukturen durch
mehrfache Referenzierung auf dasselbe Objekt enthalten, werden diese durch
20
2.2. KOMMUNIKATION MITTELS JAVA REMOTE METHOD INVOCATION
die Hashing-Technik aufgelöst. Wenn das zu serialisierende Objekt vom Ar-
raytyp ist, wird die Anzahl seiner Elemente und die Elemente analog seriali-
siert.
Bei der Deserialisierung wird der Objektzustand wieder hergestellt, d.h.
den Datenfeldern werden ihre serialisierten Werte zugeordnet und nicht-
persistenten Teilen werden typspezifische Initialwerte zugewiesen.
2.2.3. Semantische Unterscheidung zwischen verteiltem und
lokalem Objektmodell
Rechner 1
lokales Objekt O1Stub O2
O2 .method();
Rechner 2
entferntes Objekt O2
Skeleton O2
public void method() {
...
...
...
}
Rechnergrenze
Blockierter
Prozess
Abbildung 2.4.: Semantik des Methodenaufrufs bei RMI
Die Realisierung von RMI arbeitet mit verschiedenen Prozessen sowohl auf
der Client- als auch auf der Server-Seite, wie es in Abbildung 2.4 verdeut-
licht wird. Es wird dabei versucht, die Semantik eines gewöhnlichen Metho-
denaufrufs weitgehend wie im nicht verteilten Fall einzuhalten. Die Ausfüh-
rung eines entfernten Methodenaufrufs erfolgt synchron, d.h. der Aufruf blo-
ckiert so lange, bis die Abarbeitung auf dem Remote-Rechner beendet ist. Die
Syntax für einen Methodenaufruf ist, unabhängig davon ob das Objekt lo-
kal oder remote ist, identisch, weil das Stub und das remote-Objekt bezüglich
der entfernt aufrufbaren Methoden den gleichen Typ besitzen. Daher kann
der instanceof-Operator genauso wie die Typkonversion im normalen Fall
verwendet werden. Daneben gibt es aber einige grundlegende Unterschei-
dungen zwischen dem verteilten Objektmodell mit RMI und lokalem Modell:
21
KAPITEL 2. JAVA UND SEIN MODELL ZUR VERTEILTEN AUSFÜHRUNG
Das direkte Ansprechen eines remote-Objekts ist im verteilten Fall nicht
möglich, sondern nur über seine Schnittstelle. Eine Klasse, deren Objekte
remote sein sollen, muss das Interface java.rmi.Remote implementie-
ren. Die Typdeklaration für diese Objekte im Programm muss über die-
ses Interface erfolgen. Alle entfernten Methodenaufrufe mit dem remote-
Objekt erfolgen nicht direkt mit dem Objekt, sondern über seine Schnitt-
stelle.
Wenn bei einem entfernten Methodenaufruf lokale Objekte, also keine
remote-Objekte als Argumente übergeben oder als Ergebnis zurückgelie-
fert werden, dann geschieht dies per Kopie und nicht als Referenzüber-
gabe wie sonst in Java üblich. Eine Referenzübergabe würde in diesem
Kontext keinen Sinn ergeben, da eine Referenz auf ein lokales Objekt in
einer anderen JVM nicht verwendbar wäre. Außerdem verwendet RMI
zur Parameterübergabe und Ergebnisrückgabe die Objektserialisierung,
deshalb sind nur serialisierbare Objekte als Argumente für einen ent-
fernten Methodenaufruf zulässig.
Die möglicherweise auftretenden Ausnahmen müssen zusätzlich bei ei-
nem entfernten Aufruf über RMI abgefangen werden.
Entfernte Zugriffe über RMI auf den statischen Anteil kann nicht durch-
geführt werden. Dazu gehören statisch initialisierte Blöcke und als sta-
tisch deklarierte Variablen und Methoden.
Öffentlich zugreifbare Objektvariablen können nicht direkt über RMI
angesprochen werden.
Ein weiterer Punkt betrifft die Zugriffsrechte der Methoden. Da jede in
der Remote-Schnittstelle deklarierte Methode implizit den Modifikator
public bekommt, sind automatisch alle entfernt aufrufbaren Methoden
in einem remote-Objekt öffentlich zugängig.
Die Semantik zur Objektsynchronisation wird mittels RMI nicht mehr
gewährleistet. Im lokalen Fall besitzt jedes Java-Objekt ein lock, über das
gegenseitiger Ausschluss beim Zugriff von mehreren Threads auf dieses
Objekt realisiert wird. Nur derjenige, der das Lock besitzt, darf in den
kritischen Abschnitt eintreten, die anderen müssen warten. So ist jedes
Objekt mit einer Wartemenge der Threads assoziiert.
Da Operationen auf remote-Objekten mittels RMI auf indirektem Weg er-
folgen, also über Stubs und Skeletons, werden Synchronisationsopera-
22
2.3. MULTITHREADED JAVA-PROGRAMME UND IHRE
AUSFÜHRUNGSMODELLE
tionen auf diesen Stubs4angewandt, nicht auf das eigentliche Objekt.
Beispiel:
synchronized (ro) {
...
notifyAll();
...
}
Die Anwendung von synchronized auf ro hat nicht die gewünschte
Wirkung wie im lokalen Fall, denn der Zugriff wird lediglich auf den
Stub ro, nicht auf das Objekt selbst geregelt.
Die hier geschilderten Aspekte, die durch die Verwendung von RMI als Kom-
munikationsplattform entstehen, müssen berücksichtigt und so gehandhabt
werden, dass sie für den Benutzer transparent bleiben, wie im lokalen Objekt-
modell. Dies gilt insbesondere für das System JScatter, das auf dem RMI oder
KaRMI aufbaut und mehrsträngige Java-Programme automatisch verteilt, oh-
ne dass der Anwender dafür etwas tun muss. Für das gesamte Konzept und
vor allem für die Entwurfsentscheidungen des JScatter-Systems spielen diese
Erkenntnisse eine wichtige Rolle.
2.3. Multithreaded Java-Programme und ihre
Ausführungsmodelle
Durch das Multithreading-Konzept eröffnet Java dem Programmierer die
Möglichkeit, Nebenläufigkeit der Problemstellung in deren Lösung zu über-
nehmen oder gezielt nebenläufige Problemlösungen zu entwickeln. Es ist die
Aufgabe des Programmierers, die Programme auf der Java-Quellcode-Ebene
auf mehrsträngiger Weise zu formulieren. Man kann das Programm direkt
übersetzen und auf einer JVM ausführen lassen. In diesem Fall wird das
Programm verzahnt ausgeführt. Oder man nutzt den RMI-Mechanismus zur
Kommunikation und bindet manuell zusätzlichen Code in sein Programm
ein. Dieses lässt sich dann auf mehreren JVMs auf verschiedenen Rechnern
ausführen. Es wird also verteilt ausgeführt. An dieser Stelle möchte ich auf
die Aspekte der beiden Ausführungsmodelle eingehen.
4zu einem remote-Objekt können beliebig viele Stubs existieren.
23
KAPITEL 2. JAVA UND SEIN MODELL ZUR VERTEILTEN AUSFÜHRUNG
2.3.1. Verzahntes und verteiltes Ausführungsmodell
Verzahnte Ausführung
Wie bereits im Abschnitt 2.1.2 beschrieben, besteht ein mehrsträngig for-
muliertes Java-Programm aus mehreren Ausführungssträngen, die sich zur
Laufzeit im aktiven Zustand befinden. Innerhalb einer JVM auf einem
Einprozessor-System werden diese Threads verzahnt ausgeführt. Hierbei
handelt es sich um eine sequentielle Ausführung.
Parallele Ausführung auf Multiprozessor-System
Demgegenüber ist ein Geschwindigkeitsvorteil bei der Programmausführung
allerdings nur zu erwarten, wenn die virtuelle Maschine auf einem Multi-
prozessorrechner zur Ausführung kommt und sie das Ausnutzen mehrerer
Prozessoren unterstützt. Das heißt, die Java-Threads werden direkt auf die
System-Prozesse des Betriebssystems abgebildet. Die Verteilung der System-
Prozesse wird automatisch vom Betriebsystem selbst durchgeführt und man
hat hier eine parallele Ausführung. Es können so viele Prozesse gleichzeitig
abgearbeitet werden, wie die zur Verfügung stehenden Prozessoren. Sie ope-
rieren miteinander über den gemeinsamen Speicher. In den beiden Fällen ist
der Kommunikationsaufwand fast null, die Zugriffe auf Objekte können als
lokale Operationen gezählt werden.
Verteilte Ausführung
Die (kosten-)günstigere Alternative zur verteilten Ausführung solcher mehr-
strängiger Programme ist der Einsatz einer verteilten Laufzeitumgebung. In
dieser Umgebung beteiligen sich mehrere virtuelle Maschinen an der paralle-
len Lösung eines Problems, um eine Geschwindigkeitssteigerung zu erzielen.
Die einzelnen virtuellen Maschinen werden auf unterschiedlichen Arbeitssta-
tionen ausgeführt, die untereinander durch ein sehr schnelles Netzwerk ge-
koppelt sind.
Im Gegensatz zum parallelen Ausführungsmodell auf einem Multiprozessor-
System, bei dem ein gemeinsamer Adressraum benutzt wird und alle Aus-
führungsstränge ihn teilen müssen, besitzt hierbei jede einzelne virtuelle Ma-
schine ihren eigenen Adressraum. Die Objekte (auch Thread-Objekte) auf un-
24
2.3. MULTITHREADED JAVA-PROGRAMME UND IHRE
AUSFÜHRUNGSMODELLE
terschiedlichen Maschinen können durch entfernte Methodenaufrufe mitein-
ander kommunizieren. Die Ausführungsstränge und die Objekte, die einer
Maschine zugeteilt sind, werden im gleichen Adressraum ausgeführt.
Zeit
W 1
W 2
W 3
W 4
1 Rechner
Zeit
Verteilt
F 1
F 2
W 3
W 4
F 2
W 1
W 2
F 1
Rechner 1
Rechner 2
Abbildung 2.5.: Verzahnte und verteilte Ausführung
Abbildung 2.5 verdeutlicht den Unterschied zwischen einer verzahnten Aus-
führung in einem Adressraum und einem verteilten Ablauf in zwei JVMs auf
zwei unterschiedlichen Rechnern. Hierbei handelt es sich um eine ideale Ver-
teilung der Threads (W1. . . W4stehen für Worker-Threads) und der Objekte
(F1, F2sind Fabrik-Objekte). Das heißt, es findet keine entfernte Kommuni-
kationen zwischen den Objekten auf verschiedenen Rechnern statt. Dies ist
aber in der realen Welt selten der Fall. Im normalen Fall kommunizieren die
Threads über Datenobjekte über Rechnergrenzen hinweg, um Daten mitein-
ander auszutauschen, wie es in Abbildung 2.6 dargestellt wird. Auf dem Bild
sind die Thread-Objekte als Rechtecke und die Datenobjekte als Kreise darge-
stellt. Die Ausführungsstränge in derselben JVM werden verzahnt ausgeführt
oder sogar parallel im Falle eines Mehrprozessor-Systems. Die Threads auf
verschiedenen JVMs werden parallel abgearbeitet.
Wie in Abbildung 2.6 visualisiert gibt es zwei Arten von Kommunikationen:
lokale und entfernte Zugriffe. Die entfernten Zugriffe über RMI verursachen
erheblich größeren Aufwand als die lokalen. Die Konsequenz ist, dass durch
die verteilte Ausführung der Threads nicht immer ein Geschwindigkeitsvor-
teil entsteht. Bei einer ungünstigen Verteilung der Threads und Objekte kann
25
KAPITEL 2. JAVA UND SEIN MODELL ZUR VERTEILTEN AUSFÜHRUNG
JVM 1
F1
W1
W2
JVM 2
F2
W3
W4
Rechnergrenze
Abbildung 2.6.: Verteilte Ausführung
das dazu führen, dass das Programm sogar langsamer abläuft als die verzahn-
te Ausführung auf einem einzigen Rechner.
Zur Vereinfachung seien die an einer verteilten Laufzeitumgebung beteiligten
Rechner nur Einprozessor-Systeme. Das heißt, es kommt keine echte Paralle-
lität innerhalb einer JVM vor.
2.3.2. Ausführungskontext im verteilten Modell
Um den Ausführungskontext eines Java-Threads im verteilten Fall zu verste-
hen, betrachten wir nun den eines Ausführungsstrangs im sequentiellen Fall.
Sequentieller Ablauf
Nach der Erzeugung eines Thread-Objekts befindet sich ein Thread noch im
initialen Zustand. Durch den Aufruf der start()-Methode wechselt er in
den aktiven Zustand. Mit der Ausführung der run()-Methode, die unmittel-
bar innerhalb der start()-Methode aufgerufen wird, beginnt der eigentli-
che Ablauf eines Java-Threads. Nachdem die run()-Methode vollständig ab-
gearbeitet wurde, beendet der Thread seine Ausführung. Während der Aus-
führung werden die einzelnen Anweisungen innerhalb von run() sequenti-
ell abgearbeitet. Wenn innerhalb von run() Methodenaufrufe enthalten sind,
26
2.3. MULTITHREADED JAVA-PROGRAMME UND IHRE
AUSFÜHRUNGSMODELLE
werden die aufgerufenen Methoden auf diese Weise genauso behandelt. Al-
le diese Aufrufe finden synchron statt. Da sich alle Objekte, also auch die
Thread-Objekte, im selben Adressraum befinden, umfasst die gesamte Aus-
führungszeit eines Threads die Zeit zur Abarbeitung aller Anweisungen in-
nerhalb von run() und die Abarbeitungszeit aller aufgerufenen Methoden.
Verteilter Ablauf
Im verteilten Fall können ein Thread und Objekte, deren Methoden von die-
sem Thread aufgerufen werden, in verschiedenen Adressräumen liegen. Die
Ausführung eines entfernten Methodenaufrufs über RMI erfolgt auch syn-
chron, wie es im Abschnitt 2.2 beschrieben wurde. Während der Bearbeitung
der aufgerufenen Methoden auf anderen Rechnern wird der Thread auch so
lange blockiert, bis das endgültige Ergebnis an den ursprünglichen Aufruf-
sort zurück gegeben wird. Die Semantik eines Aufrufs im verteilten Fall mit-
tels RMI bleibt also erhalten.
Weiterhin wird bei solchen entfernten Aufrufen aber nur auf der Seite Rechen-
zeit verbraucht, auf der sich die Methoden tatsächlich befinden. Es ist so, als
ob ein Thread sich durch einen entfernten Methodenaufruf und weitere Un-
teraufrufe von seinem ursprünglichen Standort zu anderen Rechnern wandel-
te, dort die Methode ausführte und schließlich wieder zu seinem Ausgangs-
punkt zurückkehrte. Zur Bearbeitungszeit eines solchen entfernten Metho-
denaufrufs müssen neben der Ausführungszeit der aufgerufenen Methoden
noch zusätzlich die Kommunikationskosten mitgerechnet werden. Zu den
Kommunikationskosten tragen die Abarbeitung der Stubs und Skeleton, die
Serialisierung der Parameter und Rückgabewerte und die eigentliche Über-
tragung der Bytestrom über das Netzkwerk Rechnung. Dies ist der wesent-
liche Unterschied zur Ausführung eines Java-Threads in einem Adressraum.
2.3.3. Verteilte Ausführungsstränge und einige relevante
Eigenschaften
In meinem gesamten Konzept zur automatischen Verteilung von mehrsträn-
gigen Java-Programmen liegt dieses Ausführungsmodell zugrunde. Im Fol-
genden werden die verteilten Threads und einige relevante Eigenschaften be-
trachtet.
27
KAPITEL 2. JAVA UND SEIN MODELL ZUR VERTEILTEN AUSFÜHRUNG
Aktivitäten - Die verteilten Ausführungsstränge
Das Konzept der verteilten Ausführungsstränge wurde früher von D. Jensen
an der Carnegie Mellon University (CMU) [CJR92] als distributed thread einge-
führt. Es wird im Kern des verteilten Echtzeit-Betriebssystems Alpha verwen-
det. Ein distributed thread ist ein leichgewichtiger Prozess, der sich über meh-
rere Adressräume erstreckt. In diesem Umfeld ist ein solcher Thread zusätz-
lich noch mit einer Menge von Parametern und Attributen assoziiert, die das
Scheduling- und Echtzeit-Verhalten beeinflussen. Er ist unabhängig von dem
Ort, wo er mit der Ausführung beginnt. Ein solcher verteilter Thread wur-
de im Verteilungskonzept beim JavaParty-System (siehe 4.1.1) RemoteThread
genannt und gilt dort als eine Aktivität. Um ihn vom üblichen Java-Thread
zu unterscheiden, möchte ich im Verlauf der Arbeit einen derartigen Thread
auch als eine Aktivität bezeichnen.
Rechner 1
Stub
Rechnergrenze
f()
Rechner 2
Skeleton
g()
Stub
Rechner 3
Skeleton
h()
Rechnergrenze
T1
T2T3
Aktivität
Kontrollfluss
legend:
T1..3 Java-Thread
Abbildung 2.7.: Aktivität und verteiltes Ausführungsmodell
Eine Aktivität besteht aus mehreren konventionellen Java-Threads, die sich
auf verschiedenen JVMs befinden und die eigentliche lokale Ausführung
der einzelnen Methodenaufrufe durchführen. Wie ein konventioneller Java-
Thread beginnt eine Aktivität mit der Ausführung der run()-Methode sei-
nen Ablauf und endet dann, wenn diese Methode vollständig abgearbeitet
wurde. Abbildung 2.7 veranschaulicht den Ablauf einer Aktivität (dicke Pfei-
le). Sie besteht aus drei konventionellen Ausführungssträngen T1. . . T3, die
jeweils die Methoden f(), g() und h() auf drei verschiedenen Rechnern
ausführen. Im Beispielcode wird die Methode g() von f() und h() von g()
aufgerufen. Die Aufrufe gehen aber tatsächlich über die Stubs und Skeletons.
Zu beachten ist, dass die vom RMI-System erzeugten Threads für den Emp-
fang der Methodenaufrufe (Skeleton-Threads) von den drei obigen Threads
T1. . . T3zu unterscheiden sind.
28
2.3. MULTITHREADED JAVA-PROGRAMME UND IHRE
AUSFÜHRUNGSMODELLE
Kontrollfluss einer Aktivität
Jeder lokale Thread besitzt eine eigene Ablaufstruktur, dargestellt als lokalen
Kontrollflussgraphen (CFG). Eine Aktivität hat auch ihre eigene Ablaufstruk-
tur. Da eine Aktivität aus mehreren verschiedenen lokalen Java-Threads be-
steht, erstreckt sich ihr Kontrollfluss über die Rechnergrenzen hinaus. Er um-
fasst also die lokalen Kontrollflüsse der Methoden, die von den zugehörigen
Ausführungssträngen lokal ausgeführt werden. Im Vergleich zu einem ge-
wöhnlichen CFG besitzt er Sonderkanten, die zwei verschiedene lokale Kon-
trollflussgraphen verbinden. Die eine Kante präsentiert den entfernten Auf-
ruf und die andere stellt die Rückgabe dar. Der Datenfluss bei diesen beiden
Kanten kann Parameter und Rückgabewert eines Methodenaufrufs enthal-
ten. Betrachten wir das Beispiel in Abbildung 2.7: Der Kontrollfluss der darin
dargestellten Aktivität umfasst die lokalen Kontrollflüsse der lokalen Threads
T1. . . T3. Das sind also die Kontrollflüsse der drei Methoden f(), g() und
h(), die sich von den drei lokalen Java-Threads ausführen lassen. Für die
Analyse solcher mehrsträngigen Java-Programme spielt dieser Aspekt eine
wichtige Rolle, worauf später im Kapitel 6 in Details eingegangen wird.
Identität einer Aktivität
Um die Synchronisation zwischen verschiedenen Aktivitäten und deren Ma-
nipulation gewährleisten zu können, muss jede Aktivität eine eindeutige
Identität besitzen, wie es beim konventionellen Thread der Fall ist. Wenn
eine Identitätsüberprüfung erforderlich ist, beispielsweise bei der Synchro-
nisation zweier verschiedener Aktivitäten über ein Monitor-Objekt, müssen
sich alle dazu gehörigen lokalen Threads mit dieser im ganzen System ein-
deutigen Identität erkennen lassen. Sonst wird die ursprüngliche Ausfüh-
rungssemantik nicht mehr erhalten bleiben. Es besteht sogar die Gefahr zur
Verklemmung und man hat keine Möglichkeit die Aktivitäten zu kontrol-
lieren, was aber bei normalen Java-Threads möglich ist. Dieses Konzept ist
nicht nur wichtig bei der Modellierung und Verteilungsanalyse von multi-
threaded Java-Programmen, sondern muss auch von der verteilten Laufzeit-
umgebung unterstützt werden. Die entstehende Problematik wurde ausführ-
lich in [WTV02a] und [HMRT03] diskutiert.
29
KAPITEL 2. JAVA UND SEIN MODELL ZUR VERTEILTEN AUSFÜHRUNG
Parallelität der Aktivitäten
Ein nebenläufiges Java-Programm besteht konzeptionell aus mehreren Akti-
vitäten, die die einzelnen Programmobjekte lokal oder entfernt auf eine durch
den Programmfluss festgelegte Weise berühren. Wenn zwei verschiedene Ak-
tivitäten die gleiche Methode eines Objekts im selben Adressraum ausführen,
wird dies verzahnt geschehen. Diese Verzahnung kommt zustande, weil zwei
normale Java-Threads, die jeweils zu einer der beiden Aktivitäten gehören,
auf dieser Maschine dieselbe Objektmethode ausführen.
Die Aktivitäten können aber auch parallel auf verschiedenen Rechnern ab-
laufen. Dies ist der Fall, wenn auf einem Rechner die eine Aktivität einen
entfernten Methodenaufruf ausführt und auf das Ergebnis wartet. Während
der Wartezeit kann eine andere Aktivität seine Ausführung beginnen bzw.
fortsetzen. So betrachtet werden die Aktivitäten im gesamten System parallel
ausgeführt.
Der Nachteil dieses Ausführungsmodell ist allerdings, dass der Wechsel ei-
ner Aktivität von einer Maschine zu einer anderen und zurück zusätzliche
Kommunikationskosten verursacht. Damit eine Geschwindigkeitssteigerung
durch die verteilte Ausführung erzielt werden kann, muss der Gewinnan-
teil an Parallelität größer sein als der Anteil an Kommunikationsaufwand. Es
gibt Anwendungsklassen, die sich durch eine gute Verteilung der Objekte auf
mehrere Rechner schneller ausführen lassen. Für Anwendungen, bei denen
erheblich mehr Kommunikation als Rechnen stattfindet, lohnt es sich nicht
zu verteilen.
Zusammenfassung
Die hier behandelten Aspekte stellen eine wichtige Grundlage zum Kon-
zept zur automatischen Verteilung der Objekte eines mehrsträngigen Java-
Programms dar. Diese geschilderten Aspekte spielen nicht nur eine wichtige
Rolle beim Entwurf des JScatter-Systems, sondern auch beim Einsatz sowie
bei der Entwicklung der Analyseverfahren zur Berechnung der Programmei-
genschaften, die relevant für die automatische Verteilung sind.
30
3. Grundüberlegungen zur
automatischen Verteilung
Inhalt
3.1. Manuelle Verteilung eines mehrsträngigen Java-Programms 33
3.1.1. Hand-Verteilung . . . . . . . . . . . . . . . . . . . . . . . . 33
3.1.2. Technische Umsetzung . . . . . . . . . . . . . . . . . . . . 35
3.2. Konzept zur automatischen Verteilung . . . . . . . . . . . . . . 37
3.2.1. Das Szenario . . . . . . . . . . . . . . . . . . . . . . . . . . 37
3.2.2. Verteilungsentscheidungen . . . . . . . . . . . . . . . . . . 39
3.2.3. Automatische Transformation . . . . . . . . . . . . . . . . 42
3.2.4. Programmanalyse . . . . . . . . . . . . . . . . . . . . . . . 43
31
KAPITEL 3. GRUNDÜBERLEGUNGEN ZUR AUTOMATISCHEN
VERTEILUNG
Nach dem im Abschnitt 2.3 vorgestellten Ausführungsmodell entsteht die
Rechenlast dort, wo eine Aktivität die aufgerufene Methode eines Objekts
ausführt. Zu einer entfernt aufgerufenen Methode tragen nicht nur die ver-
richteten Arbeiten zur Ausführung der Methode, sondern auch die teuren
Kommunikationskosten Rechnung. Konzeptionell sind in diesem Modell die
Aktivitäten die kleinsten Einheiten der Parallelität. Daher sollen zugehörige
Threads möglichst auf verschiedene Rechner verteilt werden, damit eine ech-
te parallele Ausführung erzielt werden kann. Der entscheidende Faktor ist
dennoch, die Objekte, mit denen die Aktivitäten in Berührung kommen, so
geschickt mit ihren zugehörigen Aktivitäten zu verteilten, dass so wenig
Kommunikationen wie möglich für die entfernten Methodenaufrufe entste-
hen.
Diesen Aspekt sollte ein Programmierer befolgen, wenn er sein mehrsträn-
gig formuliertes Programm manuell verteilen will. Zunächst sind bestimm-
te maßgeschneiderte Verteilungsstrategien für dieses Programm aufzustel-
len, die er später als Verteilungsdirektive in seinen Programmcode einbauen
wird. Für diese Strategien hat er davor alle benötigten Informationen über
das Programm zu sammeln. Das heißt, er hat das Programm gezielt nach die-
sem Aspekt analysiert und eventuell einige Abläufe des Programms auch be-
obachtet. Nun folgen „nur noch“ die technischen Schritte: Das Programm wird
mittels RMI in geeigneter Weise zerlegt, damit es wie gewünscht verteilt auf
verschiedenen Rechnern ablaufen kann.
Mein Ansatz ist es, all diese Schritte für jedes mehrsträngige Java-Programm
zu automatisieren und dennoch eine gewünschte Verteilung des Programms
zu erhalten, durch die eine Geschwindigkeitssteigerung erzielt wird. Als Aus-
gangspunkt dient ein als multithreaded formuliertes Java-Programm. Um
Verteilungshinweise für ein Programm automatisch zu berechnen ist der Ein-
satz der Programmanalyse erforderlich. Die Hauptidee ist es, dass zunächst
so viel Strategien wie möglich zur Verteilung so aufgestellt werden, damit
diese auf jedes Programm angewandt werden können. Zur Umsetzung der
Strategien braucht man bestimmte Programmeigenschaften, die sich durch Pro-
grammanalysen gewinnen lassen. Die für ein Programm ergebenen Vertei-
lungsentscheidungen können in geeigneter Weise im Programm angewandt
werden. Mit Hilfe von Werkzeugen kann das ursprüngliche Programm zu-
sammen mit den Verteilungshinweisen in eine verteilte Version transformiert
werden. Diese enthält die notwendigen RMI-Kommunikationsmechanismen
und kann auf einer verteilten Laufzeitumgebung ausgeführt werden. Die tat-
sächliche Verteilung der Objekte wird vom System automatisch durchgeführt.
32
3.1. MANUELLE VERTEILUNG EINES MEHRSTRÄNGIGEN
JAVA-PROGRAMMS
Dieses Kapitel beschreibt die Grundüberlegungen zur automatischen Ver-
teilung solcher mehrsträngig formulierten Java-Programme. Im ersten Ab-
schnitt werden die Schritte vorgestellt, nach denen ein Programmierer nor-
malerweise vorgeht, um sein Programm manuell auf mehrere Rechner ver-
teilt ausführen zu können. Auf diesen aufwändigen Schritten aufbauend wird
mein Ansatz zur Automatisierung vorgestellt. Dabei wird beschrieben, wel-
cher Schritt mit welchem Hilfsmittel automatisch realisiert werden kann. So-
mit wird dann motiviert, dass eine automatische Verteilung sehr hilfreich ist
und dem Anwender viele dieser Schritte abnehmen kann. Anschließend wer-
den einige Überlegungen dargestellt, die als Grundlage für das Konzept zur
automatischen Verteilung dienen. Einige Voraussetzungen für eine vollstän-
dige Automatisierung der Verteilung sowie allgemeine Kriterien werden ab-
schließend diskutiert.
3.1. Manuelle Verteilung eines mehrsträngigen
Java-Programms
Beim Konzept entfernter Methodenaufrufe mittels Remote Method Invocation
ist das Bestreben deutlich erkennbar, die durch entfernte Aufrufe verursachte
Netzwerkkommunikation vor dem Programmierer zu verbergen. Mittels RMI
wird dem Anwender die Semantik eines entfernten Objekts so dargestellt als
ob die eines entsprechenden lokalen Objekts wäre. So kann der Program-
mierer sich auf die Formulierung seines Programms konzentrieren und wird
von der Implementierung der entfernten Zugriffe auf verteilte Objekte nicht
mehr belastet. Trotzdem bleibt immerhin noch die Aufgabe des Programmie-
rers die Entscheidung zu treffen, welche Aktivitäten bzw. welche Objekte auf
welchen Rechner zu platzieren bzw. zu erzeugen sind. Darüber hinaus muss
die Synchronisation zwischen den Aktivitäten und deren Kommunikation ge-
währleistet werden. Diese Aufgaben sind nicht nur „auf Konzeptpapier“, al-
so durch Analyse des Eingabeprogramms, Treffen der Platzierungsentschei-
dungen usw., sondern auch in der technischen Realisierung zu erledigen.
3.1.1. Hand-Verteilung
Zunächst hat der Benutzer alle Platzierungsentscheidungen problemspezi-
fisch auf der Entwurfsebene zu treffen. Der erste Schritt ist zu entscheiden,
33
KAPITEL 3. GRUNDÜBERLEGUNGEN ZUR AUTOMATISCHEN
VERTEILUNG
welche Objekte zu verteilen sind und welche lokal zu lassen sind. Die lokal zu
erzeugenden Objekte sind beispielsweise diejenigen, die Hardware-nahe Ei-
genschaften besitzen und deshalb mit einer lokalen Maschine festgebunden
sind. Eine Platzierung solcher Objekte auf andere Maschinen macht keinen
Sinn. Kandidaten für lokale Objekte sind auch diejenigen, die als Hilfsobjekte
innerhalb anderer Objekte verwendet werden und deshalb nur lokal für um-
gebende Objekte unmittelbar zugreifbar sind. Für solche Objekte sind keine
entfernten Zugriffe erforderlich.
Es sind im Allgemeinen zwei verschiedene Arten von Objekten zu unterschei-
den: Thread-Objekte und Nicht-Thread-Objekte, im Folgenden nur als Objekte
bezeichnet. Methoden der Objekte, auf denen mehrere Aktivitäten auf ver-
schiedenen Rechnern Operationen durchführen, müssen über Rechnergren-
zen hinweg zugreifbar sein. Operationen können Berechnungen oder Syn-
chronisation sein.
Der zweite Schritt, auch der schwierigere Teil, ist festzulegen, welche Objekte
mit welchen zusammen zu gruppieren sind und wie sie auf welchen Ziel-
rechner zu platzieren sind. Im Prinzip sollen die Thread-Objekte möglichst
auf verschiedene Maschinen verteilt werden. Nachdem diese Verteilung fest-
gelegt wurde, erfolgt dann die Verteilung der Nicht-Thread-Objekte. Diese
Objekte sollen dorthin platziert werden, wo die meisten Aufrufe ihrer Metho-
den durchgeführt werden und dadurch viel Berechnungsaufwand entsteht.
Somit können viele zusätzliche entfernte Kommunikationen vermieden wer-
den.
Nach diesem Prinzip kann der Programmierer verschiedene Strategien auf-
stellen, die er zur Verteilung der Objekte seines Programms verwendet. Eini-
ge Strategien ergänzen sich gut, so dass eine gute Verteilung resultiert. Es gibt
aber auch Strategien, die sich nicht gut kombinieren lassen. Beispielsweise
besagt die eine Strategie, dass Objekte mit einer Aktivität zusammen verteilt
werden, weil diese Aktivität viel Arbeit auf ihnen verrichtet. Eine andere gibt
vor, dass diese Objekte mit einer anderen Aktivität zu platzieren sind, weil
sie viel mit ihnen kommuniziert. Wenn nicht zueinander passende Strategien
kombiniert werden, können unnötige Kommunikationskosten entstehen und
dies führt zu einer Geschwindigkeitsreduktion. Der Programmierer hat dann
zu entscheiden, welche Strategien mit welchen zu kombinieren sind, damit
eine Geschwindigkeitssteigerung durch die verteilte Ausführung erzielt wer-
den kann.
Je nach Verteilungsstrategien werden bestimmte Programmeigenschaften be-
nötigt. Das sind beispielsweise die Kommunikationsstruktur zwischen den
34
3.1. MANUELLE VERTEILUNG EINES MEHRSTRÄNGIGEN
JAVA-PROGRAMMS
Aktivitäten und Objekten, die Beziehung zwischen den Objekten und/oder
die (abgeschätzten) Ausführungskosten, die die Aktivitäten auf ihnen verur-
sachen. Um all diese Informationen zu beschaffen muss der Programmierer
sein Programm durch „scharfes Hingucken“ genau analysieren. So kann er die
Laufzeitstruktur einzelner Methoden, die Hierarchie der Klassen, die Benut-
zungsbeziehung durch Referenzierung oder Methodenaufrufe zwischen den
Objekten usw. bestimmen. Nach diesen Entwurfsschritten muss er das Ganze
noch technisch umsetzen.
3.1.2. Technische Umsetzung
Um das Programm mit den darauf anzuwendenden Verteilungsstrategien oh-
ne Hilfe eines Systems wie JScatter nur unter Verwendung von RMI verteilt
auf vielen Rechnern ausführen zu können, sind viele aufwändige technische
Schritte notwendig. Grob wird die Umsetzung in zwei Phasen aufgeteilt: das
Umprogrammieren des ursprünglichen Programms in eine verteilte Version
mit eincodierten Verteilungshinweisen unter Verwendung von RMI und das
verteilte Ausführen von diesem auf mehreren Rechnern.
Umschreiben des Programms
Beim Umprogrammieren des ursprünglichen Programms in eine verteilte
Version muss als Erstes festgelegt werden, wie die Objekte platziert werden.
Es gibt zwei Möglichkeiten dazu: Das Objekt wird sofort am gewünschten
Zielort erzeugt oder es wird zunächst lokal erzeugt, dann zu einem ande-
ren Zeitpunkt zum Zielort umgezogen. Der Umzug eines Objekts nach seiner
Erzeugung durch die Serialisierungstechnik, die Java-RMI anbietet, ist im-
mer teurer und aufwändiger als die erste Möglichkeit. Deshalb ist die Erzeu-
gung eines Objekts am Zielort vorzuziehen. Der Benutzer muss bei der For-
mulierung der Problemlösung dann die RMI-Konstrukte nach seiner getrof-
fenen Verteilungsentscheidung in sein Programm an passenden Stellen ein-
bauen bzw. das Programm entsprechend so umgestalten, dass es in verschie-
dene Teile aufgeteilt wird und somit diese Teile semantikerhaltend auf ver-
schiedenen Rechnern ablaufen können. Hierbei sind die semantischen Aspek-
te, die in 2.2.3 aufgelistet sind, zu berücksichtigen. Hinzu kommen noch
Kommunikations- und Synchronisationsaufgaben und Behandlungen in Feh-
lerfällen. Außerdem muss der Programmierer selbst für die Serialisierung /
35
KAPITEL 3. GRUNDÜBERLEGUNGEN ZUR AUTOMATISCHEN
VERTEILUNG
Deserialisierung der Parameter und Ergebnisrückgabe bei entfernt aufgeru-
fenen Methoden sorgen. Bei diesem Entwicklungsprozess ist vorausgesetzt,
dass der Programmierer neben der parallelen Programmierung in Java die
RMI-Technik schon perfekt beherrscht. Dieser Vorgang ist sehr fehleranfällig
und aufwändig.
Ausführen des verteilten Programms
Nach erfolgreicher Übersetzung des umgeschriebenen Programms und der
Generierung von Stubs und Skeletons mit Hilfe des im JDK enthaltenen Pro-
gramms rmic sind noch einige Schritte zur eigentlichen Ausführung des
als verteilbar vorbereiteten Programms von Hand durchzuführen. Das Pro-
gramm, die generierten Stubs und Skeletons und die benutzten Bibliotheken
müssen auf jedem beteiligten Rechner physikalisch vorhanden sein. Auf je-
dem beteiligten Rechnerknoten muss der Benutzer die RMI-Registrierung
starten, welche die Aufgabe der Verwaltung, des Bindens und der Identifizie-
rung der remote-Objekte auf diesem Rechner übernimmt. Nun müssen die Tei-
le, die aus dem ursprünglichen Programm durch das Umprogrammieren ent-
standen, auf einzelnen Rechnern so gestartet werden, dass es dem ursprüng-
lichen Ablauf entspricht. Wenn Fehler während der Ausführung auftreten,
die der Benutzer aber schon beim Programmieren mit Abfangmaßnahmen
berücksichtigt, werden sie durch RMI dann weitergeleitet und ausgegeben.
Es ist trotzdem sehr mühsam, die Fehlerursache zu finden, z.B. bei Laufzeit-
oder Kommunikationsfehlern, denn die Fehlermeldungen können auf ver-
schiedenen Rechnern verstreut sein, obwohl sie logisch zusammengehören.
Es ist also wünschenswert, all diese aufwändigen Schritte so weit wie mög-
lich zu automatisieren. Der Programmierer soll sich nur auf seine parallele
Problemlösung konzentrieren und sie anschließend in mehrsträngiger Form
in Java wie gewöhnlich formulieren. Zur Ausführung des Programms hat der
Benutzer nur noch die Aufgabe, die Rechnerknoten zu bestimmen, auf denen
das Programm verteilt laufen soll. Der Rest soll dann von selbst gehen. Wenn
dies gelingt, erreicht man die völlige Transparenz zwischen der nebenläufi-
gen Formulierung eines Programms und dessen verteilter Ausführung.
36
3.2. KONZEPT ZUR AUTOMATISCHEN VERTEILUNG
3.2. Konzept zur automatischen Verteilung
Basierend auf dem obigen Vorgehen sollen die durchzuführenden Schritte so
weit wie möglich automatisiert werden. Das Treffen der Entscheidung, ob
die aus dem Programm entstehenden Objekte zu verteilen sind und wie sie
gruppiert werden, bildet den Kernpunkt dieser Arbeit. Dafür sind eine einge-
hende automatische Analyse solcher nebenläufigen Programmcodes und dar-
aus abgeleitete Verteilungsaussagen notwendige Voraussetzungen. Nachdem
solche Verteilungsinformationen zur Verfügung stehen, kann das Programm
automatisch in eine verteilte Form transformiert werden, so dass Kommu-
nikationen über Rechnergrenzen hinweg mit RMI oder ähnlicher Techno-
logie erfolgt. Schließlich braucht man zur Ausführung des transformierten
Programms eine verteilte Laufzeitumgebung. Diese besondere Laufzeitumge-
bung enthält mehrere JVMs auf verschiedenen Rechnerknoten mit benötigten
Kommunikationsmechanismen. Sie ist in der Lage, das verteilte Programm
nebenläufig auszuführen und soll sich aus Benutzersicht transparent wie eine
normale JVM verhalten.
3.2.1. Das Szenario
Abbildung 3.1 stellt das gesamte Szenario von der Programmanalyse bis
zur verteilten Ausführung des transformierten Programms dar. Ausgangs-
punkt ist ein mehrsträngig formuliertes Java-Programm, das in Form von
Java-Bytecode vorliegt. Die Programmanalyse wird eingesetzt, um relevante
Eigenschaften des Eingabeprogramms für die automatische Transformation
und die automatische Verteilung zu bestimmen. Vor der Laufzeit kommen
statische Analyseverfahren zur Berechnung benötigter statischer Programm-
eigenschaften zum Einsatz. Einige von ihnen müssen der Transformations-
phase vollständig zur Verfügung stehen: Wenn beispielsweise alle Objekte
einer Klasse in der gesamten Anwendung nur lokal benutzt werden, dann
braucht diese Klasse nicht um einen Mechanismus für entfernte Zugriffe, al-
so RMI-Anbindung, erweitert zu werden. Beruhend auf diesen Informationen
wird anschließend eine verteilte Version des Programms durch eine automati-
sche Transformation generiert, die dann auf der verteilten Laufzeitumgebung
ausgeführt werden kann.
Zur automatischen Verteilung des Programms wird das Konzept Vertei-
lungsplan verwendet. Ein Verteilungsplan enthält u.a. allgemein formulier-
37
KAPITEL 3. GRUNDÜBERLEGUNGEN ZUR AUTOMATISCHEN
VERTEILUNG
Verteilte Laufzeitumgebung
while (...) {
Object o = new Object();
}O1 , ... , On
while (...) {
Object o = new RemoteObject();
}RO1 , ... , ROn
Programmanalyse
Verteilungsplan
JVM 1
automatische
Transformation
Mehrsträngiges
Java-Programm
Verteilbares
Programm
Vor der Laufzeit
Zur Laufzeit
RO1
T1
Plan
JVM 2
ROi
T2 ROj
JVM 3
ROn
T3
Programm-
eigenschaften
Plan Plan
Programmanalyse
automatische Verteilung
Abbildung 3.1.: Programmanalysen und verteilte Ausführung
te Verteilungsstrategien. Sie liefern die Entscheidung darüber, ob ein zu er-
zeugendes Objekt ein Remote-Objekt oder ein lokales ist und, im Fall ei-
nes Remote-Objektes, auf welcher JVM es erzeugt werden soll. Für bestimm-
te Verteilungsstrategien werden bestimmte Programmeigenschaften benötigt,
die vor der Laufzeit und zur Laufzeit von entsprechenden statischen und dynami-
schen Analyseverfahren beschafft werden. Die Strategien werden vor der Lauf-
zeit soweit wie möglich berechnet und direkt in das Programm in Form von
Anfragen an den Verteilungsplan eincodiert. Zur Laufzeit wird unmittelbar
vor einer Objekterzeugung eine Verteilungsentscheidung getroffen und das
Objekt wird entsprechend platziert.
Zur verteilten Ausführung des transformierten Programms wird eine verteilte
Laufzeitumgebung benutzt. Sie umfasst mehrere JVMs, die sich jeweils auf ei-
nem Rechner befinden. Während der Programmausführung werden die Ob-
jekte des transformierten Programms auf die beteiligten JVMs platziert. Die
38
3.2. KONZEPT ZUR AUTOMATISCHEN VERTEILUNG
Kommunikation zwischen den Objekten auf verschiedenen JVMs erfolgt über
RMI.
Während des Programmlaufs werden ebenfalls bestimmte Laufzeitdaten ge-
sammelt. Sie werden zum einen für einige dynamische Programmeigenschaf-
ten benötigt und füllen zum anderen einige Informationslücken, die vor der
Laufzeit durch statische Programmanalyse nicht vollständig bestimmt wer-
den konnten und deshalb noch offen gelassen werden. Basierend auf den zu-
sammengesetzten Informationen kann der Verteilungsplan verbesserte Ent-
scheidungen für zukünftig zu platzierende Objekte treffen.
3.2.2. Verteilungsentscheidungen
Aktivitäten greifen auf Objekte auf unterschiedlichen Maschinen zu. Bei ent-
fernten Methodenaufrufen werden hohe Kommunikationskosten verursacht:
die Bearbeitungszeit der Stubs und Skeletons für die Weiterleitung der Auf-
rufe, die Serialisierung der Parameter und Rückgabewerte und die eigentli-
che Übertragung der Bytestrom-Daten über das Netzwerk. Je mehr die Ob-
jekte entfernt miteinander kommunizieren, desto höher ist die Last auf dem
Netzwerk. Andererseits wird der Code der aufgerufenen Methode auf der
Maschine ausgeführt, auf der sich das diese Methode enthaltende Objekt be-
findet. Deshalb verursachen die Aktivitäten Ausführungskosten auf allen Ma-
schinen, die die zugegriffenen Programmobjekte enthalten. Also hängen die
Parallelität und die zusätzlichen Kommunikationskosten einer verteilten An-
wendung stark von der Platzierung der Objekte innerhalb der verteilten Lauf-
zeitumgebung ab.
Wie oben begründet sollte die Platzierung der Objekte während der Objek-
terzeugung durchgeführt werden. Daher muss die Verteilungsentscheidung
für jedes Objekt unmittelbar vor seiner Instanziierung getroffen werden, al-
so zur Ausführungszeit des verteilten Programms. Für verschiedene an ei-
ner Erzeugungsstelle zur Laufzeit entstehende Objekte können unterschied-
liche Verteilungsentscheidungen getroffen werden. Das hängt von der dort
auftretenden Programmeigenschaften ab, die wiederum vom Laufzeitverhal-
ten abhängig sind. Die zuerst getroffene Verteilungsentscheidung für Objekte
an einer Erzeugungsstelle heißt initiale Platzierungsentscheidung. Wann und in
welcher Reihenfolge die Entscheidungen für eine Objekterzeugungsstelle ge-
troffen werden, legen die Verteilungsstrategien fest. Verschiedene Strategien
werden in einem Verteilungsplan [Klo02] zusammengefasst.
39
KAPITEL 3. GRUNDÜBERLEGUNGEN ZUR AUTOMATISCHEN
VERTEILUNG
Konzeptionell werden die Verteilungsentscheidungen mittels Funktionen be-
rechnet, sogenannte Objektverteilungsfunktionen (Object Distribution Functi-
ons - ODF). Zur Transformationszeit liefern die Funktionen für jede Objekt-
erzeugungsstelle das Ergebnis, ob die hier entstehenden Objekte Remote-
Objekte sind und daher Transformation dafür notwendig wird. Zur Laufzeit
beschreibt das Ergebnis eines Funktionsaufrufs, auf welcher JVM das Ob-
jekt allokiert wird. Die ODFs hängen von statischen und dynamischen Ei-
genschaften des Programms ab, die ohne Benutzereingabe automatisch durch
Programmanalyse berechnet werden. Diese Eigenschaften werden in Form von
Variablen innerhalb der ODFs ausgedrückt, im Folgenden als Programmei-
genschaftsvariable (Program Property Variable - PPV) bezeichnet. Sie können
unterschiedliche Werte annehmen, die sich aus statischen und dynamischen
Analyseergebnissen ergeben. Durch dieses Konzept können Ergebnisse stati-
scher und dynamischer Analysen innerhalb der ODFs kombiniert verwendet
werden. Die Bindung statisch berechneter Werte zu den PPVs ermöglicht ei-
ne partielle Evaluation der ODFs, nachdem eine statische Programmanalyse
durchgeführt wurde. Diese Ergebnisse werden unmittelbar für die automati-
sche Transformation verwendet. Abbildung 3.2 verdeutlicht den Ansatz, dass
eine Verteilungsstrategie eine Zusammensetzung verschiedener ODFs dar-
stellt und welche Rolle die Programmanalyse hierbei spielt.
Die ODFs werden zur Laufzeit evaluiert, denn einerseits sind Werte einiger
Variablen, z.B. die Anzahl verfügbarer JVMs, erst nach dem Start der verteil-
ten Laufzeitumgebung bekannt. Andererseits werden bestimmte Laufzeitin-
formationen über das Programm, die keineswegs über statische Programm-
analyse zu bestimmen sind, benötigt, um Werte für dynamische Programm-
eigenschaften der ODFs zu berechnen. Zum Beispiel können nur grobe Ab-
schätzungen vor der Laufzeit darüber gemacht werden, wie oft ein bestimm-
ter Programmteil des Eingabeprogramms während der Laufzeit zur Ausfüh-
rung kommt. Bessere Ergebnisse lassen sich nur erzielen, indem man die sta-
tische Abschätzungen durch präzise Daten ersetzt, die während der Laufzeit
des Programms gewonnen werden können. Somit liefern die ODFs Entschei-
dungen für die initiale Verteilung und gegebenenfalls auch während des Pro-
grammablaufes verbesserte Entscheidungen zur Platzierung von zukünftig
zu erzeugenden Objekten bzw. Hinweise dazu, ob vorhandene Objekte mi-
griert oder repliziert werden sollen.
Die Güte einer Verteilungsentscheidung hängt von der Exaktheit der Analyse-
ergebnisse ab. Da die statischen Analysen vor der Laufzeit durchgeführt wer-
den, haben sie im Gegensatz zur dynamischen Analyse keinen nachteiligen
40
3.2. KONZEPT ZUR AUTOMATISCHEN VERTEILUNG
Abbildung 3.2.: Verwendung der Analyseergebnisse in Verteilungsstrate-
gien
Einfluss auf die Ausführungsgeschwindigkeit des Programms. Deshalb kön-
nen komplexere statische Analyseverfahren eingesetzt werden. Sie müssen
die Programmeigenschaften so genau wie möglich bestimmen, und ihre stati-
schen Ergebnisse werden soweit vorbereitet, dass zur Laufzeit nur minimaler
Aufwand an dynamischer Analyse entsteht. Denn nicht nur das Beschaffen
der Laufzeitinformation, sondern auch der Prozess zur Verbesserung der Plat-
zierungsentscheidungen, also die Kombination statischer und dynamischer
Analyseergebnisse und Neuberechnung der Verteilungsentscheidung, verur-
sachen zusätzliche Laufzeitkosten bei der Programmausführung.
In diesem Konzept wird die eigentliche Programmanalyse zur Berechnung
der Programmeigenschaften von der Formulierung der ODFs und somit
von den Verteilungsstrategien im Verteilungsplan voneinander getrennt. Der
Grund ist, dass die Objektverteilungsfunktionen im Prinzip nur die Werte
der Programmeigenschaften benutzen, aber nicht davon abhängen, wie sie
berechnet werden. Außerdem ermöglicht das verwendete Bindungskonzept
den Einsatz verschiedener Werte zu verschiedenen Zeitpunkten. Dadurch
können statische und dynamische Analysen kombiniert zum Einsatz kom-
men. Im Kapitel 5 werde ich ausführlich auf das hier vorgestellte Konzept des
41
KAPITEL 3. GRUNDÜBERLEGUNGEN ZUR AUTOMATISCHEN
VERTEILUNG
Verteilungsplans sowie die einzelnen Verteilungsstrategien mit ihren ODFs
eingehen.
3.2.3. Automatische Transformation
Die Aufgabe, welche während der Transformationsphase erledigt wird, be-
steht darin, den Programmcode so zu modifizieren, dass zur Laufzeit die Ob-
jekte nach den Verteilungsdirektiven aus dem Verteilungsplan auf den JVMs
der Laufzeitumgebung instanziiert werden. Außerdem muss gewährleistet
werden, dass entfernte Zugriffe aus anderen JVMs auch erfolgen können und
die Ausführungssemantik des ursprünglichen Programms erhalten bleibt.
Während der Transformationsphase wird jede Erzeugungsstelle wie folgt be-
trachtet: Für diese Stelle wird eine Planevaluierung durchgeführt. Wenn der
Plan das Ergebnis liefert, dass die Objekte lokal zu erzeugen sind und auf
sie keine entfernten Zugriffe in der gesamten Anwendung benötigt werden,
dann bleibt der Programmcode an dieser Stelle unverändert. Anderenfalls
wird die Stelle entsprechend so transformiert, dass die dort zur Laufzeit ent-
stehenden Objekte auf einer bestimmten JVM erzeugt werden können und
eine Anfrage an den Verteilungsplan für die Platzierung angebunden wird.
Wenn der Plan zur Laufzeit angefragt wird, werden bestimmte Strategien an-
gewandt. Entsprechend der getroffenen Entscheidung wird das Objekt auf ei-
ner der JVMs erzeugt. Außerdem werden alle Zugriffsstellen auf ein Remote-
Objekt auch so entsprechend transformiert, dass die Semantik im verteilten
Fall erhalten bleibt.
Bei diesem Transformationskonzept wird die Objektsemantik berücksichtigt:
Eine urspüngliche Klasse wird in zwei Teile aufgeteilt. Der eine stellt den sta-
tischen Anteil der Klasse dar, der andere den Instanzanteil. Aus dem Instan-
zanteil können mehrere Remote-Objekte entstehen. Jedes von ihnen hat eine
eindeutige Objekt-Identität im gesamten verteilten System und kann über ei-
ne Referenz entfernt zugegriffen werden. Dafür wird das Handle-Konzept ver-
wendet. Es gibt aber nur einen einzigen statischen Anteil einer Klasse wäh-
rend der verteilten Ausführung. Es wird so transformiert, dass Zugriffe auf
den statischen Anteil über Rechnergrenzen hinweg erfolgen können.
Zur Migration und Replikation von Objekten während der Programmaus-
führung müssen einige technische Vorbereitungen während der Transforma-
tionsphase erledigt werden. Die Klassen, deren Objekte migriert bzw. repli-
ziert werden können, müssen entsprechend transformiert werden. Die Ent-
42
3.2. KONZEPT ZUR AUTOMATISCHEN VERTEILUNG
scheidung dafür, welche Klassen dazu zu transformieren sind, wird vom Ver-
teilungsplan vor der Laufzeit getroffen. Außerdem basieren einige Entschei-
dungen zur Migration bzw. Replikation auf Laufzeitdaten, deshalb werden
an passenden Stellen Instrumentierungscode eintransformiert.
Damit das transformierte Programm hinterher verteilt ausgeführt werden
kann, wird nicht nur die Anwendung transformiert, sondern auch die inner-
halb dieser Anwendung potentiell benutzten Klassen aus Bibliotheken ein-
schließlich der Java-Standard-Bibliothek. Diese Klassen bilden zusammen mit
der Anwendung eine so genannte transitive Hülle. Sie wird auf einen mini-
malen Umfang so beschränkt, dass nur die erforderlichen Komponenten zur
Ausführung des Programms darin enthalten sind. Die gesamte Transformati-
on des Eingabeprogramms inklusive der verwendeten Bibliotheken soll aus
der Sicht des Benutzers völlig transparent ablaufen.
Da die Transformation und die verteilte Laufzeitumgebung eng aufeinander
abgestimmt sind, müssen Entwurfsentscheidungen für beide gleichzeitig ge-
troffen werden. Details über Entwurfskriterien des Transformators und der
verteilten Laufzeitumgebung finden sich im Kapitel 7.
3.2.4. Programmanalyse
Bei nebenläufigen Java-Programmen ergeben sich mehr Anforderungen an
die Programmanalyse als bei sequentiellen Programmen. Das Problem be-
steht darin, dass die Aktivitäten im Programm konzeptionell aus verschie-
denen Threads auf unterschiedlichen JVMs bestehen. Der Kontrollflussgraph
einer Aktivität erstreckt sich somit über mehrere JVMs und enthält mehre-
re lokale CF-Graphen der Methoden, die von diesen verschiedenen Threads
ausgeführt werden. Eine Datenflussanalyse für solche Aktivitäten muss den
gesamten CFG einer Aktivität in Betracht ziehen. Außerdem ist der Kontroll-
fluss bei den Wechselwirkungen zwischen verschiedenen Aktivitäten nicht
mehr so beschränkt wie im sequentiellen Fall. Während im sequentiellen Fall
allenfalls durch bedingte Verzweigungen oder die Auflösung dynamischer
Methodenbindung eine Auswahl an möglichen Nachfolgern einer bestimm-
ten Instruktion entsteht, kommt im mehrsträngigen Fall jede Instruktion ei-
ner nebenläufigen Aktivität als Nachfolger in Frage. Eine einfache Übertra-
gung normaler Analysen würde inkorrekte Informationen liefern. Deshalb
muss die Programmanalyse in diesem Kontext in der Lage sein, die mög-
lichen Interaktionen zwischen den Aktivitäten und Objekten einzuschrän-
43
KAPITEL 3. GRUNDÜBERLEGUNGEN ZUR AUTOMATISCHEN
VERTEILUNG
ken. Es gibt verschiedene Ansätze zur statischen Programmanalyse in die-
sem Umfeld, z.B. die Objektgraphanalyse, Untersuchung der Wechselwirkun-
gen zwischen den Aktivitäten, Erweiterung der Kontrollflussanalyse oder die
Seiteneffekt- und Referenzzielanalyse usw. All diese Verfahren können einge-
setzt werden, um gezielt Programmeigenschaften zwecks Verteilung zu be-
rechnen. Auf Einzelheiten werde ich im Kapitel 6 eingehen.
Die Analyseergebnisse werden, wie in Abbildung 3.2 gezeigt wird, in den
Verteilungsstrategien verwendet, nämlich in den Objektverteilungsfunktion-
en. Jede ODF ist charakterisiert durch bestimmte Programmeigenschaften, die
sich durch Einsatz passender Analyseverfahren automatisch berechnen las-
sen. Beispielsweise kann die Proxy-Eigenschaft durch ein statisches Analyse-
verfahren, die Escape-Analyse für mehrsträngige Java-Programme, bestimmt
werden. So kann die Funktion ODFP roxy ihre Entscheidung für Objekte tref-
fen, die nur lokal innerhalb anderer Objekte benutzt werden und deshalb kei-
ne entfernten Zugriffe braucht. Sie werden also nicht verteilt sondern nur dort
erzeugt, wo die umgebenden Objekte platziert werden.
Zusammenfassung
Zur Berechnung der Programmeigenschaften haben die Programmanalysen
relevante Informationen zu liefern. Die Güte der Verteilungsentscheidung
hängt sehr stark von den berechneten Daten ab. Mächtige und dafür auf-
wändige statische Analyseverfahren können hierbei eingesetzt werden, da
sie vor der eigentlichen Durchführung des Programms angewendet werden
und ihre Ergebnisse unabhängig von einzelnen Programmabläufen sind. Nur
statisch berechnete Ergebnisse allein reichen aber nicht für eine gute Vertei-
lung der Objekte aus, weil sich nicht nur der Kontrollfluss eines Programms
und dessen Eingabedaten, sondern auch andere Faktoren wie Rechnerlast
oder Kommunikationskosten zur Laufzeit ändern können. Deshalb kann ei-
ne dynamische Analyse fehlende Information, bespielsweise über bestimm-
ten Häufigkeiten, dazu ergänzen. Reales Laufzeitverhalten und wertvolle
Laufzeitdaten lassen sich bei der statischen Analyse nicht herausfinden oder
höchstens nur approximieren. Daten, die mit statischen Ergebnissen zu kom-
binieren sind, bilden die wesentliche Grundlage für die zu treffende Vertei-
lungsentscheidung für zukünftig erzeugte Objekte und für die Replikation
und Migration von Objekten.
44
3.2. KONZEPT ZUR AUTOMATISCHEN VERTEILUNG
Auf dieser Grundlage entsteht das Gesamtkonzept zur automatischen Ver-
teilung nebenläufiger Java-Programme. Die eingesetzen Analysetechniken zu
diesem Ansatz werden genau im Kapitel 6 beschrieben.
45
.
4. Verwandte Arbeiten
Inhalt
4.1. Existierende Systeme, Frameworks und Bibliotheken . . . . 48
4.1.1. Systeme mit Sprachänderung . . . . . . . . . . . . . . . . 50
4.1.2. Systeme und Bibliotheken mit Semantik-Änderung . . . 52
4.1.3. Systeme mit statisch basierter Verteilungsentscheidung 55
4.1.4. Systeme mit dynamisch basiertem Ansatz . . . . . . . . 56
4.1.5. Andere Systeme . . . . . . . . . . . . . . . . . . . . . . . . 58
4.1.6. Systeme für High-Performance Java - Datenparallel . . 59
4.2. Kommunikationsmodelle und -techniken . . . . . . . . . . . . 61
4.2.1. Sun-RMI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
4.2.2. KaRMI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
4.2.3. Distributed Threads . . . . . . . . . . . . . . . . . . . . . . 62
4.2.4. JavaSpaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
4.2.5. Andere Techniken . . . . . . . . . . . . . . . . . . . . . . . 63
4.3. Vergleiche - Überblicke . . . . . . . . . . . . . . . . . . . . . . . 64
47
KAPITEL 4. VERWANDTE ARBEITEN
In diesem Kapitel werden Systeme und Bibliotheken beschrieben, die der-
zeit existieren und zur Verteilung von Java-Programmen eingesetzt werden.
Die Systeme und Bibliotheken werden nach folgenden Aspekten kategori-
siert: Änderung an Sprache und/oder Programmsemantik; getroffene Ent-
scheidungen zur Verteilung basiert auf statischen oder dynamischen Ansät-
zen; High-Performance Java mit regulären Ansätzen. Die Systeme werden mit
JScatter auf konzeptioneller Ebene verglichen. Im Verlauf der Arbeit, vor al-
lem in den Kapiteln 5, 7 und 8 wird dann auf die hier beschriebenen Charak-
teristiken zurückgegriffen, um detaillierte Vergleiche anzustellen. Anschlie-
ßend werden die Kommunikationsmodelle und -techniken, die meist als un-
tere Schicht der Systeme und Bibliotheken verwendet werden, kurz angege-
ben. Abschließend gibt eine Tabelle einen Überblick über alle wesentlichen
Merkmale der hier beschriebenen Systeme und Bibliotheken.
Verwandte Arbeiten zu Analysetechniken, die zur Berechnung der Pro-
grammeigenschaften zur automatischen Verteilung eingesetzt werden, wer-
den im Kapitel 6 extra beschrieben.
4.1. Existierende Systeme, Frameworks und
Bibliotheken
Die grundlegenden Arbeiten zu diesem Thema entstanden ab 1997, als Phil-
ippsen das JavaParty-System (Abschnitt 4.1.1) der Welt bekannt machte. Seit-
dem verbreitet sich dieses Thema immer weiter und es existieren derzeit vie-
le Systeme bzw. Bibliotheken aus der ganzen Welt, die sich mit dem Ansatz
„automatische Verteilung von Java-Programmen“ beschäftigen. Man bemüht
sich ständig, Systeme bzw. Werkzeuge zu schaffen, die dem Programmierer
die Verteilung mehrsträngig formulierter Java-Programme abnehmen, so dass
sie keine speziellen Verteilungskenntnisse brauchen. Bei allen bekannten Sys-
temen oder Bibliotheken muss der Programmierer bisher Verteilungsdirek-
tiven selbst angeben. Der Programmcode wird dann entsprechend transfor-
miert und mit Hilfe von Werkzeugen oder eigenen Laufzeitumgebungen ver-
teilt ausgeführt. Es wird dabei berücksichtigt, dass das transformierte End-
produkt semantikerhaltend bleibt.
Zunächst wird erklärt, was man unter Änderung der Sprache und der Se-
mantik genau bei der Verteilung von Java-Programmen versteht und was sta-
tisch oder dynamisch basierte Verteilungsentscheidungen bei den Systemen
bedeuten.
48
4.1. EXISTIERENDE SYSTEME, FRAMEWORKS UND BIBLIOTHEKEN
Änderung an der Sprache Es werden neue Sprachkonstrukte in die Java
Sprache eingefügt, damit der Programmierer Verteilungsdirektiven explizit
angeben kann. Dafür wird ein eigener Compiler benötigt, der diese Erweite-
rung erkennt und das Programm dementsprechend so transformiert, dass es
nach diesen Verteilungshinweisen korrekt verteilt ausgeführt wird. Vertreter
hierfür sind das JavaParty-System und ein System namens Remote Objects in
Java (siehe Abschnitt 4.1.1).
Änderung an der Semantik Verteilungsdirektiven werden meist vom Pro-
grammierer explizit in Form von Bibliothek-Methodenaufrufen (ProActive im
Abschnitt 4.1.2), spezielle Tags/Kommentare (Doorastha im Abschnitt 4.1.2),
Graphpartition auf grafischer Darstellung (Pangaea im Abschnitt 4.1.2) oder
direkt in Quellcode durch Spracherweiterung (remote bei JavaParty) angege-
ben. Das ursprüngliche Programm wird entsprechend so transformiert, dass
Fernzugriffe auf gekennzeichnete Objekte möglich sind, z.B. Einbindung von
RMI-Code, Generierung von Stubs, Skeletons usw. Das Originalprogramm
bleibt nach der Transformation bzw. Übersetzung nicht mehr dasselbe, son-
dern es entstehen daraus eventuell mehrere Programme. Diese können dann
in einer geeigneten Laufzeitumgebung ausgeführt werden. Wenn beobacht-
bare Effekte geändert werden, so dass sie nicht mehr den Beschreibungen
der ursprünglichen Sprachdefinition genügen, dann wird das Ursprungpro-
gramm semantisch geändert. Das folgende Beispiel soll dies verdeutlichen:
Im Originalcode sollen zwei Objektreferenzen miteinander verglichen wer-
den (entspricht dem Operator ==). Durch eine Verteilungsstrategie gelan-
gen die beiden referenzierten Objekte auf unterschiedliche Rechner. Nach der
Transformation entspricht das Vergleichergebnis nicht dem ursprünglichen
Fall. Dann entspricht dies nicht mehr der Semantik des Programms.
Statisch basierte Verteilungsentscheidung Vor der Laufzeit werden sta-
tische Analysen eingesetzt, um Programmeigenschaften zu berechnen, die re-
levant für die Transformation und Verteilung sind. Aus den Analyseergeb-
nissen werden Verteilungsentscheidungen getroffen, die aber dann fest in das
Programm integriert werden und zur Ausführungszeit unverändert bleiben.
Die meisten Systeme benutzen diesen Ansatz und wenden Analysen während
oder nach der Übersetzung des Programms, aber vor der ersten Ausführung
an.
49
KAPITEL 4. VERWANDTE ARBEITEN
Dynamisch basierte Verteilungsentscheidung Im Gegensatz zum sta-
tisch basierten Ansatz wird das Programm vor der Laufzeit nicht analysiert,
sondern es wird an bestimmten Programmstellen Instrumentierungscode ein-
gefügt. Zur Laufzeit werden an solchen Stellen Laufzeitdaten nach bestimm-
ten Kriterien gesammelt, beispielsweise die Anzahl der Zugriffe zwischen
Objekten. Bei vielen Ansätzen wird eine spezielle JVM verwendet, die in der
Lage ist, die Last durch Zugriffe auf Objekte und die aktuelle Last der verwen-
deten Rechner zu berechnen. Diese Daten dienen dann als Grundlage für die
Platzierungsentscheidung der Objekte (Juggle-System in 4.1.4, cJVM [AFT99],
[AFT+00]).
Nachfolgenden möchte ich basierend auf den obigen geschilderten Kategori-
en die einzelnen Systeme und Bibliotheken beschreiben.
4.1.1. Systeme mit Sprachänderung
JavaParty
Dies ist eins der ersten Systeme zur automatischen Verteilung von Java-
Programmen. Hier wurde Java um einen neuen, die Klassendeklarati-
on ergänzenden, Klassenmodifikator remote erweitert. Der Programmie-
rer kennzeichnet damit potentiell entfernt zu realisierende Objekte für den
JavaParty-Übersetzer. Der Übersetzer und das verteilte Laufzeitsystem über-
nehmen die Transformation und Verteilung des Quellprogramms und liefern
Kommunikations- und Synchronisationsprimitive für den Zugriff auf verteil-
te Objekte. Eine erweiterte Typanalyse wurde hierbei zur Übersetzungszeit
verwendet, um eine möglichst genaue statische Approximation an den inter-
prozeduralen Kontrollfluss des Programms zu erzielen. Durch Untersuchung
des Aufrufgraphen der vorkommenden Aktivitäten im Programm ergibt sich
eine Zuordnung von Objekten zu den Aktivitäten. Diese Zuordnung beruht
auf Schätzungen von Aufrufhäufigkeiten, Komplexitäten von Methoden und
Strukturgrößen. Ein wesentliches Merkmal des verwendeten Konzepts ist die
Aufteilung von polymorpher Verwendung der Klassen und Methoden. Somit
werden auch die Daten, die in den Klassen gekapselt sind und in Form von
gespeicherten Objekten von unterschiedlichen Typen in Instanzvariablen vor-
liegen, und Methodenparameter in monomorphe Teile aufgeteilt. Diese wer-
den als Konturen oder Schablonen bezeichnet. Dadurch werden die verwen-
deten Typen eindeutig und das erleichtert die Verteilungsanalyse zur Opti-
mierung der Platzierung von Objekten ([PZ97], [HRP], [BCP96], [PH00]).
50
4.1. EXISTIERENDE SYSTEME, FRAMEWORKS UND BIBLIOTHEKEN
Konzeptionell werden aus einer remote Klasse zehn Klassendateien generiert,
die den statischen Instanz-Anteil und eine lokale Version mit zugehörigen
Schnittstellen darstellen. Dadurch existiert eine neue, generierte Klassenhier-
archie neben der ursprünglichen. Das Konzept Objekt-Handles [Hau98] wurde
bei JavaParty benutzt, um u.a. Zugriffprobleme in der neuen Klassenhierar-
chie zu lösen.
Basierend auf diesen Kenntnissen gibt es im JScatter-System auch eine Auf-
teilung einer Klasse, deren Objekte entfernt zugreifbar sind, in sechs Teile: In-
stanzenanteil (Cuckoo-Klasse), Instanzenverwalter, statischer Anteil und das
Handle plus zwei zugehörige Schnittstellen. Mehr dazu wird im Abschnitt 7
beschrieben. Dadurch haben wir wesentlich weniger Teile zu generieren.
Als Kommunikationstechnik wurde Sun-RMI oder die hauseigene RMI
(KaRMI [HP99], im Abschnitt 4.2.2) im JavaParty-Laufzeitsystem verwendet.
Diese optimierte RMI-Version ist um einen Faktor 12 schneller gegenüber
Sun-RMI. Trotz der neuen entstehenden Klassenhierarchie und der neu ge-
nerierten Teile bleibt aber das Verhalten der verteilten Ausführung des Pro-
gramms unverändert. Der Übersetzer beim JavaParty-System sorgt während
der Transformation dafür, dass die Semantik des Programms erhalten bleibt.
Remote Objects in Java
Nataraj Nagaratnam und Arvind Srinivasan haben ein verteiltes Java-System
implementiert, genannte Remote Objects in Java [NS96]. Sie erweiterten die
Sprache Java um ein weiteres Schlüsselwort remotenew. Dies ist anstelle von
new zu benutzen, um ein Objekt auf einer entfernten virtuellen Maschine zu
erzeugen. Entfernte Methoden können aber mit der üblichen Java-Syntax auf-
gerufen werden. Sie werden wie beim Java-RMI auf der entfernten Maschine
ausgeführt1. Objekt-Migration wird nicht unterstützt, d.h. ein remote object ist
fest an die Maschine gebunden, auf der es instantiiert wurde. Es besitzt zwei
Identitäten, eine auf der lokalen Maschine und eine weitere auf der Heimat-
maschine, aus der der Erzeugungscode stammt. Eine Abbildungstabelle der
lokalen Identität auf die entfernte Identität befindet sich auf der lokalen Ma-
schine. Über diese kann entschieden werden, ob ein Objekt entfernt oder lokal
ist. Es gibt zu einer Klasse sowohl lokale als auch entfernte Objekte.
Unser Ansatz ist zu Remote Objects in Java in diesem Punkt ähnlich, d.h. so-
wohl entfernte als auch lokale Objekte können aus einer Klasse stammen. Das
1Sun-RMI wird nicht bei Remote Objects in Java eingesetzt.
51
KAPITEL 4. VERWANDTE ARBEITEN
stellt in diesem System aber ein großes Problem dar, das erst zur Laufzeit be-
kannt ist: Abhängig davon, ob ein Objekt remote oder lokal ist, kann dann
erst entschieden werden, ob ein Methodenaufruf mit Parametern vom Refe-
renztyp bezüglich eines Objekts überhaupt möglich ist. Dieses Problem ha-
ben wir elegant durch den Delegationsmechanismus mittels Objekt-Handles
(siehe Kapitel 7) gelöst. Im Falle eines existierenden lokalen Objekts werden
lediglich lokale Aufrufe ausgeführt und nicht über den umständlichen RMI-
Mechanismus.
Charakteristisch für Remote Objects in Java ist auch das Prinzip, dass der Byte-
code sämtlicher Klassen anfangs lediglich auf der lokalen virtuellen Maschine
bekannt ist. Eine entfernte virtuelle Maschine wartet nach der Initialisierung
lediglich auf Anfragen anderer Maschinen. Zur entfernten Ausführung eines
Konstruktors bzw. einer Methode muss dann stets der betreffende Bytecode
mitgeschickt werden. Dies bringt aber erhebliche Probleme mit sich, da eine
gesendete Methode Parameter zu lokalen Variablen auf der einen Maschine
enthalten kann und diese Variablen auf der anderen Maschine nicht existieren
oder andere Werte besitzen. Außerdem entsteht durch das Hin- und Zurück-
schicken sehr viel Kommunikationsaufwand.
4.1.2. Systeme und Bibliotheken mit Semantik-Änderung
Pangaea
Pangaea ist ein System zur (halb)automatischen Verteilung von Java-
Programmen. Zunächst wird der Quelltext analysiert und ein sogenannter
Objektgraph wird dann aufgebaut. Ein Objektgraph ist eine Annäherung der
Laufzeitstruktur des Programms und beschreibt, welche Objekte es zur Lauf-
zeit geben wird und wie sie miteinander kommunizieren [Spi99b]. Mit Vorga-
ben und Rahmenbedingungen vom Programmierer und in Abhängigkeit von
der zu verwendeten Verteilungsplattform entscheidet das System über die
Verteilung des Programms durch Objektgraphanalyse. Die Festlegung durch
den Programmierer geschieht in visueller Form, indem er in einer grafischen
Darstellung des Objektgraphen die Objekte fest bestimmten Rechnern zuord-
net. Abhängig von der unterstützten Verteilungsplattformen, hier CORBA,
JavaParty und Sun-RMI wird vom Pangaea entsprechender Quellcode dafür
generiert. Wenn diese Plattformen Objektmigration oder -replikation unter-
stützen, erzeugt Pangaea dann auch Code dafür [Spi99c], [BLS97], [Spi99a].
Das System ist vor allem geeignet für Client-Server-artige Anwendungen.
52
4.1. EXISTIERENDE SYSTEME, FRAMEWORKS UND BIBLIOTHEKEN
Bei Pangaea werden beobachtbare Effekte des auszuführenden Programms
verändert. Es gibt auch Einschränkungen des Systems, dass die Objektidenti-
täten nicht im Typgraphen gespeichert werden. Diese Information ist vor al-
lem wichtig für Vergleiche von Objektreferenzen oder für Aufruf dynamisch
gebundener Methoden. Im Allgemeinen bietet die Objektgraphanalyse Mög-
lichkeiten, viel statische Eigenschaften des Programms vor der Laufzeit be-
rechnen zu können. Daher benutzen wir sie auch, um Objektbeziehungen im
Programm für Kommunikationsabschätzungen zu bestimmen.
Doorastha
Hierbei handelt es sich um ein System zur Verteilung von Java-Programmen.
Es erlaubt entfernte Zugriffe, die dem Programmierer transparent bleiben.
Das System bietet zwei verschiedene Möglichkeiten für Fernzugriffe auf Ob-
jekte und eine Mischform aus beiden. Durch das Hinzufügen von Tags, die ge-
sonderte Kommentare für Fernzugriffe oder Verteilungsdirektive kennzeich-
nen, wird ein normales Java-Programm in ein verteiltes transformiert. Die
Vererbungshierarchie bleibt dabei erhalten. Wie JavaParty baut es auf RMI
auf und benutzt nur den Serialisierungsmechanismus. Zur Notation von Ver-
teilungsanweisungen gibt es spezielle Tags (remotenew und Rechnername),
die direkt an der Erzeugungsstelle nach new als gesonderte Kommentare ein-
zufügen sind [Dah00], [Dahb].
Besonders zu erwähnen ist im Doorastha-System das Konzept Proxy und
Wrapper. Die Wrappers nehmen die entfernten Zugriffe entgegen und leiten sie
weiter an das lokale Objekt. Die Proxies sind Bevollmächtigte auf der entfern-
ten Seite. Durch diesen Mechanismus werden entfernte Zugriffe über Refe-
renzen einfach weiter delegiert. Über das Netzwerk werden dann nur solche
Wrappers verschickt, nicht die tatsächlichen Objekte. Während der Transfor-
mationsphase wurde auch berücksichtigt, dass trotz Delegation die Semantik
der Referenzen und des Programms auch erhalten bleibt. Diesen Vorteil haben
wir ausgenutzt und vereinigten Proxy und Wrapper in einem einzigen Objekt-
Handle. Im JScatter-System leiten die Handles auf der Client-Seite die Auf-
rufe an Instanzmanager weiter. Diese verwalten alle Objekte einer Klasse auf
einem Rechner und delegieren die Aufrufe auch an sie (siehe Kapitel 7). Au-
ßerdem unterstützt das Doorastha-System Objektmigration und -replikation,
53
KAPITEL 4. VERWANDTE ARBEITEN
welche bei JScatter nach dem Konzept in der Diplomarbeit von Weißenborn
[Wei03] realisiert wurden.
ProActive
ProActive ist eine Java-Bibliothek, mit der ein Java-Programm in ein verteil-
tes umgewandelt werden kann. Ein wesentliches Merkmal von ProActive
sind aktive Objekte, die als Verteilungseinheiten fungieren. Die Objekte des
Programms werden dann in sogenannte Subsysteme strukturiert. Ein sol-
ches System besteht aus einem aktiven Objekt, das die Kommunikation mit
anderen Subsystemen übernimmt, und einer beliebigen Anzahl von norma-
len (passiven) Objekten. Methoden solcher passiver Objekte werden nur in-
nerhalb ihres Subsystems ausgeführt. Die Subsysteme sind voneinander dis-
junkt. Um die zu erzeugenden Objekte als aktive Objekte zu kennzeichnen
oder passive Objekte in aktive umzuschalten, muss der Programmierer auf
Quellcode-Ebene Methodenaufrufe der ProActive-Bibliothek an den zugehö-
rigen Erzeugungsstellen einfügen. Das sind auch die Verteilungshinweise für
solche Objekte.
Erwähnenswert bei ProActive ist die Realisierung von asynchronen Metho-
denaufrufen zwischen den Objekten durch sogenannte Future-Objekte. Der
Aufrufer kann seine Aufgaben weiter ausführen, während die „Antwort“ ab-
gearbeitet wird. Als weitere Eigenschaften bietet das System Objektmigration,
die Wiederverwendung durch erhaltene Polymorphie zwischen aktiven und
passiven Objekten und die Kompatibilität mit grafischen Komponenten wie
Swing und AWT. Wie die meisten anderen Systeme basiert ProActive auf der
Standard Kommunikationstechnik RMI von Sun ([Ant], [CKV98], [ACG00]).
Durch die Umwandlung in aktive Objekte sind Java-Befehle nicht mehr er-
laubt, wie zum Beispiel Referenzvergleiche von aktiven Objekten.
Do!
Do! ist ein Framework von Institute Irisa. Es bietet ein paralleles Program-
miermodell für mehrsträngige Java-Programme. Ähnlich wie ProActive hat
der Benutzer Methodenaufrufe aus der Bibliothek an Erzeugungsstellen ein-
zusetzen, um die zu erzeugenden Objekte als remote zu kennzeichnen. Metho-
den solcher Objekte sind dann entfernt aufrufbar. Zur parallelen Programmie-
rung von Programmen mit strukturierten Threads und Verteilung der Objekte
54
4.1. EXISTIERENDE SYSTEME, FRAMEWORKS UND BIBLIOTHEKEN
bietet Do! so genannte „verteilte Collections“. Threads innerhalb einer sol-
chen Collection können parallel ausgeführt werden. Dafür ist ein Sprachkon-
strukt PAR anzuwenden. Der Preprozessor transformiert das Programm dann
in eine verteilbare Version mit eingebundenem Code aus dem Framework.
Zu verteilende (aktive) Objekte müssen in solche Collections hinzugefügt
werden. Für die Verteilung der Elemente in einer „Collection“ ist ein LAY-
OUT_MANAGER im Do!-System zuständig. Damit kann der Programmierer
seine Verteilungsstrategie beschreiben oder die vorhandene Blockzyklus- und
Zyklus-Verteilung benutzen. Ähnlich wie bei ProActive wird die Semantik
der Referenzen über aktive Objekte verändert ([LP98], [LP97]).
4.1.3. Systeme mit statisch basierter Verteilungsentscheidung
In den vorgestellen Systemen und Bibliotheken hat der Programmierer
Transformations- oder Verteilungshinweise explizit anzugeben. Zum größten
Teil werden hierbei Ergebnisse statischer Programmanalysen benutzt.
In JavaParty wurde die Standard-Typinferenz [PS91] erweitert [PH00], um
die polymorphe Verwendung der Klassen und Methoden sowie zugehörige
Klassendaten und Parameter in monomorphe Teile zu überführen. Dies ist
notwendig, um eine vom Programmierer als remote gekennzeichnete Klasse
zu transformieren. Basiert auf der Typinformation wird die Verteilungsanaly-
se anschließend verwendet, um Schätzungen von Aufrufhäufigkeiten, Kom-
plexitäten von Methoden und Strukturgrößen zu machen. Dadurch ergibt
sich auch die Zuordnung der Objekte zu Aktivitäten, die für die Verteilung
benutzt wird. Diese Analyseverfahren können im allgemeinen die Unschär-
fe in Kontrollflüssen nicht auflösen und scheitern vor allem bei Problemen
mit dynamischer Zuordnung der Aktivitäten, wie z.B. beim Arbeitsvorrat-
Paradigma.
Beim Pangaea-System wird das Programm in die Objektgraph-Darstellung
umgeformt. Diese Struktur hat den Vorteil, dass die Laufzeitstruktur des Pro-
gramms und die Beziehungen zwischen potentiellen Laufzeitobjekten hier-
durch repräsentiert werden können. Der Objektgraph kann aber die Informa-
tion über Objektidentität nicht repräsentieren, was nachteilig für eine Vertei-
lung ist.
Mittels der Ergebnisse der Objektgraph-Analyse erhält der Anwender einen
Vorschlag über eine Graphpartitionierung des Objektgraphen. Die Vertei-
lungsentscheidung muss aber schließlich vom Programmierer selber getrof-
55
KAPITEL 4. VERWANDTE ARBEITEN
fen werden. Die Analyseergebnisse werden auch zur Transformation des Pro-
gramms benutzt.
Die Technik der Programmanalyse wird in Doorastha benutzt, um die von
Benutzern angegebenen Anweisungen in Form von speziellen Tags in Java-
Kommentaren zu bewerten und das Programm so zu transformieren, dass
Objekte über Rechnergrenzen hinweg angesprochen werden können. So ähn-
lich ist es auch bei ProActive und Do!-Framework, wobei Verteilungsanwei-
sungen explizit vom Programmierer anzugeben sind. Bei ProActive haben
die Entwickler versucht, aktive Objekte mittels statischer Analyse (Erweite-
rung der interprozeduralen Shape-Analyse von Sagiv et al. [SRW98]) bei Pro-
grammen mit baumartig strukturierten Prozessen automatisch zu erkennen.
Sie haben Kriterien für die Erkennung aufgestellt und dann mittels Analyse
überprüft. Die Entwickler haben sich hierbei aber auf eine kleine Teilmenge
der Java-Sprache eingeschränkt: Keine Threads, keine statischen Anteile der
Klassen und keine dynamische Methodenbindung dürfen vorkommen.
Bei meinem Konzept beruhen Verteilungsentscheidung auf statischen und
dynamischen Programmeigenschaften, die innerhalb spezieller Objektver-
teilungsfunktionen (ODFs) zur Transformations- und Platzierungsberech-
nung verwendet werden und mittels Programmanalysen automatisch be-
stimmt werden. Statische Eigenschaften werden vollständig vor der Lauf-
zeit durch verschiedene Analysetechniken berechnet, dynamische Faktoren
werden ebenfalls vor der Laufzeit abgeschätzt. Das Modell zur Abschätzung
lehnt sich stark an das von JavaParty an. Anfragen an Verteilungsplan wer-
den automatisch in das Programm eintransformiert, so dass die Verteilungs-
hinweise zur Laufzeit vor jeder Erzeugung abgefragt werden können und das
Objekt entsprechend danach platziert wird.
4.1.4. Systeme mit dynamisch basiertem Ansatz
Bei diesem Ansatz beruhen die Verteilungsentscheidungen vollständig auf
den sich zur Laufzeit ergebenen Daten. Diese Daten werden von einer sys-
temeigenen, verteilten JVM gesammelt. Diese JVM protokolliert die Zugriffe
der Objekte zur Laufzeit und verteilt die Objekte nach einem eigenen Last-
modell. Zur Platzierung der Objekte kommt ebenfalls die Technik zur Last-
balancierung zum Einsatz. Der Vorteil an diesem Ansatz ist, dass echte Daten
über Objekte, Threads, Kommunikationen usw. zur Verfügung stehen und
man diese nicht abzuschätzen braucht. Der Nachteil ist, dass das Sammeln
56
4.1. EXISTIERENDE SYSTEME, FRAMEWORKS UND BIBLIOTHEKEN
der Daten und das Treffen der Entscheidungen zur tatsächlichen Laufzeit des
Programms dazugezählt werden müssen. Ein Vertreter für diese Kategorie ist
das Juggle-System.
Juggle
Das Juggle-System implementiert eine verteilte virtuelle Maschine, die trans-
parent Objekte und Threads nach einer automatischen Lastverteilung auf die
beteiligten Rechner verteilt. Die virtuelle Maschine bei Juggle wurde so in-
strumentiert, dass während des Programmablaufs ständig günstige Positio-
nen für Objekte und Threads errechnet werden. Die neu errechneten Konfi-
gurationen werden über Migrationen und Replikationen umgesetzt [SH98].
Juggle besitzt einige interessante Eigenschaften, die zu erwähnen sind. Jedes
Objekt im System ist für jeden beteiligten Rechner ansprechbar. Nur explizit
zugegriffene Daten, z.B. nach Zugriff auf eine Instanzvariable eines Objekts,
werden zum Zugreifenden migriert. Im Juggle werden nur einmal modifizier-
te Objekte überall auf allen Knoten repliziert und die Replikate werden nach
weiteren Zugriffen entsprechend aktualisiert. Methodenaufrufe finden nicht
entfernt statt, sondern der Programmcode wird auf allen Knoten repliziert
und dann dort lokal ausgeführt. Dies ist unabhängig davon, ob die Objektda-
ten lokal liegen oder nicht. Dadurch ist die Position der Threads unabhängig
von Code und Daten. Juggle verteilt die Objekte dynamisch und automatisch
nach dem Lastmodell, bei dem die Anzahl der Zugriffe aller Threads auf ein
Objekt in einer bestimmter Zeitspanne gezählt wird. Die Auslastung der Kno-
ten wird periodisch überprüft und ggf. unter Berücksichtigung der entfern-
ten Objektzugriffe korrigiert. Mit diesem vollständigen dynamischen Ansatz
konnte Juggle ein Speedup von 2,14 bis 4 bei der Anwendung „Raytracer -
Ball“ erzielen.
Die Objektreplikation wird im JScatter-System nach zwei unterschiedlichen
Konzepten realisiert: Objekte mit stagewise-immutable Eigenschaft werden re-
pliziert. Nach der Replikation dürfen dann nur Lesezugriffe stattfinden. Nach
dem in der Diplomarbeit von Weißenborn [Wei03] beschriebenen Konzept
werden auch Objekte repliziert, die hinterher eventuell modifiziert werden.
Durch nachträgliche Schreibzugriffe werden die Replikate nach der dort ent-
worfenen Strategie aktualisiert, was dem in Juggle realisierten Konzept ent-
spricht.
57
KAPITEL 4. VERWANDTE ARBEITEN
4.1.5. Andere Systeme
Hyperion
Etwas entfernt mit JScatter verwandt ist das System Hyperion [A+01],
[ABH+a]. Es enthält einen Transformator, der Java-Bytecode in C trans-
formiert, und eine Laufzeit-Bibliothek zur verteilten Ausführung von Java
Threads. Ein mehrsträngig formuliertes Java-Programm in Bytecode wird
hierbei zunächst vom Transformator in C-Code übersetzt. Das transformier-
te Programm enthält Bibliothek-Funktionen und wird ggf. gegen externe
Bibliotheken gelinkt. Das Programm kann dann auf Cluster von PCs oder
Workstations ausgeführt werden. Das ganze System wurde auf eine ver-
teilte, mehrsträngige Umgebung namens PM2 [ABH+b] aufgebaut. Mittels
dieser Umgebung können Prozesse lokal und entfernt erzeugt werden und
sie kommunizieren miteinander über RPC. Die oberste Schicht von PM2
ist die Plattform DSM-PM2 [AB], [AB01], die mehrsträngige Protokolle in
DSM (Distributed Shared Memory) unterstützt. Auf diese Schicht werden das
Java-Speichermodell sowie die meisten von Hyperion unterstützten Primitive
wie Threads, Kommunikation shared memory Subsysteme auf Funktionen von
PM2 direkt abgebildet. Entfernte Zugriffe auf Objekte werden mittels zwei-
er Protokolle entdeckt: Das eine prüft auf explizite Lokalität der Objekte und
das andere basiert auf einer Technik namens page faults, die die Zugriffe auf
entfernte Objekte überprüft [AH01].
Ähnliche Ansätze mit verteilten JVMs finden sich in vielen verschiedenen Pro-
jekten wie Java/DSM [YC97], cJVM [AFT99], [AFT+00], Jackal [VHBB01] und
JESSICA [MWL00]. Die verteilte JVM wird bei diesen Systemen als ein ein-
ziges Systemabbild (SSI - Single System Image) modelliert. Hierbei wird das
Konzept GOS (Global Object Space) benutzt, um nur ein einziges Java Objekt-
Heap zu modellieren, das allen beteiligten verteilten Knoten des Clusters zur
Verfügung steht. Dadurch bleiben die Objektzugriffe über Rechnergrenzen
hinweg in einer solchen verteilten Umgebung transparent. Durch das neue
Objektmodell werden remote und lokale Objekte voneinander unterschieden,
was auch als Grundlage zur Verteilung verwendet wird. Außerdem wird zur
Laufzeit die Technik der Lastbalancierung eingesetzt, nach der die Objekte
und Threads verteilt werden.
Zur Verteilungsentscheidung während der Programmausführung wird bei
meinem Konzept die hybride Analyse benutzt. Hierbei hat man gegenüber
nur auf dynamischen Daten basierter Entscheidung den Vorteil, dass stati-
58
4.1. EXISTIERENDE SYSTEME, FRAMEWORKS UND BIBLIOTHEKEN
sche Eigenschaften des Programms, insbesondere interprozedurale Informa-
tionen, schon zur Verfügung stehen. Die Beschaffung solcher Informationen
hat keinen Einfluss auf die Laufzeit des Programms. Dynamische Analyse
wird dann nur gezielt eingesetzt, um die noch fehlenden Daten zu ermitteln.
Dieser Ansatz und der Einsatz der Lastbalancierungstechnik zur Verteilung
sind prinzipiell zwei verschiedene Konzepte. Sie beschaffen die Information
zur Platzierungsentscheidung aber auf gleiche Weise. Außerdem besteht die
verteilte Laufzeitumgebung des JScatter-Systems aus den Standard-JVMs und
es wird die Kommunikationstechnik KaRMI verwendet.
J-Orchestra
J-Orchestra [TS01], [StJOG01], [TS02] ist ein System, das monolithische Java-
Programme automatisch in eine verteilbare Version partitioniert. Die Trans-
formation findet auf der Bytecode-Ebene statt und die lokalen Objektrefe-
renzen im Programm werden dabei durch entfernte Referenzen mittels der
RMI-Technik ersetzt. Das transformierte Programm kann dann verteilt auf
mehreren Standard-JVMs ausgeführt werden. Dieses Konzept ist vergleich-
bar mit dem des JScatter-Systems, wobei hier ein anderes Transformations-
konzept verwendet wird.
4.1.6. Systeme für High-Performance Java - Datenparallel
Obwohl das Thema High-Performance Java nur entfernt verwandt mit mei-
nem Thema ist, möchte ich hier einen kleinen Ausflug in die Systeme für HP-
Java unternehmen. HP-Java hat HP-Fortran als Vorbild und entwickelt sich
mit der Beliebtheit von Java mit der Zeit immer weiter. Es ist vor allem für re-
guläre Anwendungen zum wissenschaftlichen Rechnen geeignet. Die hierbei
verwendeten Techniken und Konzepte können aber auch nützlich für irregu-
läre Ansätze sein.
JavaParty
Philippsen erweiterte in [Phi98] Java um eine forall-Anweisung für die da-
tenparallele Berechnung. Eine Source-to-Source-Transformation wurde einge-
setzt, um forall-Konstrukte auf mehrsträngigen Java-Code abzubilden. Zu-
59
KAPITEL 4. VERWANDTE ARBEITEN
sätzlich wurde ein Optimierungsverfahren präsentiert, das die Anzahl der be-
nötigten Barrierensynchronisationen verringert. Der entstehende nebenläufi-
ge Programmcode wird nicht explizit auf parallele Prozessoren verteilt, son-
dern bildet die Eingabe für das JavaParty-System. Die größte bekannte An-
wendung ist die Umsetzung des Large-Scale parallelen geophysikalischen Al-
gorithmus zur Erdkundung.
Manta
Manta [vNMB+99] ist ein System, das Java in ein hierarchisches
Supercomputer-Netz (Myrinet-basierter Cluster) integriert. Kommunikati-
onsplattform ist ein eigenes RMI, das aber auf Sun-RMI basiert. Manta erwei-
tert wie JavaParty die Java-Sprachkonstrukte um das Schlüsselwort remote.
Orca
Orca [vNMB+99], [BBH+96], [BK93], [BBH+98] ist ein objektbasiertes distri-
buted shared memory System. Orca selbst ist eine Sprache, die nicht klassen-
basiert sondern objektbasiert ist. Das System enthält einen eigenen Compiler,
ein Laufzeitsystem und eine virtuelle Maschine (Panda). Der Compiler gene-
riert reguläre Ausdrücke, die die Verwendung von gemeinsamen Objekten
beschreiben. Das Laufzeit-System verwendet diese Informationen zusammen
mit Laufzeitdaten zur Verteilung und zur Steuerung der Replizierung.
HPJava
Dies ist eine Erweiterung von Java, um die Programmierung mit verteil-
ten Arrays zu erleichtern (SPMD-Modell). Die Erweiterung enthält drei neue
Konstrukte (on,at und over), die nützlich für datenparallele Schleifen sind. Die
Kommunikationstechnik ist MPI [CZF+98].
Titanium
Titanium [YSP+98] ist eine Java-basierte Sprache und gleichzeitig auch ein
System für High-Performance und paralleles wissenschaftliches Rechnen. Die
60
4.2. KOMMUNIKATIONSMODELLE UND -TECHNIKEN
wesentlichen Erweiterungen sind immutable Klassen, multidimensionale Ar-
rays für explizite SPMD-Berechnungen mit globalem Adressraum und so ge-
nanntes bereich-basiertes Speicher-Management.
4.2. Kommunikationsmodelle und -techniken
Als Grundlage für die Kommunikation auf unterster Ebene wird in den meis-
ten Systemen und Bibliotheken das Modell Remote Method Invocation verwen-
det. Mit Hilfe dieser Techniken können Objekte ihre Nachrichten und Daten
über das Netzwerk austauschen. Im Folgenden möchte ich einige wichtige
Modelle mit ihren relevanten Aspekten beschreiben, die wir direkt in unse-
rem System verwendet haben. Die anderen, die weniger relevant sind, sind in
der Kategorie „andere Techniken“ im Abschnitt 4.2.5 aufgeführt.
4.2.1. Sun-RMI
RMI ist eine Client-Server-orientierte Verteilungstechnologie von Sun [Mic],
[Mic98], [MPA99]. Instanzen von Klassen werden unter Verwendung des Se-
rialisierungsmechanismus by-copy übertragen. Die Klassen, deren Objekte mit
RMI zu verteilen sind, müssen das Interface Remote implementieren oder
von der Klasse UniCastRemoteObject erben. Des Weiteren müssen für die
zu verteilenden Klassen mit dem Java-Übersetzer rmicStub-(Clientseite) und
Skeleton-Objekte (Serverseite) erzeugt werden. Die Stubs übernehmen dann
die Rolle der eigentlichen Objekte sowie den Transfer der Argumente und die
Initialisierung des eigentlichen Methodenaufrufs. Diese Stellvertreterobjekte
sehen bezüglich der entfernt aufrufbaren Methoden genauso aus wie die ent-
sprechenden remote Objekte. Die Skeleton-Objekte empfangen die Parameter
und rufen ausschließlich lokal die korrespondierenden Methoden auf.
Zum Übermitteln der Argumente und des Resultats bei einem entfern-
ten Methodenaufruf verwendet das RMI-System die Technik der Objekt-
Serialisierung [Dow98], [Mic]. Ein als serialisierbar gekennzeichnetes Objekt
wird in einen Strom umgewandelt, geschrieben und daraus wieder gelesen.
Es gibt in RMI einige Einschränkungen, die aber wichtige Voraussetzung für
ein verteiltes Objektmodell darstellen. Sie wurden ausführlich in [HP99] aus-
geschildert und in unserem System auch berücksichtigt.
61
KAPITEL 4. VERWANDTE ARBEITEN
4.2.2. KaRMI
Mit der Begründung, dass das Sun-RMI zu langsam für Hochleistungsanwen-
dungen ist, haben die Karlsruher ein schnelleres RMI mit einer effizienten
Serialisierung entwickelt. Durch die Aufschlüsselung der Kommunikations-
kosten mittels Sun-RMI wurde dort die Serialisierungsphase durch die
schlanke Typcodierung, eine neue Variante der reset()-Methoden für die
Kopiersemantik der Argumente und eine verbesserte Technik zur Pufferung
verbessert. Dazu wurde die Struktur von KaRMI gegenüber Sun-RMI kom-
plett neu entworfen, so dass das System neben Geschwindigkeitsverbesse-
rungen auch die Nutzung mehrerer verschiedener Netzwerktechnologien an-
bietet [HP99].
Wir benutzen neben dem Standard Sun-RMI auch diese Kommunikations-
technik, um eine Geschwindigkeitssteigerung zu erzielen.
4.2.3. Distributed Threads
Eddy Tryen, Danny Weyns und Pierre Verbaeten haben ein Konzept zu dis-
tributed Threads [TRV+00], [WTV02a], [WTV02b] bei verteilter Ausführung
mehrsträngiger Java-Programme entwickelt. Mit diesem Konzept können sie
bei verteilten Anwendungen jedem Thread eine eindeutige Identität zuord-
nen und können somit die Semantik des verteilten Kontrollflusses gewähr-
leisten. So kann das Problem unerwarteter Deadlocks gelöst werden. Au-
ßerdem kann das Monitor-Konzept wie im sequentiellen Fall einfach be-
nutzt werden, ohne dessen Semantik zu verlieren. Durch die Technik der
Bytecode-Transformation können mehrsträngige Anwendungen mit diesem
Ansatz realisiert werden. Die Transformation wurde in einen eigenen Klas-
senlader integriert und so werden Programme in Bytecode erzeugt, die auf
einer normalen JVM ablaufen können. Wir haben im System einen ähnlichen
Mechanismus zur eindeutigen Erkennung der Aktivitäten, damit die Seman-
tik des verteilten Programms erhalten bleibt. Außerdem verwenden wir auch
das verteilte Monitor-Konzept in unserem System.
4.2.4. JavaSpaces
JavaSpaces [FHA99] ist ein spezieller Jini-Service von Sun Microsystems, Inc.
Ein Space ist ein persistentes Objekt-Lager, das von verschiedenen Prozessen
62
4.2. KOMMUNIKATIONSMODELLE UND -TECHNIKEN
gemeinsam benutzt wird und über das Netzwerk zugänglich ist. Die Koope-
ration der Prozesse geschieht dadurch, indem die Objekte einen oder mehrere
Spaces betreten und wieder verlassen. Damit kann schwierige Synchronisa-
tion der Aktivitäten mit Hilfe des Spaces stattfinden. Die Prozesse können
drei einfache Operationen ausführen: Mit write schreiben sie Objekte in ein
Space, mit take werden Objekte aus dem Space herausgenommen und mit
read lesen die Prozesse Objekte im Space. Solange sich ein Objekt im Space
befindet, ist es passiv. Um ein Objekt zu verändern oder eine Methode auf ihm
aufzurufen, muss ein Prozess das Objekt explizit aus dem Space entfernen, es
eventuell verändern und danach wieder zurücklegen. Die Eigenschaften der
Spaces leiten sich aus den Eigenschaften der Jini-Technologie her.
Das Programmiermodell bei JavaSpaces unterscheidet sich von den üblichen
Techniken wie Message Passing und RMI. Über die Spaces koordinieren die
remote Java-Prozesse ihre Aktivitäten und tauschen ihre Daten aus.
4.2.5. Andere Techniken
javab
Javab [BG98], [BG97] ist ein Werkzeugsystem zur Parallelisierung von Byte-
code unter Verwendung von Bytecode-to-Bytecode Optimierung und Trans-
formation. Durch Kontroll- und Datenfluss-Analyse wird die Parallelität in
Schleifen automatisch erkannt.
mpiJava
Dies ist eine API zur allgemeinen Benutzung des Message Passing Interface
(MPI). Die Funktionalität von MPI wird über JNI-Schnittstellen [BCFK] auf
Java-API abgebildet. Diese Schnittstelle wurde unter dem Projekt HPJava
[CZF+98] entwickelt, setzt aber keine spezielle Spracherweiterung wie
HPJava voraus.
Corba
Corba [MZ96] ist das OMG Standard-Verteilungssystem. Die Schnittstelle von
CORBA-Objekten wird durch IDL definiert. Die Objekte werden ebenfalls
63
KAPITEL 4. VERWANDTE ARBEITEN
mit by-copy übertragen. Entfernte Objekte kommunizieren miteinander zur
Laufzeit über Stubs, die vom CORBA-eigenen Übersetzer erzeugt werden.
4.3. Vergleiche - Überblicke
In diesem Abschnitt werden nochmals die wesentlichen Merkmale der oben
vorgestellten Systeme und Bibliotheken tabellarisch zusammengefasst. Hier-
bei werden nur diejenigen in Betracht gezogen, die direkt mit unserem Ansatz
verwandt sind. Legende:
: vorhanden, : nicht vorhanden, ?: konnte nicht in Referenzen gefunden
werden
64
4.3. VERGLEICHE - ÜBERBLICKE
JavaParty Remote Objects Pangaea Doorastha ProActive Do! Juggle Hyperion
Sprach-
änderung (remote) (newremote)
Semantik- ?
erhaltend
Statische
Entscheidung
Dynamische
Entscheidung
Eigener Pre-
Compiler prozessor
Eigene
Laufzeit-
Umgebung
Eigene
Kommuni- (KaRMI) (nicht
kation Sun-RMI)
Migration plattform- ??
abhängig
Replikation plattform- ? ?
abhängig
Weitere polymorphe Bytecode grafische spezielle Polymorphie Collection Instrumen- transfomiert
Merkmale Verwendung wird Objektgraph- Tags zwischen und neue tierung Java-
in geschickt Darstellung als aktiven PAR- in Programme
monomorphe und dort zur Angabe Kommentar und Konstrukte eigener in
Teile lokal der für die passiven für aktive JVM C-Code
zerlegt ausgeführt Verteilung Verteilung Objekten Objekte
Tabelle 4.1.: Alle Systeme und Bibliotheken - Zusammenfassung
65
KAPITEL 4. VERWANDTE ARBEITEN
Zusammenfassung
Im JScatter-System werden die Konzepte und vor allem die interessanten
Merkmale anderer Systeme wie JavaParty, ProActive, Doorastha usw. ver-
wendet. Das Programm wird automatisch in eine verteilte Version transfor-
miert und die Objekte werden vollständig automatisch verteilt. Die Vertei-
lungsentscheidung für Programmobjekte basiert auf statischen sowie dyna-
mischen Programmeigenschaften, die mit Hilfe der statischen und dynami-
schen Programmanalysen berechnet werden. Hier werden auch verschiedene
statische Analysetechniken verwendet, die in anderen Systemen eingesetzt
wurden.
66
5. Verteilungsstrategien und
Verteilungsplan: Konzept und
Modellierung
Inhalt
5.1. Modell zur automatischen Verteilung mittels Programm-
analyse ................................. 69
5.1.1. Programmanalyse zur Berechnung der Programmei-
genschaften . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
5.1.2. Anforderungen vor der Laufzeit . . . . . . . . . . . . . . 75
5.1.3. Aufgaben zur Laufzeit . . . . . . . . . . . . . . . . . . . . 76
5.2. Verteilungsplan: die Komponenten, deren Modellierung
und verwendete Konzepte . . . . . . . . . . . . . . . . . . . . . 77
5.2.1. Modellierung von Verteilungsvorschriften als ODFs . . 79
5.2.2. Modellierung der Verteilungsstrategien . . . . . . . . . . 87
5.2.3. Modell eines Verteilungsplanes . . . . . . . . . . . . . . . 90
5.2.4. Andere Komponenten . . . . . . . . . . . . . . . . . . . . . 94
5.3. Grundlegende Objektverteilungsfunktionen . . . . . . . . . . 95
5.3.1. Statische Verteilungsfunktionen . . . . . . . . . . . . . . . 96
5.3.2. Basis-Verteilungsfunktionen . . . . . . . . . . . . . . . . . 99
5.3.3. Dynamische Verteilungsfunktionen . . . . . . . . . . . . 104
5.4. Spezielle Verteilungsstrategien . . . . . . . . . . . . . . . . . . 106
5.4.1. Statische Verteilungsstrategien . . . . . . . . . . . . . . . 106
5.4.2. Basis-Verteilungsstrategien . . . . . . . . . . . . . . . . . . 107
5.4.3. Strategien mit dynamischen Entscheidungen . . . . . . . 109
5.5. Konkrete Verteilungspläne . . . . . . . . . . . . . . . . . . . . . 111
67
KAPITEL 5. VERTEILUNGSSTRATEGIEN UND VERTEILUNGSPLAN:
KONZEPT UND MODELLIERUNG
Abgesehen von der aufwändigen technischen Umsetzung werden die wich-
tigen strategischen Schritte nicht mehr von einem Programmierer oder ei-
nem Entwickler durchgeführt, sondern durch das im Folgenden vorgestell-
te Verfahren automatisch erledigt. Der erste Schritt ist das Analysieren des
Programms zur Sammlung von Informationen, die Hinweise auf die Vertei-
lung der Programmobjekte liefern. Beruhend auf diesen Hinweisen werden
verschiedene Verteilungsstrategien aufgestellt, die zur Entscheidung über die
Verteilung der Objekte angewandt werden.
Konzeptionell werden die verschiedenen Verteilungsstrategien so entwickelt,
dass sie für jede mehrsträngige Java-Anwendung angewandt werden kön-
nen. Jede Strategie ist eine Zusammensetzung aus bestimmten Verteilungs-
vorschriften, die als Objektverteilungsfunktionen (ODFs) bezeichnet und in ei-
ner geeigneten Weise miteinander kombiniert werden. Die ODFs innerhalb
einer Strategie berechnen ähnliche Eigenschaften des Programms, beispiels-
weise statische oder dynamische Eigenschaften. Je nach berechneten Analyse-
ergebnissen liefern die ODFs Entscheidungen für die Programmtransformati-
on an jeder Erzeugungsstelle beziehungsweise für die eigentliche Verteilung
der zu erzeugenden Objekte zur Laufzeit. Durch strukturierte Zusammenset-
zung bestimmter Strategien entsteht ein Verteilungsplan. Abbildung 5.1 stellt
die konzeptionellen Unterschiede dieser drei Ebenen (ODFs Strategien
Verteilungsplan) dar.
Der Verteilungsplan ist in diesem Konzept die wichtigste Komponente, die
alle strategischen Entscheidungen zur Transformation und zur Verteilung zu
treffen hat. Er erledigt die strategischen Aufgaben eines Programmierers. Wie
im Abschnitt 3.2 grob angedeutet spielt die Programmanalyse in diesem Kon-
zept die entscheidende Rolle. Die Exaktheit der Analyseergebnisse trägt zur
Güte der Verteilungsentscheidung bei.
Dieses Kapitel befasst sich mit den Konzepten und der Modellierung der ein-
zelnen genannten Komponenten, die eine fundamentale Rolle spielen. Das
gesamte Schichtenmodell, das als Grundlage für eine automatische Vertei-
lung mehrsträngiger Java-Programme dient, wird hier vorgestellt. Einige zu
erfüllende Grundvoraussetzungen und Anforderungen, zusammen mit dem
Berechnungsmodell in einem Verteilungsplan werden ebenfalls im ersten Ab-
schnitt dargestellt. Die Modellierung der Verteilungsvorschriften als Funktio-
nen, deren Zusammensetzung in einer geeigneten Weise zu Verteilungsstra-
tegien und das Modell eines Verteilungsplans werden anschließend beschrie-
ben. Mit den erforderlichen Formalismen ist man dann in der Lage, grundle-
gende Verteilungsfunktionen zu formulieren. Aus diesen konkret definierten
68
5.1. MODELL ZUR AUTOMATISCHEN VERTEILUNG MITTELS
PROGRAMMANALYSE
Verteilungsvorschriften werden spezielle Strategien entwickelt, die bei der
Planung der Verteilung zum Einsatz kommen. Je nach angewandten Strate-
gien bilden sich somit verschiedene Pläne, die dazu dienen, das eingegebene
Programm für die verteilte Ausführung zu transformieren und dessen Akti-
vitäten und Objekte zur Laufzeit automatisch zu verteilen. So befassen sich
die letzten drei Abschnitte mit diesen Gesichtspunkten.
5.1. Modell zur automatischen Verteilung mittels
Programmanalyse
Die Kernidee des hier verwendeten Verteilungsansatzes besteht darin, alle
strategischen Entscheidungen über Transformation und Verteilung der Ob-
jekte vom Verteilungsplan treffen zu lassen. Der Grund dafür, warum solche
Entscheidungen nicht direkt im verteilten Programm codiert, sondern durch
den Verteilungsplan gekapselt werden, ist, dass dadurch die geforderte Flexi-
bilität und Anpassbarkeit gewährleistet wird. Außerdem werden die Vertei-
lungsentscheidungen im Plan nicht konkret berechnet, sondern nur in Form
einer Sammlung von Vorschlägen formuliert. Dadurch bietet sich die Mög-
lichkeit, Informationen über das Programm, welche erst nach der statischen
und dynamischen Programmanalyse berechnet werden können, und Infor-
mation über die verteilte Umgebung, beispielsweise die Anzahl der zur Ver-
fügung stehenden virtuellen Maschinen, in die Entscheidung mit einfließen
zu lassen.
Geeignete statische und dynamische Analyseverfahren werden verwen-
det, um die Programmeigenschaften zu bestimmen, die relevant für die
automatische Verteilung sind. Diese Eigenschaften werden dann inner-
halb spezieller Objektverteilungsfunktionen (ODFs) benutzt, die elementaren
Transformations- und Verteilungsentscheidungen berechnen. Durch geeigne-
te Zusammensetzung bestimmter ODFs entstehen Strategien, die Entschei-
dungen über Programmtransformation und Objektverteilung treffen. Meh-
rere passende Strategien bilden zusammen einen Verteilungsplan, der über
die Transformation der Objekterzeugungsstellen entscheidet und Aussagen
zur Objektplatzierung während der Programmausführung liefert. Abbildung
5.1 stellt das konzeptionelle Modell zur automatischen Verteilung visuell dar,
wobei es aus fünf verschiedenen, aufeinander aufbauenden Bereichen be-
steht.
69
KAPITEL 5. VERTEILUNGSSTRATEGIEN UND VERTEILUNGSPLAN:
KONZEPT UND MODELLIERUNG
CHA
Objektgraph
Analyse
Points-to-
Analyse
Escape-
Analyse
Stagewise-Immutable-
Analyse
Global Def-Use
Analyse
Local Def-Use
Analyse
Peer-
Analyse
Migrations-
Analyse


 
Replikations-
Analyse
Zugriffhäufigkeits-
Analyse
Ausführungshäufigkeits-
Analyse
Instruktion-Def-Use
Analyse


 !
 "
 #
 !
 "
 $

%&'%&&
%(

&((
&
)'
&
&((
(* +'&*
,*( &**
-.

/0/1 2
3
34 3
34
001/3
05


-.

/0/1
001/3
3 2
34
3
2
3 3
2
34
500/1
05
Analysetechniken
statisch
dynamisch
Funktionen zur
Berechnung der
Programm-
eigenschaften
Laufzeit-
eigenschaft
Objektverteilungs-
funktionen (ODFs)
Verteilungsstrategien
Zusammensetzung
bestimmter ODFs
Verteilungspläne
Zusammensetzung
bestimmter Strategien
Kapitel 6
Kapitel 5
Werte-
Bindung
060((,)'
500/1
Abbildung 5.1.: Modell zur automatischen Verteilung - Von Programm-
analyse zu Verteilungsplänen
Wie in Abbildung 5.1 angedeutet werden die erforderlichen Formalismen zur
Formulierung der ODFs, Verteilungsstrategien und somit der Verteilungsplä-
ne in diesem Kapitel beschrieben. Hierbei wird auf einzelne spezielle ODFs
(angegeben in Abbildung 5.1) im Detail eingegangen. Deren Zusammenset-
zung als Strategien und darauf aufgebaute Verteilungspläne werden hier
ebenfalls dargestellt. Das Konzept, Analyseverfahren zur Bestimmung der
Programmeigenschaften für die Verteilung automatisch anzuwenden, insbe-
sondere der Einsatz statischer und dynamischer Analysen, wird noch in die-
70
5.1. MODELL ZUR AUTOMATISCHEN VERTEILUNG MITTELS
PROGRAMMANALYSE
sem Abschnitt vorgestellt. Details über einzelne Eigenschaften, angewandte
Analysetechniken sowie das formale Modell für die Kombination statischer
und dynamischer Eigenschaften finden sich dann im Kapitel 6.
5.1.1. Programmanalyse zur Berechnung der
Programmeigenschaften
Die elementaren Eigenschaften eines Programms, die bei der automatischen
Verteilung eine wichtige Rolle spielen, werden durch bestimmte Analysever-
fahren berechnet. Statische Programmanalyse ist in der Lage, vor der Lauf-
zeit viel Information über das Programm, vor allem verteilungsrelevante Ei-
genschaften des Programms zu beschaffen. Es steht im Prinzip statischen
Analyseverfahren mehr Zeit zur Verfügung, weil sie vor der eigentlichen
Ausführung des Programms durchgeführt werden und ihre Ergebnisse un-
abhängig von einzelnen Programmabläufen sind. Diese Ergebnisse können
aber bei jeder konkreten Programmausführung genutzt werden. Somit lohnt
sich der dort investierte Aufwand. Allerdings reicht statische Analyse allein
nicht aus, da sich nicht nur der Kontrollfluss und die Eingabedaten eines
Programms, sondern auch andere Faktoren wie z.B. die Rechnerlast oder die
Kommunikationskosten zur Laufzeit ändern können. Diese Faktoren dürfen
nicht ignoriert werden, deshalb liegen sie vor der Laufzeit als Verteilungs-
variablen vor und werden bei der Formulierung der Verteilungsfunktionen,
ebenfalls auch vor der Laufzeit, mit berücksichtigt.
Wie in Abbildung 5.2 verdeutlicht wird, werden statische und dynamische Pro-
grammeigenschaften (als dunkle bzw. helle Ovale visualisiert) mittels Funktio-
nen berechnet. Innerhalb dieser Funktionen liegen die Eigenschaften als sta-
tische bzw. dynamische Verteilungsvariablen vor, die durch entsprechende Ver-
fahren zur Programmanalyse berechnet werden. Außerdem gibt es Laufzeit-
eigenschaften, deren konkrete Werte erst zur Laufzeit bekannt werden. Sie
werden in Abbildung 5.2 im mittleren Bereich als ganz helle Ovale (#JVM,
Runtime constants usw.) dargestellt. Zu verschiedenen Zeitpunkten wer-
den bestimmte konkrete Werte, die sich aus statischen oder dynamischen Ana-
lyseergebnissen oder durch Bekanntgeben der Anzahl der JVMs ergeben, an
diese Verteilungsvariablen gebunden. So können elementare Verteilungsergeb-
nisse berechnet werden. Es gibt drei verschiedene Arten von Wertebindun-
gen: Bindung der Werte an statische Variablen, an dynamische Variablen und
Bindung an Laufzeitunbekannte.
71
KAPITEL 5. VERTEILUNGSSTRATEGIEN UND VERTEILUNGSPLAN:
KONZEPT UND MODELLIERUNG
CHA
Objektgraph
Analyse
Points-to-
Analyse
Escape-
Analyse
Global Def-Use
Analyse
Local Def-Use
Analyse
Peer-
Analyse
Migrations-
Analyse


 
Replikations-
Analyse
Zugriffhäufigkeits-
Analyse
Ausführungshäufigkeits-
Analyse
Instruktion-Def-Use
Analyse


 060((,)'
%&'
%&&
%(

&((
&
)'
&
&((
(* +'&*
,*( &**
-.

/0/1 2
3
34 3
34
001/3
05
500/1
Programmanalyse
Statische Verfahren
Dynamische Verfahren
Funktionen zur Berechnung
der Programmeigenschaften
Laufzeiteigenschaft
Objektverteilungs-
funktionen (ODFs)
Werte-
Bindung
Statische P-Eigenschaft
Dynamische P-Eigenschaft
Stagewise-Immutable-
Analyse
Abbildung 5.2.: Analysetechniken zur Bestimmung der Programmeigen-
schaften
Jede spezielle Verteilungsfunktion (ODF) verwendet bei ihrer Berechnung der
Verteilungsergebnisse bestimmte Eigenschaften des Programms und/oder
Laufzeiteigenschaften. Vor der Laufzeit sind die Laufzeiteigenschaften un-
bekannt, deshalb fließen sie in die Berechnung der ODFs als Variablen ein.
Bei einigen ODFs können die verwendeten Programmeigenschaften vollstän-
dig vor der Laufzeit ausgerechnet werden. Es gibt aber manche Programm-
eigenschaften, die von der Programmanalyse vor dem Programmlauf abge-
schätzt werden müssen und die abgeschätzten Werte lassen sich zur Laufzeit
durch Ergebnisse dynamischer Analyse verbessern. Somit lassen sich die Ver-
teilungsergebnisse der zugehörigen ODFs verbessern. Dies ist auch der An-
satz zur hybriden Analyse, die statische und dynamische Analyseergebnisse
kombiniert.
Statische Eigenschaften
Statische Analyseverfahren werden hier eingesetzt, um sowohl statische Ei-
genschaften des Programms zu bestimmen, als auch dynamische Programm-
72
5.1. MODELL ZUR AUTOMATISCHEN VERTEILUNG MITTELS
PROGRAMMANALYSE
eigenschaften abzuschätzen, die sich vor der Laufzeit nicht berechnen lassen.
Einige Verfahren wurden in Abbildung 5.2 im Analysebereich (unterer Be-
reich) namentlich genannt. Eine Programmeigenschaft kann aus zwei Teilen
bestehen: Der eine beschreibt den statischen berechenbaren Aspekt und der
andere repräsentiert den dynamischen. Diese zur Laufzeit ergebenen Eigen-
schaften modellieren die Ausführungskosten oder Kommunikationskosten.
Für den statisch bestimmbaren Anteil einer solchen Eigenschaft kommen ein
oder mehrere statische Analysetechniken zum Einsatz.
Einige spezielle ODFs brauchen für ihre Berechnungen vollständige Ergeb-
nisse der verwendeten Programmeigenschaften, die aus solchen statischen
und dynamischen Anteilen bestehen. Daher werden ebenfalls statische Ver-
fahren zur Programmanalyse benutzt, um Abschätzungen für dynamische
Anteile dieser Programmeigenschaften durchzuführen. Dennoch liegen Lauf-
zeiteigenschaften als Variablen bei den Berechnungen vor. Mit diesem Modell
können alle Faktoren, sowohl statische als auch dynamische, bei der Berech-
nung der Verteilungsergebnisse berücksichtigt werden. Als Beispiel betrach-
ten wir die ODF ODFTHREADWORK im oberen Bereich von Abbildung 5.2:
Um Verteilungsergebnisse bestimmen zu können, braucht diese ODF u.a. die
Programmeigenschaft Work, die prinzipiell aus statischen und dynamischen
Anteilen besteht und mittels der beiden Funktionen Work() und Work_d()
(im mittleren Bereich) berechnet wird. Vor der Laufzeit liefert die Funktion
Work_d() einen durch die statische Analyse abgeschätzten Wert, der zunächst
mit zugehörigen Variablen innerhalb der ODF gebunden ist. Die andere Funk-
tion Work() kann vollständig ausgerechnet werden. Unter der Verwendung
dieser Werte liefert die ODFTHREADWORK ein so genanntes initiales Vertei-
lungsergebnis, das eine Zuordnung der an einer Erzeugungsstelle entstehen-
den Objekte zu abstrakten JVMs beschreibt.
Dynamische Eigenschaften
Dynamische Programmeigenschaften sind solche, die sich bei jeder Pro-
grammausführung ändern und sich vor der Laufzeit durch statische Analy-
se sehr schwer bestimmen lassen. Beispiele dafür sind die Ausführungskos-
ten der Programmschleifen, Kommunikationskosten durch Methodenaufrufe
aufgrund der dynamischen Methodenbindung oder Zugriffshäufigkeiten be-
stimmter Methoden. Statische Analyseverfahren müssen Abschätzungen für
solche Eigenschaften machen, damit initiale Verteilungsergebnisse vor der
Laufzeit berechnet werden können. Durch das vorgestellte Modell werden
73
KAPITEL 5. VERTEILUNGSSTRATEGIEN UND VERTEILUNGSPLAN:
KONZEPT UND MODELLIERUNG
solche Eigenschaften durch Verteilungsvariablen innerhalb der ODFs model-
liert, die zu verschiedenen Zeitpunkten unterschiedliche Werte annehmen
können. Während der Programmausführung werden einige Häufigkeitsana-
lysen durchgeführt, die gezielt zu bestimmten Eigenschaften Laufzeitwerte
sammeln. Die abgeschätzten Werte werden durch diese ersetzt und somit las-
sen sich neue Verteilungsergebnisse errechnen. Sie werden mit den initialen
Ergebnissen verglichen. Falls sich dabei bessere Platzierungsergebnisse erge-
ben, werden diese für die demnächst zu erzeugenden Objekte verwendet und
die alten werden überschrieben. Dieses Prinzip wird in Abbildung 5.2 durch
die Werte-Bindung zwischen der Funktion Work_d() und der Verteilungsva-
riable in der ODFTHREADWORK verdeutlicht.
Ergebnisse solcher Häufigkeitsanalysen sind auch Grundlage für die Objekt-
migration und -replikation. Nach dem gerechneten Verteilungsergebnis hat
man einen Plan, wohin ein Laufzeitobjekt migriert oder repliziert werden
kann. Basierend auf den Zugriffshäufigkeiten der Methoden wird dieser Plan
zur Laufzeit umgesetzt: Es wird entschieden, zu welcher konkreten JVM das
Objekt migriert oder repliziert wird. Ein Beispiel dafür ist die Funktion migra-
tion_d() in Abbildung 5.2, die zur Laufzeit aktuelle Werte zur Zugriffshäufig-
keit für die ODFMIGRATE liefert.
Außer den beiden Programmeigenschaften werden noch Architektur- und
Laufzeiteigenschaften in die Berechnung der Verteilung einbezogen. Sie mo-
dellieren unter anderem Laufzeitkonstanten,die vor der Laufzeit unbekannt sind
und während der Ausführung konstant bleiben. Beispiele dafür sind die Anzahl
der beteiligten JVMs oder Programmvariablen, die sich bei jeder Programm-
ausführung ändern können, aber während einer Ausführung konstant blei-
ben.
Unter Berücksichtigung aller relevanten Parameter zur Verteilung können
Platzierungsergebnisse mittels Strategien berechnet werden. Zur Laufzeit be-
steht ein solches Ergebnis aus einer Zuordnung von konkreten Programmob-
jekten zu Adressräumen (oder JVMs). Die Umsetzung dieser Zuordnung wird
vor der Laufzeit durch geeignete Transformationen direkt im Programm co-
diert. Dies erlaubt bei der Ausführung des transformierten Programms, den
Plan zu evaluieren, um dann konkrete Verteilungshinweise zu erhalten und
die Objekte entsprechend zu platzieren.
74
5.1. MODELL ZUR AUTOMATISCHEN VERTEILUNG MITTELS
PROGRAMMANALYSE
5.1.2. Anforderungen vor der Laufzeit
Zum Zeitpunkt der Transformation hat der Verteilungsplan für jede Objekt-
erzeugungsstelle die Entscheidung zu treffen, ob alle dort zur Laufzeit entste-
henden Objekte remote-Objekte oder nicht-remote Objekte sind. Auf diese An-
frage muss der Plan eine eindeutige Antwort liefern. Wenn festgestellt wird,
dass nur auf ein Objekt entfernte Zugriffe stattfinden können, werden auf der
Transformator-Seite erforderliche Transformationsmechanismen (siehe Kapi-
tel 7) für die zugehörige Klasse, die Erzeugungsstelle und die Zugriffstellen
angewandt. Zur Bindung einer Verteilungsentscheidung an eine Objekterzeu-
gungsstelle wird eine Planevaluierung in Form von Anfragen direkt an die-
ser Programmstelle eincodiert. Unmittelbar vor der Erzeugung eines Objekts
liefert der Plan durch diese Evaluation ein Verteilungsergebnis, also die Ziel-
maschine, wo das Objekt erzeugt wird.
In diesem Konzept werden Strategien zur automatischen Objektmigration
und -replikation eingesetzt, um die entfernten Methodenaufrufe zu minimie-
ren und somit einen sonst entfernt ausgeführten Methodenaufruf lokal aus-
zuführen. Nach diesen Strategien wird entschieden, wann und zu welcher
Zielmaschine ein Objekt während des Programmlaufs migriert bzw. repli-
ziert wird. Mit diesem Konzept sowie mit der zugehörigen technischen Um-
setzung befasste sich die Diplomarbeit von Marco Weißenborn [Wei03]. Da-
mit die Objekte während der gesamten Ausführung der Anwendung migriert
bzw. repliziert werden können, müssen einige technische Vorbereitungen zur
Transformationszeit durchgeführt werden. Zuvor hat der Verteilungsplan die
Entscheidung zu treffen, ob die entstehenden Objekte einer Klasse migrier-
bar bzw. replizierbar sind. Zusätzlich ermittelt er Informationen über das Zu-
griffsverhalten jeder Methode einer Klasse, deren Objekte migriert bzw. repli-
ziert werden können. Für die Transformation ist es notwendig zu erkennen,
ob eine Methode den Zustand des Objekts ändert oder nicht, d.h. ob diese
Methode nur Lesezugriffe oder auch Schreibzugriffe auf die Instanzvariablen
durchführt. Auf der Grundlage der statischen Analyseergebnisse werden sol-
che Eigenschaften möglichst genau berechnet.
Vor der Laufzeit ist Berechnung aller statisch bestimmbaren Eigenschaften
mittels Programmanalyse durchzuführen. Analyseergebnisse werden in den
ODFs zur Transformation und zur Platzierungsberechnung verwendet. Da
der Analyseaufwand die Ausführungszeit des Programms nicht belastet, wer-
den komplexe statische Analyseverfahren eingesetzt, um die Programmei-
genschaften so genau wie möglich zu berechnen.
75
KAPITEL 5. VERTEILUNGSSTRATEGIEN UND VERTEILUNGSPLAN:
KONZEPT UND MODELLIERUNG
5.1.3. Aufgaben zur Laufzeit
Unmittelbar vor dem Start steht die Anzahl der beteiligten JVMs fest. Die-
se Architektureigenschaft wird dem Plan bekannt gegeben. Während der ge-
samten Ausführung des Anwendungsprogramms werden Objekte auf den
beteiligten virtuellen Maschinen erzeugt. Vor jeder Objekterzeugung wird ei-
ne Planevaluierung durchgeführt, welche durch die bei der Transformation
direkt ins Programm eincodierten Anfragen aufgelöst wird. Als Verteilungs-
ergebnis für solch eine Anfrage hat der Plan eine eindeutige Antwort zu lie-
fern, und zwar die Nummer der Zielmaschine, auf der das Objekt erzeugt
wird. Zur Berechnung solcher Verteilungsergebnisse greift der Plan intern
auf die Ergebnisse der Verteilungsstrategien zurück, welche wiederum Er-
gebnisse der ODFs verwenden. Diese Berechnungskette entspricht dem Eva-
luierungsprozess des Planes. Damit die Planevaluation die gesamte Laufzeit
des Programms nicht belastet, müssen solche Ergebnisse großenteils vor der
Laufzeit bestimmt worden sein. Der Verteilungsplan hat folgende Aufgaben
zur Laufzeit zu bewältigen:
Aufgrund der vorliegenden Ergebnisse der ODFs plus einiger sich zur
Laufzeit ergebender Eigenschaften zur Architektur der Laufzeitumge-
bung hat der Verteilungsplan nach dem Start des Programms zu jeder
Anfrage eine eindeutige Antwort zu liefern. Da die Anwendung ver-
teilt auf vielen JVMs ausgeführt wird, die sich physikalisch auf verschie-
denen Rechnern befinden und miteinander über das Netzwerk kommu-
nizieren, muss es gewährleistet sein, dass die berechneten Verteilungs-
ergebnisse des Planes für die gesamte verteilte Anwendung konsistent
und korrekt bleiben. Konzeptionell verhält sich der Verteilungsplan nach
außen wie eine einzige Komponente, die auf Anfrage Entscheidungen
zur Objektverteilung trifft. Dass in der verteilten Laufzeitumgebung auf
jeder JVM eine Instanz des Verteilungsplans vorhanden ist und wie diese
Instanzen und ihre Daten verwaltet werden, ist ein reiner Implementie-
rungsaspekt.
Während der Programmausführung werden einige dynamische Pro-
grammeigenschaften und Informationen zur Architektur und zur ver-
teilten Laufzeitumgebung bestimmt, die zur Verteilung Anwendung fin-
den. Diese neu gewonnenen Erkenntnisse werden auch mit in die Ver-
teilungsentscheidung für die nächsten zu erzeugenden Objekte einbe-
zogen. Der Verteilungsplan hat diese neu hinzugekommenen Daten mit
76
5.2. VERTEILUNGSPLAN: DIE KOMPONENTEN, DEREN
MODELLIERUNG UND VERWENDETE KONZEPTE
den vorhandenen Eigenschaften zu kombinieren. Bei der nächsten Plan-
evaluierung werden die Ergebnisse der ODFs und somit auch die der
Verteilungsstrategien aufgrund der aktualisierten Datenbasis berechnet.
Da Strategien zur Migration und Replikation eingesetzt werden, kön-
nen vorhandene Programmobjekte zur Laufzeit von einer JVM zu einer
anderen migriert oder auf mehreren JVMs repliziert werden. Die Stra-
tegien werden auf die Programmobjekte angewandt, die schon erzeugt
wurden. Vom Prinzip treffen die Strategien die Entscheidung zur Migra-
tion und Replikation aufgrund des Laufzeitverhaltens des Programms
zusammen mit einigen aufgestellten Schwellwerten [Wei03]. Diese Stra-
tegien sind in der Regel unabhängig von denjenigen im Plan. Aber die
Information über migrierte und replizierte Objekte ist auch relevant zur
Verbesserung von Verteilungsentscheidungen für die zukünftig zu er-
zeugenden Objekte. Deshalb werden die Daten über die neuen Aufent-
haltsorte der Objekte den Verteilungsstrategien im Plan mitgeteilt und
fließen in die Verteilungsergebnisse ein.
Alle zusätzlichen Berechnungen, die zur Laufzeit stattfinden, beeinträchtigen
die Ausführungsdauer des Programms. So werden zur Laufzeit Werte zu dy-
namischen Verteilungsvariablen und Architektureigenschaften gebunden, die
bei einer erneuten Berechnung zur verbesserten Verteilung nötig sind. Zuvor
werden Häufigkeitsanalysen eingesetzt, um Ausführungshäufigkeiten oder
konkrete Aufrufziele dynamisch gebundener Methoden zu bestimmen. Au-
ßerdem muss die Verteilungsdatenbank nach jeder Replikation oder Objektmi-
gration aktualisiert werden. Ein Verteilungsplan hat über solche Interaktio-
nen zu informieren und seinen Wissensstand über das laufende Programm
aktuell zu halten. Dieses Wissen fließt in die nächste Verteilungsberechnung
ein.
Nun möchte ich auf die einzelnen Komponenten eines Verteilungsplanes mit
passenden Konzepten und Modellierung eingehen.
5.2. Verteilungsplan: die Komponenten, deren
Modellierung und verwendete Konzepte
Ein Verteilungsplan besteht aus mehreren auf geeignete Weise zusammen-
gesetzten Verteilungsstrategien, wie es in Abbildung 5.3 dargestellt wird. Die
77
KAPITEL 5. VERTEILUNGSSTRATEGIEN UND VERTEILUNGSPLAN:
KONZEPT UND MODELLIERUNG
 !
 "
 #
 !
 "
 $


-.

/0/1
001/3
3 2
34
3
2
3
3
2
34
500
05
Verteilungsstrategien
Zusammensetzung
bestimmter ODFs
Verteilungspläne
Zusammensetzung
bestimmter Strategien


 



%&'
%&&
%(

&((
&
)'
&
&((
(* +'&*
,*( &**
-.

/0/1 2
3
34 3
34
001/3
05
500/1
Funktionen zur Berechnung
der Programmeigenschaften
Laufzeiteigenschaft
Objektverteilungs-
funktionen (ODFs)
Werte-
Bindung
Statische P-Eigenschaft
Dynamische P-Eigenschaft
Verteilungs-
datenbank



Plan-Evaluator Anfragen
Ergebnisse
Migration/Replikation-
Strategien
Plan A Plan B Plan C
060((,)'
Abbildung 5.3.: Das Zusammenwirken aller Komponenten bei Vertei-
lungsplänen
fundamentalen Einheiten eines Verteilungsplanes sind die Objektverteilungs-
funktionen, auch ODFs genannt. Mit deren Hilfe können elementare Vertei-
lungsentscheidungen berechnet werden. Die ODFs benutzen statische und dy-
namische Eigenschaften für ihre Berechnungen. Diese Eigenschaften werden
aus einer mehrsträngigen Java-Anwendung heraus abstrahiert und charakte-
risieren relevante Verteilungsinformation des Programms. Es sind im Wesent-
lichen statische Programm- und Laufzeiteigenschaften. Außerdem spielen die
anwendungsunabhängigen Architektureigenschaften eine wichtige Rolle bei
der Berechnung zur Verteilung. Programm- und Laufzeiteigenschaften wer-
den automatisch durch verschiedene Analyseverfahren berechnet, Architek-
tureigenschaften ergeben sich zur Laufzeit unmittelbar vor der Ausführung
des verteilten Programms. All diese Eigenschaften werden in Form von Varia-
blen innerhalb der ODFs ausgedrückt. Durch das Bindungskonzept können
78
5.2. VERTEILUNGSPLAN: DIE KOMPONENTEN, DEREN
MODELLIERUNG UND VERWENDETE KONZEPTE
sie konkrete Werte zu verschiedenen Zeitpunkten, vor und/oder zur Lauf-
zeit, aufnehmen, die sich aus statischen oder dynamischen Analyseergebnis-
sen ergeben. Sobald alle für die Berechnung einer ODF notwendigen Werte
vorliegen, kann diese ODF ein Verteilungsergebnis liefern.
Durch geeignete Zusammensetzung bestimmter ODFs entsteht eine Vertei-
lungsstrategie. Innerhalb einer Strategie lässt sich das Prinzip der funktiona-
len Abstraktion sehr sinnvoll anwenden: Die Ergebnisse einer ODF können
als Berechnungsparameter oder Bedingungen für die nächsten Berechnungs-
schritte anderer ODFs benutzt werden. Dadurch kann die Abhängigkeit ver-
schiedener ODFs ausgedrückt werden.
Durch geeignete Kombination von bestimmten Strategien erhält man einen
Plan für die automatische Verteilung eines mehrsträngigen Java-Programms.
Die Verteilungseigenschaften sowie die Strategien, damit auch die Pläne wer-
den so allgemein formuliert, dass sie sich auf jedes mehrsträngige Java-
Programm anwenden lassen können. Der Plan-Evaluator bietet die Schnitt-
stelle zur Plananfrage bzw. Planevaluation und liefert Ergebnisse zur Trans-
formation und zur Platzierung der Objekte. Außerdem werden alle getrof-
fenen Entscheidungen zur Verteilung und aktuell nützliche Laufzeitdaten,
die durch die dynamische Analyse zur Laufzeit gesammelt werden, inklusive
Migrations- und Replikationsentscheidungen in einer Datenbank aufgezeich-
net. Sie dienen vor allem als Grundlage zur Berechnung einiger Strategien
zur Laufzeit und zur Berechnung der demnächst zu treffenden bzw. zu ver-
bessernden Verteilungsentscheidungen.
Im Folgenden werde ich auf die formale Definition der Komponenten einzeln
eingehen und stelle ihre Charakteristika und ihre Zusammenhänge dar.
5.2.1. Modellierung von Verteilungsvorschriften als ODFs
Wie im Abschnitt 3.2.2 eingeführt, berechnet eine ODF eine elementare
Transformations- und Verteilungsentscheidung. Ergebnisse einer ODF hän-
gen stark von Werten der darin verwendeten Variablen ab, die die Programm-
und Architektureigenschaften repräsentieren und mittels statischer und dy-
namischer Programmanalyse bestimmt werden. So wird ein Verteilungser-
gebnis durch eine bestimmte Verteilungsstrategie berechnet. Dieses Ergebnis
ist das Resultat aus der Zusammensetzung einer oder mehrerer elementarer
Ergebnisse der ODFs, die in der Strategie verwendet werden. Um ODFs for-
79
KAPITEL 5. VERTEILUNGSSTRATEGIEN UND VERTEILUNGSPLAN:
KONZEPT UND MODELLIERUNG
mal definieren zu können, sollen zunächst einige der verwendeten Begriffe
definiert werden.
Definition 5.0:
Sei OG die Menge aller Objekterzeugungsstellen im gesamten Programm, die
beginnend mit 1 fortlaufend durchnummeriert sind. Es sei Odie Menge aller zur
Laufzeit entstehenden Objekte. Dann sei Oi,j Odas j-te Objekt, das an der
Erzeugungsstelle iOG zur Laufzeit erzeugt wird. Oisei ein Objektrepräsentant
für alle Objekte, die an der Programmstelle iOG erzeugt werden. 4
Da Objekte erst zur Laufzeit erzeugt werden, kann man vor der Laufzeit ver-
schiedene Objekte nicht explizit unterscheiden, die an derselben Stelle im
Programm erzeugt werden. Dies ist zum Beispiel der Fall, wenn eine Erzeu-
gungsstelle in einer Schleife liegt. Diese Objekte werden in äquivalente Klas-
sen eingestuft, die die gleichen statischen Eigenschaften aufweisen, aber un-
terschiedliches Laufzeitverhalten besitzen können.
Für spezielle Objekte wurde der Begriff Aktivität schon im Kapitel 2.3.3 einge-
führt, für die nun definiert wird:
Definition 5.1:
Sei A1Odie Menge aller Aktivitäten, die zur Laufzeit aktiv sind. Astatic sei die
Menge der Aktivitätsrepräsentanten. Eine Aktivität AiAstatic stellt somit einen
Aktivitätsrepräsentanten für alle Aktivitäten dar, die an derselben Programmstelle
iOG erzeugt werden. Dann sei Ai,j Adas j-te Thread-Objekt, das an der
Erzeugungsstelle iOG zur Laufzeit erzeugt wird.
Es seien des Weiteren die Funktionen T hreadId und ObjectId wie folgt definiert, die
jeder Aktivität bzw. jedem Objekt eine eindeutige Identität, hier also eine natürliche
Zahl, zuordnet:
ThreadId :AIN,ObjectId :OIN
4
Es liegt in der Natur der Sache, dass vor der Laufzeit die Beziehung zwi-
schen Objektrepräsentanten und Aktivitätsrepräsentanten mittels statischer
Programmanalyse bestimmt wird. Daher wird zur Berechnung der Verteilung
1Das sind Thread-Objekte im ursprünglichen Programm.
80
5.2. VERTEILUNGSPLAN: DIE KOMPONENTEN, DEREN
MODELLIERUNG UND VERWENDETE KONZEPTE
vor der Laufzeit hauptsächlich die Menge der Aktivitätsrepräsentanten be-
nutzt. Sie werden dann zur Laufzeit zusammen auf verschiedenen Rechner-
knoten platziert. Aus einzelnen Aktivitätsrepräsentanten können zur Laufzeit
konkrete Aktivitäten entstehen, die auf die tatsächlichen JVMs platziert wer-
den. Nun kann eine Definition der ODFs erfolgen.
Definition 5.2:
Sei ndie Anzahl der JVMs, die zur Laufzeit in der verteilten Laufzeitumgebung zur
Programmausführung zur Verfügung stehen. Dann sei AR := {0,...,n-1}die Men-
ge der verfügbaren JVMs. Des Weiteren sei hereein ausgezeichnetes Element, das die
aktuelle JVM zur Laufzeit repräsentiert. Für die Transformationsentscheidung sei die
Menge R:= {remote,noremote}eingeführt. Diese Elemente sagen aus, ob eine
Erzeugungsstelle und ggf. die zugehörige Klasse transformiert werden oder nicht.
Außerdem sei ein Element noans definiert. Es gibt an, dass die vorliegende ODF
zu einer gegebenen Erzeugungsstelle keine Aussage macht. Es sei eine Hilfsfunktion
definiert, die den Aufenthaltsort eines Objektes lokalisiert:
LOCATE :OAR {noans}
mit
LOCATE(Ai)6=noans,AiAstatic
Nun läßt sich eine ODF wie folgt definieren:
fname :O×N
iI
Pi×(OAR {noans})((AR)×R) {noans}
wobei Pidie Menge der Funktionen bezeichnet, die die Programmeigenschaft iIbe-
rechnen. Isei die Indexmenge der Namen aller Programmeigenschaften, die relevant
für die automatische Verteilung sind.
4
Eine Verteilungsfunktion bildet somit die zur Laufzeit erzeugten Objekte, ab-
hängig von darin verwendeten spezifischen Programmeigenschaft(en) und
der LOCATE()-Funktion, auf eine Menge von Kandidaten der Adressräu-
me oder JVMs, zusammen mit dem Hinweis für eine notwendige Trans-
formation ab. Einer ODF wird ein Tupel von konkreten Funktionen, z.B.
(EisProxy, EWork, . . .)N
iI
Pi, übergeben, die alle relevanten Eigenschaften
81
KAPITEL 5. VERTEILUNGSSTRATEGIEN UND VERTEILUNGSPLAN:
KONZEPT UND MODELLIERUNG
mittels Programmanalyse berechnen. Innerhalb der ODF werden die entspre-
chenden Funktionen nach ihren indizierten Eigenschaftnamen aus dieser Ein-
gabe ausgesucht und verwendet. Das berechnete Verteilungsergebnis einer
ODF hängt von diesen Eigenschaften ab. Unter der Verwendung der Hilfs-
funktion LOCATE(), die den Aufenthaltsort eines Objekts oder einer Aktivi-
tät modelliert und ebenfalls als Parameter der ODF übergeben wird, können
relative Zuordnungen der Objekte innerhalb einer ODF berechnet werden.
Wenn an derselben Erzeugungsstelle zwei ODFs verschiedene Ergebnis-
se remote und noremote zur Transformation liefern, dann wird noremote
bevorzugt. Außerdem wird eine Klasse für entfernte Zugriffe und remote-
Erzeugung ihre Objekte transformiert, wenn nur an einer Erzeugungsstelle
von Objekten dieser Klasse im Programm remote vorliegt. Konzeptionell un-
terscheidet sich noans vom remote dadurch, wenn keine Aussage über eine
Programmeigenschaft gemacht werden kann und auch keine Verteilungsent-
scheidung getroffen wird.
Definition 5.3:
Es gibt verschiedene Arten von Objektverteilungsfunktionen:
Totale und partielle Funktion: Eine ODF ist eine partielle Funktion, wenn eine
Eingabe aus dem Definitionsbereich existiert, bei der noans als Ergebnis geliefert
wird. Berechnet eine ODF zu jeder Eingabe ein konkretes Ergebnis ungleich noans,
so ist sie total.
Statische und dynamische Funktion: Eine ODF heißt statisch, wenn alle Werte,
die zur Berechnung der Funktion beitragen, vor der Laufzeit vollständig vorliegen.
Im Gegensatz dazu können bestimmte Programmeigenschaften, die innerhalb einer
ODF verwendet werden, nicht vollständig vor der Laufzeit berechnet, sondern nur
abgeschätzt werden. Solche Funktionen sind dynamisch. Außerdem ist eine Funkti-
on auch dynamisch bzw. statisch, wenn ihre Ergebnisse wiederum von Ergebnissen
anderer dynamischer bzw. statischer Funktionen abhängen.
Konstante Funktion: Eine ODF, die zu jeder Eingabe immer das gleiche Ergebnis
liefert, heißt konstant. 4
Zur Verdeutlichung sollen im Folgenden zwei konkrete ODF-Beispiele darge-
stellt werden.
BEISPIEL 5.0:
Die folgende ODF demonstriert eine Verteilungsvorschrift für die entstehen-
den Objekte, deren Daten ausschließlich von umgebenden Objekten benutzt
82
5.2. VERTEILUNGSPLAN: DIE KOMPONENTEN, DEREN
MODELLIERUNG UND VERWENDETE KONZEPTE
werden, beispielsweise durch Methodenaufrufe. Deshalb bleiben sie nur für
ihre umgebenden Objekte sichtbar und zugreifbar. Derartige Objekte haben
die so genannte Proxy-Eigenschaft. Die ODF für solche Objekte sieht folgen-
dermaßen aus:
fPROXY(Oi, E, LOCAT E):=(({here},noremote)if EisProxy(Oi),
noans else (5.1)
Diese Verteilungsvorschrift nimmt Repräsentanten für Objekte als Eingabe,
die an der Programmstelle ierzeugt werden. Abhängig von der Programmei-
genschaft Proxy, welche mittels einer konkreten Funktion EisProxy berechnet
wird, liefert diese ODF das Ergebnis, dass die Transformation an dieser Stelle
nicht nötig ist und zur Laufzeit die dort entstehenden Objekte nur lokal auf
der JVM erzeugt werden, wo der Programmcode gerade ausgeführt wird.
Dies ist eine partielle Funktion, denn nicht für alle Objekt-Repräsentanten
OiOist die Eigenschaft Proxy erfüllt und somit kann nicht immer ein Ver-
teilungsergebnis ermittelt werden. In diesem Fall wird noans zurückgeliefert.
An der Stelle von Ewird dieser ODF ein Tupel von konkreten Funktionen
zu allen Programmeigenschaften übergeben. EisProxy ist ein Tupelelement,
welches durch den Index isProxyIidentifiziert wird.
Aus dem obigen Beispiel für die ODF fPROXY läßt sich erkennen, dass die Pro-
grammeigenschaft Proxy eine wichtige Rolle spielt. Sie beeinflusst nicht nur
die Entscheidung der Verteilungsfunktion, sondern charakterisiert auch, dass
diese ODF rein statisch ist. Das bedeutet, dass die verwendeten Eigenschaften
vor der Laufzeit durch statische Programmanalyse vollständig berechnet wer-
den können, sodass eine Evaluierung dieser Funktion vor der Laufzeit erfol-
gen kann. Somit läßt sich eine Teilmenge von Objekterzeugungen bestimmen,
deren Platzierung nicht von Laufzeitdaten abhängt. Die Berücksichtigung sol-
cher Verteilungsentscheidungen schon zum Zeitpunkt der Transformation er-
möglicht wieder die direkte Umsetzung der Verteilungsentscheidung in Pro-
grammcode. Zu bemerken ist, dass eine Programmeigenschaft innerhalb ei-
ner ODF über eine Funktion berechnet wird, wie hier die Proxy-Eigenschaft
über die Funktion EisProxy.
Zur Vereinfachung werden im Folgenden die Namen solcher zur Berechnung
der Eigenschaften verwendeten Funktionen direkt als Programmeigenschaf-
ten bezeichnet.
83
KAPITEL 5. VERTEILUNGSSTRATEGIEN UND VERTEILUNGSPLAN:
KONZEPT UND MODELLIERUNG
Das Ergebnis noremote gilt also definitiv für alle Objekte an einer Erzeu-
gungsstelle, die man nicht zu verteilen braucht. fPROXY ist eine der einfachs-
ten ODFs, die zu den Verteilungsentscheidungen beitragen. Im Folgenden sei
eine weitere ODF definiert, die zur Berechnung grundlegender Verteilungs-
entscheidungen für alle Programmobjekte eingesetzt werden kann.
BEISPIEL 5.1:
Durch folgende ODF kann für jedes zu erzeugende Objekt berechnet werden,
mit welcher Aktivität es zusammen platziert wird.
fTHREADWORK(Oi, E, LOCAT E) :=
({LOCAT E(Ak)|EWork(Ak, Oi)EWork(Al, Oi)AlAstatic},remote).(5.2)
Diese Funktion berechnet, dass jedes Oizusammen mit der Aktivität platziert
wird, die die meisten Arbeiten auf ihm verrichtet. Man erkennt im Wesentli-
chen zwei besondere Aspekte bei dieser Funktion: Das resultierende Ergebnis
ist abhängig von der Berechnung einer anderen Funktion (LOCAT E()), wel-
che vor der Laufzeit noch keine Nummer einer konkreten JVM liefern kann.
Trotzdem wird durch die Definition der Funktion LOCAT E() sichergestellt,
dass sich als Ergebnis eine konkrete Zielmaschine ergeben wird, sobald die
konkrete Anzahl der JVMs zur Verfügung steht. So wird eine relative Zuord-
nung der Objekte zu den Aktivitäten modelliert. Diese Zuordnung ist eine
n:m-Relation zwischen nObjekten an der Erzeugungsstelle iund mAktivi-
täten an der Stelle k. Diese Funktion berechnet also noch keine Zusammen-
platzierung einzelner Laufzeit-Objekte bzw. -Aktivitäten, dennoch reicht das
Ergebnis für eine initiale Platzierung aus, deren Berechnung vor der Laufzeit
notwendig ist.
Der zweite Aspekt ist, dass die Verwendung und Bewertung der Programm-
eigenschaft Work mittels einer prädikatenlogischen Formel geschieht. Eine
solche Formel kann genügend effizient vor der Laufzeit berechnet werden,
denn die Menge der Aktivitätsrepräsentanten ist klein und die Menge der Er-
zeugungsstellen ist endlich. Außerdem kann die Programmeigenschaft Work
sowohl vor der Laufzeit mittels statischer Programmanalyse abgeschätzt,
als auch mit Ergänzung der hinzu kommenden Laufzeitdaten während der
Ausführung genauer berechnet werden. Somit lassen sich die Ergebnisse
dieser ODF nicht nur zum Zeitpunkt der Transformation, sondern auch
zur Laufzeit benutzen. Die abgeschätzte Größe der Work-Eigenschaft kann
jederzeit zur Verteilungsentscheidung verwendet werden, solange es keine
Verbesserung durch dynamische Analyse gibt.
84
5.2. VERTEILUNGSPLAN: DIE KOMPONENTEN, DEREN
MODELLIERUNG UND VERWENDETE KONZEPTE
Durch die beiden obigen Beispiele ist deutlich zu erkennen, dass die Varia-
blen, die die Programmeigenschaften modellieren, eine entscheidende Rol-
le in einer Objektverteilungsfunktion besitzen. Das berechnete Verteilungser-
gebnis ist abhängig von den Werten dieser Variablen. Die Modellierung der
elementaren Verteilungsvorschriften als Funktion mit Programmeigenschaf-
ten als Funktionsvariablen und die Auswertung zusammenwirkender Eigen-
schaften mittels prädikatenlogischer Formeln hat viele Vorteile und bietet Fle-
xibilität. Die folgenden Eigenschaften sollen dies deutlicher machen:
Funktionen können sowohl feste als auch relative Zuordnungen model-
lieren. Dies erkennt man deutlich an den beiden Beispielen: Die ODF
fPROXY bildet bestimmte Mengen der Programmobjekte direkt auf die
Menge der Adressräume ab. Derartige Funktionen können schon zur
Transformationszeit zur Planevaluierung benutzt werden. Es ist zu be-
merken, dass es immer besser ist, die Ergebnisse einer Verteilungsfunk-
tion so früh und so weit wie möglich berechnen zu können, also mög-
lichst vor der Laufzeit. Denn dies hat den Vorteil, dass der hohe Berech-
nungsaufwand, vor allem von der statischen Programmanalyse, die Pro-
grammausführung nicht beeinträchtigen wird.
Im Gegensatz dazu ordnet die ODF fTHREADWORK jeder Aktivität eine
bestimmte Menge der zu erzeugenden Objekte zu. Sie liefert nicht den
genauen Adressraum, auf dem die Objekte zur Laufzeit platziert wer-
den. Solche Funktionen können verwendet werden, um Objekte mitein-
ander zu gruppieren. Für eine konkrete Platzierung müssen noch andere
Funktionen mit betrachtet werden.
Funktionen können Abschätzungen der Programmeigenschaften enthal-
ten. Im Gegensatz zu der Programmeigenschaft isProxy, welche vor
der Laufzeit mittels passender statischer Analyseverfahren vollständig
berechnet werden kann, kann die Work-Eigenschaft vor der Programm-
ausführung mit Hilfe der statischen Programmanalyse nur abgeschätzt
werden. Es liegt auch in der Natur der Sache, dass solche Ausführungs-
kosten nicht konkret vor der Laufzeit in irgendeiner Weise berechnet
werden können. Diese Art der Modellierung, wie es am Beispiel der ODF
fTHREADWORK verdeutlicht wurde, ermöglicht es, abgeschätzte Werte
für die Programmvariable Work aufzunehmen und dadurch die zugehö-
rige Funktion vor der Laufzeit berechnen zu können. Außerdem ist diese
Modellierung so flexibel, dass die abgeschätzten Werte später durch ver-
85
KAPITEL 5. VERTEILUNGSSTRATEGIEN UND VERTEILUNGSPLAN:
KONZEPT UND MODELLIERUNG
besserte Werte einfach ersetzt werden können und eventuell neue Ver-
teilungsergebnisse dadurch entstehen.
Funktionen können verschiedene Verteilungsvorschriften formulieren.
Zu einer Objekterzeugungsstelle können mehrere Verteilungsaussagen
gemacht werden. Sie können als einzelne elementare Funktionen formu-
liert und dann bedingt durch die Programmvariablen zueinander in Be-
ziehung gebracht werden. Ein einfaches Beispiel: Zu einer Objekterzeu-
gungsstelle gibt es zwei Vorschriften, modelliert durch zwei verschie-
dene elementare ODFs. Die eine sagt etwas über die darauf verrichtete
Wordload der Aktivitäten aus, wie unsere obige fTHREADWORK. Die an-
dere ODF berechnet die entfernten Kommunikationskosten der Aktivi-
täten, die zur Laufzeit entstehen können. Durch bestimmte Gewichtung
können diese beiden ODFs zusammengesetzt werden und sie liefern
dann eine passende Entscheidung für die Objekte, die an dieser Stelle
erzeugt werden.
Partielle Ergebnisse können modelliert werden. Die Funktionen brau-
chen nicht notwendigerweise konkrete Ergebnisse für jede Eingabe
zu liefern. Es kann beispielsweise vorkommen, dass die eingesetzten
Analyseverfahren zu einer Objekterzeugung die Programmeigenschaft
isProxy nicht bestimmen können. Daher kann die zugehörige ODF
auch keine Verteilungsentscheidung zu dieser Eigenschaft für diese Stel-
le treffen. Solche Verteilungsentscheidungen können außerdem von an-
deren ODFs beziehungsweise bei verwendeten Analyseverfahren bei ih-
rer Durchführung berücksichtigt werden.
Die Auswertung der Funktionen kann sowohl vor als auch zur Aus-
führungszeit erfolgen. Dieses Merkmal ist am Beispiel fTHREADWORK
deutlich zu erkennen. Die statischen Analyseverfahren berechnen Ab-
schätzungen über Laufzeitkosten der Aktivitäten und Objekte vor der
Laufzeit und setzen diese als Werte in die Programmvariablen in den
vorkommenden prädikatenlogischen Formeln ein. Nach der Auswer-
tung dieser Formeln kann die Funktion zu jeder Erzeugungsstelle vor
der eigentlichen Ausführung Verteilungsergebnisse berechnen. Bei der
Formulierung dieser ODF werden auch Faktoren mit modelliert, die
Laufzeitwerte wie die tatsächlichen Ausführungskosten einer Schleife
darstellen. Während der Ausführung ergeben sich realistische Größen
zu Laufzeitkosten. Diese Größen werden dann zur eventuellen erneu-
ten Auswertung der Funktion verwendet, um eine verbesserte Entschei-
86
5.2. VERTEILUNGSPLAN: DIE KOMPONENTEN, DEREN
MODELLIERUNG UND VERWENDETE KONZEPTE
dung für die nächste Erzeugung zu treffen. Diese Möglichkeit verdeut-
licht vor allem das Konzept zur Kombination statischer und dynami-
scher Programmanalysen.
Das Modell, Verteilungsvorschriften als ODFs zu modellieren, ist somit voll-
ständig und sehr flexibel, so dass alle für die Verteilung als relevant gelten-
den Programm- und Laufzeiteigenschaften mitmodelliert werden können. Es
ist aber unabhängig von der Art und Weise, wie und mit welchen Mitteln sie
berechnet werden. Außerdem läßt sich der gesamte Verteilungsplan einfach
erweitern oder ergänzen, indem neue Verteilungsvorschriften hinzukommen
und daraus neue Verteilungsstrategien gebildet werden.
Dieses Kapitel stellt nur die Verwendung der Programm- und Laufzeiteigen-
schaften als solche in den hier vorgestellten ODFs und Strategien vor. Wie
die Programm- und Laufzeiteigenschaften als Variablen in solchen Formeln
modelliert werden und wie sie vor sowie zur Laufzeit ausgewertet werden,
werde ich im Kapitel 6 beschreiben.
5.2.2. Modellierung der Verteilungsstrategien
Verteilungsstrategien sind im Prinzip auch Funktionen. Sie bestehen aus ei-
ner oder mehreren bestimmten Objektverteilungsfunktionen, die bedingt in
einer festgelegten Reihenfolge ausgewertet werden. Eine Strategie trifft ei-
ne bestimmte strategische Entscheidung über die zu verteilenden Objekte
bzw. Aktivitäten. Abhängig von den darin benutzten ODFs kann eine Ver-
teilungsstrategie das Ergebnis liefern, dessen Güte durch abgeschätzte Wer-
te der Programm- bzw. Laufzeiteigenschaften oder auch exakte Werte be-
stimmt wird. Die Auswertung der Strategien kann sowohl vor der Laufzeit
als auch zur Laufzeit erfolgen. Anders als in den ODFs sind die verwendeten
Eigenschaften nicht direkt in den Strategien sichtbar. Außerdem besteht ei-
ne Schnittstelle zwischen einer Strategie und der Verteilungsdatenbank. Da-
durch können vorher getroffene Entscheidungen, die in der Datenbank ge-
speichert sind, für die Berechnung innerhalb einer Strategie verwendet wer-
den.
Definition 5.4:
Mit den oben definierten Notationen sei eine Strategie wie folgt definiert:
strategyname :O×N
iI
Pi×(OAR {noans})((AR)×R) {noans}
87
KAPITEL 5. VERTEILUNGSSTRATEGIEN UND VERTEILUNGSPLAN:
KONZEPT UND MODELLIERUNG
Wie eine ODF kann eine Strategie auch total oder partiell sein. Eingegeben werden
Objektrepräsentanten, ein Tupel von Funktionen, die alle zur Verteilung relevanten
Eigenschaften bestimmen, und eine Funktion zur Lokalisierung der Objekte. Diese
werden innerhalb einer Strategie mittels einiger ODFs berechnet und entsprechend
der Strategie wird ein Verteilungsergebnis zurückgeliefert. 4
Nach Definition ist eine Verteilungsstrategie nichts anders als eine Objekt-
verteilungsfunktion. Sie besitzt die gleiche Signatur wie ODF. Konzeptionell
fungiert eine Verteilungsstrategie bei der automatischen Verteilung als eine
strukturelle Zusammensetzung von einzelnen konkreten ODFs. Man hat da-
durch den Vorteil, ODFs mit ähnlicher strategischer Bedeutung für die Vertei-
lung zusammen zu gruppieren und anzuwenden. Dies soll durch das folgen-
de Beispiel verdeutlicht werden.
BEISPIEL 5.2:
Es sei eine grundlegende Strategie definiert, die eine lokale Platzierung für
bestimmte Objekte berechnet (informelle Beschreibung):
strategyLOCAL: Zu einer gegebenen Objekterzeugungsstelle wird mittels einer
Reihe von ODFs berechnet, ob die dort entstehenden Objekte lokale Eigen-
schaften besitzen und die zugehörigen Erzeugungsstellen deshalb nicht trans-
formiert zu werden brauchen und dann nur lokal erzeugt werden. In die-
ser Strategie werden die ODFs zur Überprüfung der Proxy-, ThreadLocal-
und der pure-immutable-Eigenschaften angewandt. Die zu replizierenden
Objekte werden dort lokal erzeugt, wo auf sie zugegriffen wird.
Alle diese ODFs richten sich nach statischen Programmeigenschaften der
zu erzeugenden Objekte. Sie können vor der Laufzeit mittels statischer
Programmanalyse vollständig bestimmt oder konservativ abgeschätzt wer-
den, so dass sie während der gesamten Programmausführung unverändert
bleiben. Objekte, für die diese Strategie erfolgreich angewandt wurde, brau-
chen nicht mehr weiter betrachtet zu werden. Ansonsten müssen andere
Verteilungsstrategien auf die Objektrepräsentanten angewandt werden. Das
Ergebnis dieser Strategie ist entweder noremote oder noans.
Es gibt verschiedene Arten von Strategien zur Verteilung der Aktivitäten und
Nicht-Thread-Objekte, wie es in Abbildung 5.3 schon grob angedeutet wurde.
Definition 5.5:
Insgesamt werden Strategien in drei Kategorien aufgeteilt, abhängig von den darin
verwendeten ODFs:
88
5.2. VERTEILUNGSPLAN: DIE KOMPONENTEN, DEREN
MODELLIERUNG UND VERWENDETE KONZEPTE
Statische Strategien: Sie liefern feste Verteilungsentscheidungen für entstehende
Objekte vor der Laufzeit. Diese Entscheidungen bleiben dann während der gesamten
Ausführung des Programms unverändert. Ergebnis solcher Strategien ist entweder
noremote oder noans.
Basis-Strategien: Ihre vor der Laufzeit berechneten Entscheidungen können wäh-
rend der Programmausführung aufgrund erneuter Berechnung verändert werden.
Das Endergebnis einer Basis-Strategie darf nicht noans sein.
Dynamische Strategien: Hierbei werden Platzierungsentscheidungen während der
Laufzeit getroffen. Ergebnisse der darin enthaltenen ODFs basieren auf Laufzeitda-
ten. 4
Bei statischen Strategien hängen die Ergebnisse der darin verwendeten ODFs
nicht von veränderlichen Laufzeiteigenschaften ab, sondern höchstens von
den Laufzeitkonstanten. Die Funktionen dieser Kategorie sind partiell. Exem-
plarisch für solche Strategien ist strategyLOCAL im Beispiel 5.2. Im Gegensatz
dazu sind die Basis-Strategien totale Funktionen, d.h. sie müssen zu jeder Ein-
gabe eine strategische Entscheidung liefern und können im Prinzip auch ohne
Kombination mit Strategien anderer Klasse angewandt werden.
Abhängig von der Reihenfolge der angewandten ODFs kann eine Entschei-
dung zu einer Objekterzeugungsstelle eine früher schon getroffene Entschei-
dung ungültig machen. Dies betrifft vor allem Entscheidungen, die von Basis-
Strategien und dynamischen Strategien geliefert werden. Ausnahmsweise gilt
das nicht für Entscheidungen zu Objekten, die nicht remote sein sollen: Wenn
eine ODF innerhalb einer statischen Strategie noremote liefert, wird die Aus-
wertung anderer ODFs abgebrochen und dies gilt als Endergebnis der Strate-
gie. Das heißt, dass solche Platzierungsentscheidungen erhalten bleiben und
nicht überschrieben werden.
Ein Beispiel für solche Überschreibungsphänomene ist die Migrationsstra-
tegie: Eine Basis-Strategie lieferte das Ergebnis, dass Objekte an der Erzeu-
gungsstelle jOG zusammen mit der Aktivität Aiplatziert werden. Zur
Laufzeit werden diese Objekte auf der JVM kerzeugt. Durch die Migration
werden diese Objekte auf eine andere Maschine umplatziert. Ab diesem Zeit-
punkt gilt für die Objekte Ojnur noch die neue Entscheidung, bis das Pro-
gramm zu Ende läuft.
Die Auswertung einer Strategie impliziert das Auswerten einzelner Vertei-
lungsfunktionen innerhalb dieser Strategie. Hierbei werden alle verwendeten
Programmeigenschaften zur Berechnung der ODFs bestimmt. Deren Werte,
89
KAPITEL 5. VERTEILUNGSSTRATEGIEN UND VERTEILUNGSPLAN:
KONZEPT UND MODELLIERUNG
inklusive abgeschätzter Werte, werden für die Auswertung der enthaltenen
Formeln eingesetzt und schließlich wird die einzelne ODF berechnet.
5.2.3. Modell eines Verteilungsplanes
Ein Verteilungsplan entsteht durch strukturierte Zusammensetzung be-
stimmter Verteilungsstrategien in einer geeigneten Reihenfolge, wie es in Ab-
bildung 5.3 dargestellt wurde. Die Aufgabe eines Verteilungsplans ist es, vor
der Laufzeit zu jeder Objekterzeugungsstelle die Entscheidung über deren
Transformation und über eine initiale Verteilung zu liefern sowie zur Lauf-
zeit eine konkrete Zielmaschine zu jeder Objekterzeugung und zur Migration
bzw. Replikation zu ermitteln.
Definition 5.6:
Ein Verteilungsplan sei wie folgt definiert:
planname :O×N
iI
Pi×(OAR {noans})(AR ×R)
4
Ein Verteilungsplan muss im Prinzip zwei Berechnungsphasen durchführen:
In der ersten Phase, vor der Laufzeit, müssen die Ergebnisse für die Trans-
formation und für eine initiale Verteilung berechnet werden. Die Berechnung
einer konkreten Zielmaschine als Platzierungsort zu einer Objekterzeugung
gehört zur zweiten Phase zur Laufzeit, wobei die Ergebnisse der ersten Phase
als Grundlage für die Berechnung dienen.
In der zweiten Phase werden tatsächliche Objekte an Erzeugungsstellen er-
zeugt und es liegen die erforderlichen Informationen vor. Das sind beispiels-
weise die Anzahl der JVMs und die Identitäten der erzeugten Objekte. Mittels
der Planabfrage bei einer Objekterzeugung wird der Zielort als Verteilungs-
ergebnis zurückgeliefert.
Planevaluation
Die Anwendung der Funktion planname(. . .)entspricht der Planevaluation.
Dieser Prozess impliziert die Berechnungen der darin enthaltenen Strategien
90
5.2. VERTEILUNGSPLAN: DIE KOMPONENTEN, DEREN
MODELLIERUNG UND VERWENDETE KONZEPTE
und ODFs sowie das Bestimmen der nötigen Eigenschaften zur Platzierungs-
entscheidung. Zur Programmtransformation und zur Berechnung der initia-
len Verteilung wird ein Verteilungsplan vor der Laufzeit evaluiert. Das wird
als statische Evaluation bezeichnet. Während der Programmausführung wird
der Plan nach einem konkreten Platzierungsort abgefragt. Dies entspricht der
dynamischen Evaluation eines Planes.
Bei der statischen Evaluation nimmt ein Verteilungsplan Objektrepräsentant-
en Oi, die Folge aller Programmeigenschaften und LOCATE als Eingabe. Bei
der initialen Verteilung vor der Laufzeit müssen zuerst die Aktivitätsreprä-
sentanten auf die abstrakten JVMs abgebildet worden sein, bevor irgendwel-
che Verteilungsberechnungen der normalen Objektrepräsentanten durchge-
führt werden. Bei der statischen Evaluation werden ebenfalls an bestimm-
ten Programmstellen passende Codesegmente in das Programm eintransfor-
miert: An der Erzeugungsstelle zu Oienthält der eintransformierte Code die
Planabfragen für die spätere dynamische Evaluation. Außerdem können In-
strumentierungscode oder andere Arten von Anfragen in das Programm ein-
gebunden werden, beispielsweise zur Objektmigration oder -replikation.
Zur Laufzeit wird mittels der eingebauten Anfragen die dynamische Evalua-
tion des Verteilungsplans ausgelöst. Zu diesem Zeitpunkt sind die Laufzeit-
und Architektureigenschaften schon bekannt, beispielsweise die Anzahl der
zur Verfügung stehenden JVMs. Daher kann bei der Evaluation der Vertei-
lungsplan zu jeder einzelnen Objekterzeugung eine konkrete Zielmaschine
liefern, auf der das Objekt erzeugt wird.
Ein Beispiel
Im Folgenden wird ein Beispiel vorgestellt, das das Konzept der statischen
und dynamischen Planevaluation durch die Anwendung der oben definierten
Funktionen verdeutlicht. Gegeben sei ein vereinfachtes mehrsträngiges Java-
Programm, das im Folgenden im Programmausdruck 1 zu finden ist.
Zur Vereinfachung der Beschreibung werden Objekt- bzw. Aktivitätsreprä-
sentanten mit den Namen der Variablen bezeichnet, die ihre Referenzen ent-
halten. In der main-Methode der Klasse Main werden zwei Aktivitäten a1
und a2 erzeugt. Je nach Bedingung operiert a1 oder a2 auf o, das entweder
an der Stelle 28 oder 32 erzeugt wird. owird ebenfalls als Parameter an die
Konstruktor-Methode zur Erzeugung eines Objekts ban der Stelle 36 über-
91
KAPITEL 5. VERTEILUNGSSTRATEGIEN UND VERTEILUNGSPLAN:
KONZEPT UND MODELLIERUNG
public class Activity1 extends Thread
{...
public void set(Data d) { ... }
}5
public class Activity2 extends Thread
{...
public void setData(Data d) { ... } 10
}
public class Data { ... }
public class OtherData 15
{
public OtherData(Data d) { ... }
}
public class Main 20
{
public static void main(String[] args) {
Activity1 a1 =new Activity1(); // {JVM1} /* {jvm0} */
Activity2 a2 =new Activity2(); // {JVM2} /* {jvm1} */
... 25
if (...) {
o=new Data(42); // { LOCATE(a1) } /* {jvm0} */
a1.set(o);
}30
else {
o=new Data(43); // { LOCATE(a2) }
a2.setData(o);
}
35
OtherData b =new OtherData(o); // { LOCATE(o 28) , LOCATE(o 32) } /* {jvm0} */
a1.start(); a2.start();
}
}40
Listing 1: Verteilungsentscheidungen mittels statischer und dynamischer
Evaluation
92
5.2. VERTEILUNGSPLAN: DIE KOMPONENTEN, DEREN
MODELLIERUNG UND VERWENDETE KONZEPTE
geben. Es soll des Weiteren ein Verteilungsplan mit folgenden Strategien zur
Verteilung dieser Anwendung verwendet werden (informell beschrieben):
Die Objekte werden nach ihren lokalen Eigenschaften untersucht und so entspre-
chend verteilt. Dies entspricht der Strategie strategyLOCAL, die im Beispiel 5.2 be-
schrieben wurde. Anschließend werden die gefundenen Aktivitäten einfach zyklisch
auf die JVMs verteilt. Alle restlichen Objekte werden nach der Workload-Strategie
verteilt. Die statische Evaluation dieses Verteilungsplans liefert folgende Er-
gebnisse: Alle erzeugten Objekte sind remote, d.h. alle erforderlichen Trans-
formationsschritte werden durchgeführt. Die initialen Verteilungsergebnis-
se werden als Kommentare an jeder Erzeugungsstelle angegeben: Die bei-
den Aktivitäten können zyklisch auf die abstrakten JVMs, in diesem Fall
JV M1, JV M2AR verteilt werden. Es wurde außerdem festgestellt, dass die-
se Aktivitäten jeweils auf den Objektrepräsentanten oan der Stelle 28 bzw.
32 die meisten Workloads verrichten (mittels der Methode a1.set(o) bzw.
a2.setData(o)). Deshalb wird ozusammen mit a1bzw. a2durch den Hin-
weis LOCATE(a1) bzw. LOCAT E(a2) platziert. Der Objektrepräsentant bope-
riert ebenfalls mit o, deshalb wird das dort erzeugte Objekt zusammen mit
dem gerade an der Stelle von o(28 oder 32) erzeugten Objekt verteilt wer-
den. Da zu diesem Zeitpunkt noch nicht festgestellt werden kann, welches
ogenau erzeugt wird, wird für bnur ein Lösungsvorschlag ausgerechnet, al-
so {LOCATE(o_28), LOCATE(o_32)}. Im Allgemeinen werden solche Lösungs-
vorschläge auch nach dem Schema der Datenflussabhängigkeit, beispiels-
weise zwischen bund o, berechnet.
Vor der Laufzeit werden außerdem Abhängigkeiten zwischen Verteilungslö-
sungen der verschiedenen Erzeugungsstellen aufgelöst. Das bedeutet, dass
für onicht mehr {LOCATE(a1)}bzw. {LOCAT E(a1)}, sondern {JV M1}bzw.
{JV M2}und für bdie Lösungsmenge {JV M1, JV M2}als Verteilungsergebnis
zur Verfügung stehen.
Zur Laufzeit seien zwei JVMs zur verteilten Ausführung verfügbar. So wer-
den die abstrakten JVMs JV M1und JV M2auf die beiden echten Maschi-
nen abgebildet, diese seien jvm0und jvm1. Die Aktivitäten a1und a2werden
entsprechend auf diesen Maschinen erzeugt. Während der Programmausfüh-
rung sei die Bedingung der if-Abfrage erfüllt, d.h. das Objekt an der Stelle 28
wird auf der JVM jvm0erzeugt und mit einer Objektidentität ObjectId(o_28)
gekennzeichnet. Diese Identität und die Information über den Aufenthaltsort
des o_28 sind die dynamischen Programmeigenschaften, die sich zur Laufzeit
ergeben und von der Datenbank verwaltet werden. Somit läßt sich ein kon-
kreter Verteilungshinweis für das zu erzeugende Objekt baus der statischen
93
KAPITEL 5. VERTEILUNGSSTRATEGIEN UND VERTEILUNGSPLAN:
KONZEPT UND MODELLIERUNG
Lösungsmenge bestimmen, dass es mit dem gerade erzeugten Objekt o(er-
kannt durch die Objekt-Id) zusammen platziert wird, also auf jvm0. Die Ver-
teilungshinweise, die der Plan für einzelne Objekterzeugungen ermittelt, sind
an den Erzeugungsstellen im Programmcode (Listing 1) in den Kommentaren
(/* */) angegeben.
Die Verwendung von Funktionen höherer Ordnung, um die Verteilungspläne
zu modellieren, ermöglicht die Evaluation eines Planes vor der Laufzeit und
zur Laufzeit. Initiale Verteilungsergebnisse dienen hauptsächlich als Grund-
lage zur Berechnung konkreter Platzierungsentscheidungen. Zur Verteilung
der Objekte zur Laufzeit werden zusätzlich einige dynamische Programmei-
genschaften wie die Identität der Objekte bzw. der Aktivitäten benötigt.
Alternative Verteilungspläne
Mit diesem Modell ist es möglich, verschiedene Verteilungspläne zu formulie-
ren, die auf denselben elementaren ODFs beruhen. Sie unterscheiden sich dar-
in, dass jeweils verschiedene Verteilungsstrategien auf unterschiedliche Wei-
se verwendet werden. Die Ergebnisse der initialen Verteilung, insbesondere
der Transformation vor der Laufzeit und die konkrete Verteilung zur Laufzeit
sind daher je nach verwendetem Plan auch unterschiedlich. Somit kann man
für eine Anwendung verschiedene Pläne mit unterschiedlichen Verteilungs-
strategien einsetzen.
5.2.4. Andere Komponenten
Neben den Verteilungsplänen gibt es noch zwei andere Komponenten: den
Plan-Evaluator und die Verteilungsdatenbank. Im Folgenden werden ihre
Funktionalitäten im Zusammenhang mit einem Verteilungsplan vorgestellt.
Plan-Evaluator
Diese Komponente dient hauptsächlich als Schnittstelle zur Kommunikation
mit den Verteilungsplänen, ist aber mit diesen sehr eng gekoppelt. Bei Anfra-
gen, beispielsweise vom Programmtransformator, hat der Plan-Evaluator die
Eingaben „richtig umzuformulieren“ und leitet dies an einen entsprechenden
Plan weiter. Dies löst den Evaluationsprozess aus. Da ein Verteilungsplan nur
die Aufgabe hat, Platzierungsentscheidungen für Objekte zu treffen, muss der
94
5.3. GRUNDLEGENDE OBJEKTVERTEILUNGSFUNKTIONEN
Evaluator zusätzlich zu einem Ergebnis Codesegmente zur Planabfrage für
die Transformation konstruieren und zurückliefern. Mittels der eingebauten
Anfragen im Programm wird der verwendete Plan auch über den Evaluator
dynamisch evaluiert.
Verteilungsdatenbank
Die Aufgabe der Verteilungsdatenbank besteht darin, alle strategischen Ent-
scheidungen zur Verteilung der Objekte und Aktivitäten sowie Vorabberech-
nungen zur Objektmigration bzw. -replikation, die vor der Laufzeit gefal-
len sind, aufzubewahren. Vor der Laufzeit können eingetragene Datensät-
ze zur Berechnung weiterer Strategien wiederverwendet werden. Alternative
Entscheidungen zur Objektverteilung, die aufgrund fehlender Laufzeiteigen-
schaften vor der Laufzeit nicht vollständig berechnet werden können, werden
ebenfalls in die Datenbank aufgenommen. Zur Laufzeit werden einige Strate-
gien erneut berechnet, die Entscheidungen zu offenen Fragen liefern sollen.
5.3. Grundlegende Objektverteilungsfunktionen
Im vorherigen Abschnitt wurden zwei konkrete ODFs eingeführt, die dort
vorwiegend als Beispiele dienen. Die Komplexität einer ODF hängt stark von
den verwendeten Formeln ab, die Zusammenhänge der Eigenschaften mo-
dellieren und als Bedingung zur Berechnung der Verteilungsergebnisse ver-
wendet werden. In diesem Abschnitt werde ich einige grundlegende Ob-
jektverteilungsfunktionen vorstellen, bei denen alle relevanten Programmei-
genschaften zur automatischen Verteilung verwendet werden und die aus-
reichen, um daraus gute Verteilungsstrategien entwickeln zu können. Die
hier vorgestellten ODFs gliedern sich in drei verschiedene Gruppen: stati-
sche ODFs, Basis-ODFs und dynamische ODFs. Wie im vorherigen Abschnitt
schon angemerkt, werde ich die hierbei zum Einsatz kommenden Programm-
und Laufzeiteigenschaften nur verwenden, aber nicht auf Details eingehen.
Diese finden sich im nächsten Kapitel.
Als Eingabe für die ODFs wird ein Objektrepräsentant Oiverwendet. Damit
ist gemeint, dass er für alle zur Laufzeit an der Erzeugungsstelle iOG zu
erzeugenden Objekte gilt, wenn die ODFs vor der Laufzeit evaluiert werden.
Hiermit erreicht man eine initiale Verteilung für alle Objekt- und Aktivitäts-
repräsentanten. Zur Laufzeit kann eine ODF verschiedene Verteilungsergeb-
95
KAPITEL 5. VERTEILUNGSSTRATEGIEN UND VERTEILUNGSPLAN:
KONZEPT UND MODELLIERUNG
nisse für die Objekte ermitteln, die an derselben Erzeugungsstelle ierzeugt
werden. Für die Hilfsfunktion LOCAT E gilt das Entsprechende.
5.3.1. Statische Verteilungsfunktionen
Es ist allen statischen ODFs gemein, dass sie schon endgültige Verteilungser-
gebnisse vor der Laufzeit liefern können. Das bedeutet, dass man die Werte
für die verwendeten Verteilungsvariablen mittels statischer Analysetechniken
ausrechnen und die Formeln vollständig auswerten kann. Außerdem sind al-
le statischen ODFs partielle Funktionen. Für Objekte, die keine dieser Eigen-
schaften erfüllen, können auch keine Verteilungsergebnisse geliefert werden.
Objekte mit der Proxy-Eigenschaft
Wie schon beschrieben sollen Erzeugungsstellen, aus denen Objekte mit der
Proxy-Eigenschaft entstehen, statisch erkannt werden. Diese Stellen bleiben
dann vom Transformationsprozess unberührt und die erzeugten Objekte wer-
den dort lokal erzeugt, wo der Programmcode ausgeführt wird. Zur Erinne-
rung wird hier nochmals die Funktionsdefinition angegeben:
fPROXY(Oi, E, LOCAT E) :=
(({here},noremote)if EisProxy(Oi),
noans else (5.3)
Ein späterer Umzug dieser Objekte auf eine andere JVM während der Pro-
grammausführung ist nur möglich, wenn die umgebenden Objekte durch
Migration in einen anderen Adressraum umplatziert oder auf mehrere JVMs
repliziert werden. In diesem Fall werden die Proxy-Objekte zusammen mit
ihren umgebenden Objekten umgezogen oder repliziert, wobei ihre Proxy-
Eigenschaft immer erhalten bleibt. Die Programmeigenschaft isProxy für
ein Objekt ist eine statische Eigenschaft und wird vor der Laufzeit mittels ge-
eigneter Analyseverfahren auf konservative Weise ausgerechnet.
Stagewise-immutable Objekte
Objekte, die im ganzen Programmkontext oder ab einem bestimmten Zeit-
punkt während der Ausführung nur lesend zugegriffen werden, werden als
96
5.3. GRUNDLEGENDE OBJEKTVERTEILUNGSFUNKTIONEN
stagewise-immutable gekennzeichnet. Ab dem Zeitpunkt, wann sie nur lesend
zugreifbar, können sie dann überall repliziert werden, wo Zugriffe darauf
stattfinden. Nach der Replikation wird auf diese Objekte nur lokal zugegrif-
fen und eine Synchronisation zwischen ihnen ist auch nicht erforderlich. Die
stagewise-immutable Eigenschaft gliedert sich in zwei Stufen, die im Folgenden
vorgestellt werden. Auf Details über diese Eigenschaft sowie über das stati-
sche Analyseverfahren dazu wird ausführlich im Abschnitt 6.4 eingegangen.
Rein-konstante Objekte (pure-immutable-Eigenschaft) Objekte mit die-
ser Eigenschaft werden nach ihrer Initialisierungsphase, also nachdem eine
Konstruktor-Methode zur Objekterzeugung vollständig abgearbeitet wurde,
nur gelesen. Hiernach können sie repliziert werden. Es werden auch keine
entfernten Zugriffe für diese Objekte benötigt. Die Definition dieser ODF sieht
folgendermaßen aus:
fPURE-IMMUTABLE(Oi, E, LOCATE) :=
((M, noremote)if EisPureImmutable(Oi)
noans else (5.4)
wobei M={LOCAT E(Oj)|EhasAccess(Oj, Oi)LOCAT E(Oj)6=noans}.
Zu einer Objekterzeugungsstelle kann mittels dieser ODF bestimmt werden,
ob die dort entstehenden Objekte die pure-immutable-Eigenschaft besitzen.
Wenn dies der Fall ist, so werden die Objekte dorthin erzeugt, wo andere Ob-
jekte Zugriffe auf sie haben. Die Programmeigenschaften isPureImmutable
und hasAccess sind ebenfalls statische Eigenschaften, die durch passen-
de statische Analysetechniken bestimmt werden. Mit Hilfe dieser Funktion
können Erzeugungsstellen zur Transformationszeit erkannt werden, die nicht
transformiert zu werden brauchen.
Postponed-immutable Objekte Die Programmeigenschaft isPureImmu-
table zu einem Objekt besagt, dass es während seiner gesamten Lebensdau-
er nur lesend zugegriffen wird. Mit der postponed-immutable Eigenschaft ist ein
Objekt ab einem Zeitpunkt Tgenau dann immutable, wenn alle Zugriffe nach T
nur lesend sind. Somit können vor der Laufzeit mittels statischer Analysever-
fahren Erzeugungsstellen von solchen postponed-immutable Objekten und zu-
sätzlich Programmstellen erkannt werden, die Replikationspunkte bezeich-
nen. Dann können Replikationsmechanismen an solchen Stellen eingebaut
97
KAPITEL 5. VERTEILUNGSSTRATEGIEN UND VERTEILUNGSPLAN:
KONZEPT UND MODELLIERUNG
werden und zur Laufzeit können die Objekte ab diesen Zeitpunkt dorthin
repliziert werden, wo auf sie zugegriffen wird.
Objekte, die diese Eigenschaft besitzen, müssen vor ihrer Replikation auch
wie alle anderen verteilt werden. Das bedeutet, dass Verteilungsberechnun-
gen für die zugehörigen Erzeugungsstellen durchgeführt werden müssen.
Nachdem Platzierungsergebnisse für alle Erzeugungsstellen im Programm
vorlagen, werden diese nach der postponed-immutable Eigenschaft überprüft.
So können Programmstellen für Objektrepräsentanten mit dieser Eigenschaft
erkannt werden, die die Replikationspunkte darstellen. Dort werden erfor-
derliche Transformationsschritte durchgeführt. All diese Schritte finden vor
der Laufzeit statt.
Nachdem die Objekte zur Laufzeit repliziert wurden, sind auf sie auch keine
entfernten Zugriffe mehr nötig. Prinzipiell können sie aber von ihrer Erzeu-
gung bis zu diesem Replikationspunkt entfernt zugegriffen werden. Daher
können solche Objekte auch remote sein.
Objekte mit Peer- oder Hardware-nahen Eigenschaften
(anwendungsspezifische Muster)
Objekte, die Hardware-Ressourcen benutzen, z.B. Grafiken zeichnen, Maschi-
nencode (native code) ausführen oder Interaktion mit Benutzern durchführen,
sind eng mit dem Adressraum verbunden, auf dem sie erzeugt werden. Ei-
ne Verteilung solcher Objekte würde ihre semantische Bedeutung verletzen.
Deshalb sollen sie nach ihrer Erzeugung auch dort bleiben und man braucht
auch keine Transformation für solche Erzeugungsstellen durchzuführen. Die
Funktionsdefinition dafür ist wie folgt:
fPEER(Oi, E, LOCAT E) :=
(({here},remote)if EisPeer(Oi),
noans else (5.5)
Aus dem ersten Blick sieht diese ODF sehr einfach aus, aber die Berechnung
der Programmeigenschaft isPeer ist eine sehr komplexe Aufgabe. Denn jede
Art der Benutzung der Hardware-Ressourcen stellt ein anwendungsspezifi-
sches Muster dar und all diese Muster fallen in die Kategorie Peer-Eigenschaft.
Das Transformationsergebnis remote besagt in diesem Fall, dass die zugehö-
rige Klasse transformiert wird. Der Grund ist, dass die Objekte zur Laufzeit
direkt here erzeugt werden, ihre Methoden jedoch auch entfernt zugreifbar
98
5.3. GRUNDLEGENDE OBJEKTVERTEILUNGSFUNKTIONEN
sind. Dennoch ist dies eine statisch bestimmbare Programmeigenschaft und
man kann sie mit Hilfe der statischen Programmanalyse vollständig ausrech-
nen.
Thread-lokale Objekte
Es handelt sich hierbei um Objekte, die von einem Thread erzeugt werden
und nur von diesem benutzt werden. Aus der Sicht der Programmanalyse
entkommen diese Objekte dem Thread nicht, d.h. sie besitzen den non-escape-
Status bezüglich des Threads. Der Ansatz besteht darin, solche Objekte mit
dem Thread zusammen in einem Adressraum zu platzieren, bezüglich dem
sie lokal sind. Die ODF hierfür ist:
fTHREADLOCAL(Oi, E, LOCAT E) :=
((M, noremote)if M6=,
noans else (5.6)
wobei M={LOCAT E(Aj)|EisThreadLocal(Aj, Oi)}.
Vor der Laufzeit kann zu einer Objekterzeugungsstelle geprüft werden, ob es
einen Thread gibt, bei dem die an dieser Stelle erzeugten Objekte thread-lokal
sind. Hierfür kann das statische Analyseverfahren Escape-Analyse eingesetzt
werden, um die Programmeigenschaft isThreadLocal zu berechnen. Der
wesentliche Unterschied zwischen dieser ODF und der fPROXY besteht darin,
dass die Proxy-Objekte innerhalb ihrer umgebenden Objekte erzeugt werden
und keine entfernte Zugriffsmöglichkeit brauchen. Die isProxy-Eigenschaft
ist somit schärfer als die isThreadLocal-Eigenschaft.
Es wird hierbei gewährleistet, dass das Ergebnis der Funktion LOCAT E auf
einer Aktivität nicht noans ist. Denn in diesem Ansatz werden zunächst die
Aktivitäten auf die verfügbaren JVMs verteilt, dann folgen die Nicht-Thread-
Objekte.
5.3.2. Basis-Verteilungsfunktionen
Anders als bei den statischen Funktionen lassen sich die Verteilungsvaria-
blen, die die verwendeten Programm- und Laufzeiteigenschaften in den Ver-
teilungsfunktionen dieser Kategorie modellieren, vor der Laufzeit nicht voll-
ständig ausrechnen. Die Analysetechniken müssen, wenn es nötig ist, die
99
KAPITEL 5. VERTEILUNGSSTRATEGIEN UND VERTEILUNGSPLAN:
KONZEPT UND MODELLIERUNG
Werte dieser Variablen abschätzen, damit eine initiale Verteilungslösung ge-
funden werden kann. Ergebnisse einiger dieser Eigenschaften können sich
zur Laufzeit noch verbessern lassen. Die Basis-ODFs können auch partielle
Funktionen sein und die Objekte, die nach diesen ODFs verteilt werden, sind
alle remote-Objekte. Das bedeutet, dass eine Transformation für deren Erzeu-
gungsstellen notwendig ist.
Konstruktorparameter
Man betrachtet hierbei eine Erzeugungsstelle, bei der eine Konstruktor-
Methode zur Objekterzeugung aufgerufen wird und eine Objektreferenz als
Parameter übergeben bekommt. Es wird dann davon ausgegangen, dass die
an dieser Stelle erzeugten Objekte auf jeden Fall mit dem übergebenen Objekt
über seine Referenz interagieren. Für eine Verteilung bedeutet dies, dass die
zu erzeugenden Objekte mit dem übergebenen Objekt zusammen platziert
werden können. Diese Programmeigenschaft kann vor der Laufzeit bestimmt
werden. Es ergibt sich somit folgende Definition:
fCONSTRPARAM(Oi, E, LOCATE) :=
((M, remote)if M6=
noans else (5.7)
wobei M={LOCAT E(Oj)|EhasParameter(Oi, Oj)LOCAT E(Oj)6=noans}.
Diese ODF funktioniert aber nur für solche Erzeugungsstellen, bei denen der
Konstruktor mit einem einzigen Referenz-Parameter aufgerufen wird. Basie-
rend auf diesem Ansatz kann man die ODF erweitern, so dass sie für beliebig
viele Parameter mit Referenztypen funktioniert. Dies läßt sich in folgender
Funktion zusammenfassen:
fEX_CONSTRPARAM(Oi, E, LOCATE) :=
(M1,remote)if M16=
(M2,remote)if M1= M26=
noans else
(5.8)
wobei:
M1={LOCAT E(Aj)|EhasParameter(Oi, Aj)(EhasParameter(Oi,{Aj, Ak})EWork(Aj, Oi)
EWork(Ak, Oi)AkAstatic)},
100
5.3. GRUNDLEGENDE OBJEKTVERTEILUNGSFUNKTIONEN
M2={LOCAT E(Ol)|EhasParameter(Oi,{Ol, Ok})EcommCost(Ol, Oi)EcommCost(Ok, Oi))
LOCAT E(Ol)6=noans OkO}.
Im Fall, dass Aktivitäten in der Parameterliste übergeben werden, haben sie
Vorrang. Das heißt, wenn nur eine Aktivität übergeben wird, werden die dort
entstehenden Objekte zusammen mit dieser Aktivität platziert. Wenn aber
mehr als eine Aktivität in der Parameterliste auftaucht, werden die zu erzeu-
genden Objekte zusammen mit der Aktivität verteilt, die die meiste Workload
auf diesen Objekten verrichtet. Für den Fall, dass nur normale Objekte, al-
so keine Aktivitäten, übergeben werden, werden die dort erzeugten Objekte
mit dem zusammen platziert, das die meisten Methodenaufrufe auf denen
durchführt. Bei dieser ODF erkennt man zwei wichtige Programmeigenschaf-
ten: Work, welche im Beispiel im vorherigen Abschnitt schon vorkommt, und
commCost, die die Kommunikationskosten zwischen zwei Objektrepräsen-
tanten modelliert. Die Berechnung dieser beiden Eigenschaften geschieht so-
wohl vor der Laufzeit, ggf. durch geeignete Abschätzungen, als auch wäh-
rend der Ausführung mit konkreten Laufzeitdaten.
Bei dieser ODF ist deutlich zu erkennen, dass zyklische Abhängigkeiten bei
der Verteilungsberechnung auftreten können: Das Ergebnis für Oiist abhän-
gig von LOCATE(Ol)und dies ist noch unbekannt. Wenn diese ODF für Ol
angewandt wird und Oials Kandidaten hat, dann hängt das Ergebnis für Ol
wieder von LOCATE(Oi)ab. Um dieses Problem zu lösen ist ein Constraint-
Solver einzusetzen. Somit werden zuerst alle Bedingungen aufgestellt, dann
werden die Abhängigkeiten zwischen ihnen überprüft. Die Strategien, also
auch die ODFs, werden dann zur Berechnung der Verteilungsergebnisse an-
gewandt.
Threadwork
Nach einer Verteilungsstrategie namens MaxWork, die in der Diplomarbeit
von Klohs beschrieben wurde [Klo02], [KLK02], wird diese ODF weiterent-
wickelt. Sie dient als eine der grundlegenden Verteilungsfunktionen, die in
jedem Fall angewandt werden. Der Ansatz besteht darin, alle Objekte zusam-
men mit einer Aktivität zu platzieren, die die meiste Workload auf ihnen ver-
richtet. Dies läßt sich so formulieren:
fTHREADWORK(Oi, E, LOCAT E) :=
({LOCAT E(Ak)|EWork(Ak, Oi)EWork(Al, Oi)AlAstatic},remote).(5.9)
101
KAPITEL 5. VERTEILUNGSSTRATEGIEN UND VERTEILUNGSPLAN:
KONZEPT UND MODELLIERUNG
Zu Objekten, die aus einer Erzeugungsstelle entstehen, werden die auf ihnen
verrichteten Arbeiten aller statisch erkannten Aktivitäten berechnet, welche
durch die Programmeigenschaft Work modelliert werden. Sie werden mitein-
ander verglichen und man findet dadurch die Aktivität heraus, die die meiste
Workload auf den Objekten verursacht. Die Anwendung dieser ODF setzt
voraus, dass Verteilungsergebnisse für alle Aktivitäten schon vorliegen. Wie
schon beschrieben müssen einige Werte für die Programmeigenschaft Work
vor der Laufzeit abgeschätzt werden. Dazu zählen beispielsweise Anzahl der
Schleifendurchläufe und Verzweigungen, deren tatsächlichen Werte nur be-
kannt sind, nachdem die zugehörigen Programmfragmente ausgeführt wur-
den.
Kommunikation zwischen den Aktivitäten
Objekte können nach minimalem Kommunikationsaufwand verteilt werden.
Hier wird der Aufwand, der durch die Kommunikation einer Aktivität mit
den Objekten entsteht, als Maß verwendet. Eine weitere grundlegende ODF
wird so formuliert:
fTHREADCOMM(Oi, E, LOCAT E) :=
({LOCAT E(Ak)|EcommCost(Ak, Oi)EcommCost(Al, Oi)AlAstatic},remote).(5.10)
Die Programmeigenschaft commCost, die die Kommunikationskosten einer
Aktivität mit den an einer Stelle erzeugten Objekten darstellt, hängt von vie-
len der Faktoren ab, deren Werte zum Teil vor der Laufzeit abgeschätzt wer-
den müssen, wie die bei Work-Eigenschaft. Solche Faktoren sind beispiels-
weise die Größe der tatsächlich von einer Aktivität bei einem Objektmetho-
denaufruf übergebenen Parameter, die Größe der Ergebnisse, die Dauer der
Serialisierung bzw. Deserialisierung usw. commCost(Ak, Oi)stellt somit die
gesamten Kosten dar, die bei der Kommunikation zwischen einer Aktivität
Akund einem an der Stelle ierzeugten Objekt während des gesamten Pro-
grammablaufs entstehen.
Verrichtete Arbeiten und Kommunikation der Aktivitäten
Die ODF fTHREADCOMM und fTHREADWORK stellen somit zwei grundlegende
Verteilungsvorschriften dar, die bei jeder Erzeugungsstelle der Nicht-Thread-
Objekte angewandt werden können. Es ist aber nicht sinnvoll, beide auf eine
102
5.3. GRUNDLEGENDE OBJEKTVERTEILUNGSFUNKTIONEN
Erzeugungsstelle gleichzeitig anzuwenden. Die gelieferten Ergebnisse kön-
nen miteinander in Konflikt stehen, so dass keine vernünftige Verteilungsent-
scheidung getroffen werden kann. Trotzdem man kann diese beiden wich-
tigen Programmeigenschaften zusammen verwenden, so dass gute Vertei-
lungsergebnisse erzielt werden können. Somit läßt sich eine neue Verteilungs-
funktion daraus entwickeln, deren Definition folgt:
fTHREADWORKCOMM(Oi, E, LOCAT E) :=
({LOCAT E(Ak)|EWork(Ak, Oi)
EcommCost(Ak, Oi)EWork(Al, Oi)
EcommCost(Al, Oi)AlAstatic},remote).
(5.11)
Die an der Stelle ientstehenden Objekte werden mit der Aktivität zusammen
auf eine Zielmaschine hin platziert, die die meiste Workload auf ihnen ver-
richtet und die Kommunikationskosten durch diese Zusammenplatzierung
minimal werden läßt. Somit werden drei grundsätzliche Verteilungsfunktion-
en zur Verfügung gestellt, die alternativ in einem Verteilungsplan verwendet
werden können.
Zyklische Verteilung der Aktivitäten
Prinzipiell muss zuerst eine Verteilung aller Aktivitäten auf die ver-
fügbaren Maschinen vorgenommen werden, bevor irgendeine Basis-
Verteilungsfunktion verwendet wird. Nach dem gesamten Konzept werden
die Aktivitäten eines mehrsträngigen Programms einmal verteilt und sie blei-
ben dann in diesen Adressräumen fest. Eine nachträgliche Umplatzierung
oder Umzug der Aktivitäten ist nicht erlaubt. Die Platzierung der Nicht-
Thread-Objekte richtet sich nach den schon platzierten Aktivitäten. Ein ein-
facher Ansatz ist es, alle Aktivitäten, die zur Laufzeit erzeugt werden, auf alle
an der Lösung beteiligten JVMs zyklisch zu verteilen. Dies kann mittels fol-
gender Funktion erfolgen:
fDISTRIBUTEDTHREAD(Ai,j, E, LOCATE) :=
({i+j mod |AR|},remote)(5.12)
Ai,j bezeichnet die j-te Aktivität, die an der Erzeugungsstelle ierzeugt wird.
Obwohl die konkrete Anzahl der verfügbaren JVMs vor der Laufzeit noch
nicht bekannt ist, kann man trotzdem anhand der abstrakten Menge der JVMs
AR eine Zuteilung der Aktivitäten zu den abstrakten JVMs schon vor der
103
KAPITEL 5. VERTEILUNGSSTRATEGIEN UND VERTEILUNGSPLAN:
KONZEPT UND MODELLIERUNG
Laufzeit durchführen. Diese ODF zieht auch die einzelnen Aktivitäten in Be-
tracht, die aus derselben Erzeugungsstelle izur Laufzeit erzeugt werden.
Diesen Ansatz kann weiter verfeinert werden, indem die Ausführungskos-
ten der einzelnen Aktivitäten in Betracht gezogen werden, die an derselben
Erzeugungsstelle entstehen. In dem Fall, dass mehr als eine Aktivität an ei-
ner Erzeugungsstelle entsteht, so werden sie zusammen auf dieselbe JVM
platziert. Dies erfolgt nur, wenn ihre Ausführungskosten kleiner gleich 1
fder
maximalen Ausführungskosten eines Aktivitätsrepräsentanten sind. Dadurch
werden mehrere kleinere Arbeitspakete auf einen Rechner zusammen plat-
ziert, große Arbeitspakete auf einzelne Maschinen verteilt. Der Faktor fkann
durch den Entwickler festgelegt werden, der den Verteilungsplan zusammen-
stellt. In diesem Fall soll f= 5 sein. Das Ganze kann in einer formalen Defini-
tion zusammengefasst werden:
fEX_DISTRIBUTEDTHREAD(Ai,j, E, LOCATE) :=
(({i mod |AR|},remote)if 5EexecCost(Ai,j)max{EexecCost(Ak)|AkAstatic}
({i+j mod |AR|},remote)else
(5.13)
Durch die hier verwendete Bedingung bei dieser zyklischen Verteilung der
Aktivitäten ist die Lastverteilung etwas gleichmäßiger auf die beteiligten
JVMs. Der Preis dafür ist die Komplexität bei der Auswertung der Programm-
eigenschaften. Außerdem lassen sich die Verteilungsergebnisse dieser ODF
zur Laufzeit durch Einsatz hinzukommender Laufzeitdaten verbessern, die
bei der Auswertung der Programmeigenschaft execCost eine Rolle spielen.
Zur Auswertung dieser Funktion vor der Laufzeit wird jmit dem Wert 0 be-
legt, d.h. alle Aktivitätsrepräsentanten AiAstatic werden einfach auf die ab-
strakten JVMs zyklisch verteilt. So ähnlich wie Work enthält die Eigenschaft
execCost viele Faktoren wie die Anzahl der Schleifendurchläufe, die Ein-
fluss auf die gesamten Ausführungskosten hat und vor der Laufzeit nicht ge-
nau ausgerechnet werden kann, sondern abgeschätzt werden muss.
5.3.3. Dynamische Verteilungsfunktionen
Verteilungsentscheidungen, die aus den Funktionen dieser Gruppe entstam-
men, basieren vorwiegend auf Laufzeitdaten. Hierzu gehören Vorschriften
zur Objektmigration und -replikation. Weißenborn hat sich in seiner Diplom-
arbeit [Wei03] mit dem Konzept und verschiedenen Techniken zur Migrati-
on und Replikation von Objekten beschäftigt sowie Strategien dazu realisiert,
104
5.3. GRUNDLEGENDE OBJEKTVERTEILUNGSFUNKTIONEN
die vollständig auf Laufzeitdaten die Entscheidung treffen. Die hier vorge-
stellten Verteilungsfunktionen basieren auf einigen Programmeigenschaften,
die zum Teil vor der Laufzeit schon vorab berechnet werden und zur Laufzeit
die Ergebnisse noch weiter präzisieren. Jedenfalls entsteht mehr Aufwand zur
Berechnung dieser Funktionen zur Laufzeit als bei den ODFs anderer Kate-
gorien.
Migration
Der Grund zur Migration eines schon auf einer JVM erzeugten Objektes ist,
dass es insgesamt viel mehr entfernte Zugriffe aus einer anderen JVM gibt
als auf der JVM, wo sich das Objekt gerade befindet. Deshalb wird dieses
Objekt dorthin umgezogen, wo die meisten Zugriffe darauf stattfinden. Die
Verteilungsfunktion zur Migration sieht folgendermaßen aus:
fMIGRATE(Oi, E, LOCATE) :=
((M, remote)if EisMigrateable(Oi)
noans else (5.14)
wobei M={LOCAT E(Oj)|EhasMostAccess(Oj, Oi)LOCAT E(Oj)6=noans}.
Die notwendige Voraussetzung für eine Migration ist, dass die betrachteten
Objekte einer Klasse migrierbar sind. Mit Hilfe statischer Analysetechniken
kann man die Klassen identifizieren, deren Objekte migriert werden sollen.
In der obigen ODF wird dies als die Programmeigenschaft isMigrateable
ausgedrückt. So werden erforderliche Transformationsmechanismen auf sol-
che Klassen angewandt. EhasMostAccess(Oj, Oi)modelliert den Aspekt, dass
von Ojdie meisten Zugriffe auf Oidurchgeführt werden. Unter Zugriffen
können Methodenzugriffe zum Lesen bzw. Schreiben oder direkte Zugriffe
auf Felder verstanden werden. Bei der Bestimmung dieser Eigenschaft müs-
sen ggf. einige Faktoren, beispielsweise über tatsächlich durchzuführende
Methodenzugriffe, abgeschätzt werden. Außerdem werden dabei auch Pro-
grammstellen identifiziert und Instrumentierungsmechanismen dort einge-
führt, damit Daten über Zugriffe zur Laufzeit gesammelt werden und die tat-
sächliche Migration durchgeführt werden kann. Die Entscheidung für eine
tatsächlich stattfindende Migration wird erst zur Laufzeit getroffen, abhängig
von den gesammelten Zugriffshäufigkeiten und von den passenden Migrati-
onsstrategien (siehe [Wei03]).
105
KAPITEL 5. VERTEILUNGSSTRATEGIEN UND VERTEILUNGSPLAN:
KONZEPT UND MODELLIERUNG
5.4. Spezielle Verteilungsstrategien
Zur automatischen Verteilung müssen die oben formulierten Verteilungs-
funktionen in geeigneter Reihenfolge und so geschickt angewandt wer-
den, dass keine der berechneten Ergebnisse in Konflikt zueinander stehen
und gute Verteilungsergebnisse erzielt werden. Durch passende Zusammen-
setzung einer oder mehrerer ODFs entstehen Verteilungsstrategien. ODFs,
die zusammen innerhalb einer Strategie verwendet werden, haben ähnliche
Merkmale wie z.B. statische oder dynamische Eigenschaften.
Die Reihenfolge, in der die ODFs innerhalb einer Strategie angewandt wer-
den, ist in dem Fall wichtig, wenn Transformationsentscheidungen getroffen
werden müssen. Das heißt, dass man ODFs, die entscheiden, dass Objekte
lokal platziert werden, immer vor denjenigen anwendet, deren Ergebnisse
zu einer entfernten Platzierung führen. Im Folgenden werde ich auf einige
spezielle Verteilungsstrategien, jeweils für eine Kategorie nach Definition 5.5,
eingehen. Die Kernausage dieses Abschnitts ist, welche ODFs zu einer Strate-
gie zusammengehören. Außerdem wird hier gezeigt, wie einfach es ist, eine
Verteilungsstrategie durch Zusammensetzung der ODFs zu definieren. Zur
Vereinfachung der Darstellung wird hierbei auf die lange Parameterliste der
verwendeten Funktionen verzichtet.
5.4.1. Statische Verteilungsstrategien
Im Abschnitt 5.2.2 haben wir die Strategie im Beispiel 5.2 kennen gelernt,
die die Charakteristik dieser Gruppe darstellt und dort informell beschrie-
ben wurde. Das Ziel dieser Strategie ist es, möglichst viele Erzeugungsstel-
len mit statischen Programmeigenschaften im Programm vor der Laufzeit zu
erkennen. Wenn eine der verwendeten ODFs in dieser Strategie zu einer Er-
zeugungsstelle ein Verteilungsergebnis berechnen kann, dann braucht diese
nicht transformiert zu werden. Außerdem sind damit die erzeugten Objekte
lokal, so dass eine erhebliche Geschwindigkeitssteigerung der Programmaus-
führung erzielt werden kann. Die Definition dieser Strategie sieht folgender-
maßen aus:
106
5.4. SPEZIELLE VERTEILUNGSSTRATEGIEN
strategyLOCAL(Oi, E, LOCATE) :=
fPROXY(Oi, E, LOCAT E)if fPROXY(. . .)6=noans
fTHREADLOCAL(Oi, E, LOCAT E)if fTHREADLOCAL(. . .)6=noans
fPURE-IMMUTABLE(Oi, E, LOCAT E)if fPURE-IMMUTABLE(. . .)6=noans
noans else
(5.15)
Die ODFs werden wie angegeben in der Reihenfolge von oben nach unten
verwendet. Innerhalb dieser Strategie werden die ODFs angewandt, die Er-
gebnisse für lokale Platzierungsentscheidung der Objekte ermitteln. Das End-
ergebnis dieser Strategie ist entweder noremote oder noans. Wenn eine ODF
innerhalb dieser Strategie noremote liefert, wird die Auswertung weiterer
ODFs abgebrochen und dies gilt als das Endergebnis der Strategie. Die beiden
ähnlichen ODFs zu Proxy und ThreadLocal werden deshalb zusammen in
einer Strategie in der angegebenen Reihenfolge angewandt, denn es gibt Ob-
jekte, die nur die thread-lokale Eigenschaft erfüllen, aber nicht die Proxy.
Im Gegensatz dazu sind diejenigen auch thread-lokal, wenn sie die Proxy-
Eigenschaft besitzen. Die ODF zur Immutabilität wird deshalb hierin als
Letzte durchgeführt, weil konstante Objekte auch Proxy- oder Threadlocal-
Eigenschaft besitzen können und in solchen Fällen für sie auch keine Trans-
formation benötigt wird. Dies ist ein Optimierungsaspekt für die Transfor-
mation. Somit kann an Transformationsaufwand für den einzubauenden Pro-
grammcode gespart werden, wodurch zusätzliche Laufzeitkosten vermieden
werden. Die Strategie LOCAL ist eine partielle Funktion. Deshalb muss nach
ihrer Anwendung eine weitere totale Strategie folgen.
5.4.2. Basis-Verteilungsstrategien
Die Strategien in dieser Gruppe stellen das Fundament eines Verteilungsplans
dar. In einem Plan kann nur eine von den als global bezeichneten Strategien
verwendet werden. Die Funktionen, die die Strategien dieser Kategorie mo-
dellieren, sind totale Funktionen. Zu dieser Kategorie gehören eine Strategie
zur Verteilung der Aktivitäten, welche als Erste angewandt werden muss,
und einige Strategien, die globale Verteilungseigenschaften des Programms
darstellen. Da alle globalen Strategien ähnliche Charakteristiken besitzen,
wird im Folgenden nur auf eine Vertretende eingegangen.
107
KAPITEL 5. VERTEILUNGSSTRATEGIEN UND VERTEILUNGSPLAN:
KONZEPT UND MODELLIERUNG
Strategie zur Verteilung der Aktivitäten
Um Aktivitäten verteilen zu können, werden zwei Verteilungsfunktionen be-
nutzt, die zusammen die Strategie strategyTHREADS darstellen. Diese Strate-
gie kann vor der Laufzeit eingesetzt werden, um vorab Verteilungsentschei-
dung für alle Aktivitätsrepräsentanten zu berechnen. Dadurch gewinnt man
zunächst einen groben Überblick über die Aufteilung der Arbeitsstränge auf
die abstrakten JVMs. Außerdem ist der Einsatz dieser Strategie eine not-
wendige Voraussetzung, bevor irgendwelche Verteilungsberechnung für nor-
male Objektrepräsentanten vor der Laufzeit vorgenommen werden kann. Die
Funktionsdefinition lautet:
strategyTHREADS(Ai, E, LOCATE) :=
(fPEER(Ai, E, LOCAT E)if fPEER(. . .)6=noans
fEX_DISTRIBUTEDTHREAD(Ai,j, E, LOCAT E)else (5.16)
Die ODF fPEER wird zunächst auf die eingegebene Aktivität angewandt, um
zu überprüfen, ob sie die Peer-Eigenschaft besitzt. Wenn eine Aktivität (ein
Thread-Objekt) diese Eigenschaft erfüllt, dann ist sie semantisch nicht mehr
eine Aktivität im verteilten Sinn. Wenn beispielsweise speziell ein Thread
für die Zeichnen-Aufgabe in einem Rechner erzeugt wird, dann bleibt die-
ser Thread lokal auf diesem Rechner. Eine Transformation der Erzeugungs-
stelle ist deshalb auch nicht notwendig. Es lohnt sich auf jeden Fall, zunächst
nach der Peer-Eigenschaft zu prüfen, weil dadurch nicht nur Transformati-
onsaufwand gespart wird, sondern man auch an Geschwindigkeit durch die
Lokalität dieser Aktivität gewinnt.
Anschließend wird eine zyklische Verteilung der Aktivitäten mittels der ODF
fEX_DISTRIBUTEDTHREAD vorgenommen. Vor der Laufzeit wird der Parameter
j(das j-te erzeugte Thread-Objekt an der Stelle i) auf den Wert 0 gesetzt,
damit die Aktivitätsrepräsentanten zyklisch auf die abstrakten JVMs verteilt
werden können. Zur Laufzeit wird diese Funktion auf einzelne entstehende
Threads angewandt.
Globale Strategien
Zur Verteilung normaler Objekte, also andere als Thread-Objekte, kommen so
genannte globale Verteilungsstrategien zum Einsatz. Eine globale Strategie ist
108
5.4. SPEZIELLE VERTEILUNGSSTRATEGIEN
eine totale Funktion, d.h. sie liefert zu jeder Eingabe (einen Objektrepräsen-
tanten vor der Laufzeit bzw. eine Objekterzeugung zur Laufzeit) ein Ergebnis
zur Objektverteilung. Die Anwendung einer solchen Strategie setzt voraus,
dass die Aktivitäten schon auf die abstrakten JVMs bzw. auf die realen Ma-
schinen verteilt wurden. Getroffene Entscheidungen von globalen Strategien
lassen sich aber zur Laufzeit gegebenenfalls aufgrund erneuter Berechnung
verbessern. Dies liegt daran, dass die meisten Programmeigenschaften der
verwendeten Basis-ODFs vor der Laufzeit abgeschätzt werden müssen und
sie mit neu hinzukommenden Laufzeitdaten erneut berechnet werden kön-
nen. Eine beispielhafte globale Verteilungsstrategie sei wie folgt definiert:
strategyCOMMWORK(Oi, E, LOCAT E) :=
(fEX_CONSTRPARAM(Oi, E, LOCAT E)if fEX_CONSTRPARAM(. . .)6=noans
fTHREADWORKCOMM(Oi, E, LOCAT E)else (5.17)
Prinzipiell besteht diese Strategie aus zwei verschiedenen Basis-ODFs in der
angegebenen Reihenfolge, wobei die zuletzt anzuwendende eine totale Funk-
tion sein muss. Das liegt daran, weil eine globale Strategie eine totale Funk-
tion ist. Die Idee, warum ausgerechnet diese zwei ODFs zusammen ver-
wendet werden, ist folgende: Die erste ODF hat hauptsächlich die Aufga-
be, Objektrepräsentanten mit der Eigenschaft Konstruktorparameter „her-
auszufiltern“, bevor eine Verteilungsentscheidung für sie mittels der ODF
fTHREADWORKCOMM berechnet wird. Diese übernimmt die Hauptaufgabe,
Verteilungsentscheidungen für Objektrepräsentanten mit Hilfe der Work-
und commCost-Programmeigenschaft zu berechnen.
Anstelle der ODF fTHREADWORKCOMM kann eine andere, totale Basis-
Verteilungsfunktion, z.B. fTHREADWORK oder fTHREADCOMM verwendet wer-
den. Daraus entwickelt sich jeweils eine neue Verteilungsstrategie, die eine to-
tale Funktion darstellt und zur initialen Verteilungsberechnung benutzt wer-
den kann. Da jede dieser Funktionen eine eigenständige Strategie zur Ver-
teilung der Objekte modelliert, darf jeweils nur eine für eine Programmaus-
führung angewandt werden. Im Beispiel 5.2.3 auf der Seite 91 wurde diese
Strategie zusammen mit der Strategie strategyLOCAL in einem Plan verwendet.
5.4.3. Strategien mit dynamischen Entscheidungen
Verteilungsfunktionen, die Berechnung zur Migration bzw. Replikation von
Objekten durchführen, werden in Strategien dieser Kategorie formuliert. Die
109
KAPITEL 5. VERTEILUNGSSTRATEGIEN UND VERTEILUNGSPLAN:
KONZEPT UND MODELLIERUNG
Aufgabe, zur Laufzeit schon platzierte Objekte von einer JVM auf eine ande-
re umzuziehen bzw. auf mehrere JVMs deren Replikaten zu erstellen, wird
während der Programmausführung erledigt. Zur Unterstützung dafür kön-
nen vor der Laufzeit einige Maßnahmen und Vorabentscheidungen getroffen
werden, damit so wenig Berechnungen wie möglich zur Laufzeit durchge-
führt werden müssen. Dazu gehören u.a. statische Analysen zur Bestimmung
der Art der Objektzugriffe, ob Lese- oder Schreibzugriffe stattfinden sowie
Abschätzungen der Anzahl der Zugriffe. Dennoch besteht immer noch Re-
chenaufwand zur Informationserfassung während des Programmablaufs, um
zu endgültigen Entscheidungen zu kommen.
Zusammen mit Strategien anderer Kategorien können diese Strategien zur
weiteren Geschwindigkeitssteigerung verwendet werden. Vor der Laufzeit
treffen sie Entscheidungen zu notwendigen Transformationen und liefern
Entscheidungsvorschläge für die Migration bzw. Replikation. Zur Laufzeit
werden die Lösungsvorschläge bei genügend vorliegenden Informationen
umgesetzt. In dieser Gruppe finden sich zwei spezielle Verteilungsstrategien:
Die eine realisiert Replikation von Teil-Immutable-Objekten und die ande-
re liefert Entscheidungsvorschläge, ob und wohin Objekte migriert werden
können.
Strategie zur Replikation
Obwohl diese Strategie als dynamische Strategie bezeichnet wird, sind aber
die Programmeigenschaften der verwendeten ODF statisch. Das heißt, dass
man sie schon vor der Laufzeit vollständig ausrechnen kann. Dynamisch in
diesem Sinn ist, dass zur Laufzeit die entstehenden Objekte mittels anderer
Strategien schon platziert wurden, und durch diese ihre ursprüngliche Plat-
zierungsentscheidung geändert wird. Nachdem initiale Platzierungshinwei-
se für alle Objekt- und Aktivitätsrepräsentanten schon ausgerechnet wurden,
können diese Repräsentanten nach der postponed-immutable Eigenschaft
überprüft werden. Dadurch werden vor der Laufzeit Replikationspunkte ge-
funden und Transformationsschritte dort angewandt. Zur Laufzeit werden an
solchen Replikationsstellen die Objekte repliziert.
Strategie zur Migration
Die strategische Entscheidung über die Migration der Objekte basiert auf der
Grundlage, dass ein schon platziertes Objekt häufig von einem entfernten
110
5.5. KONKRETE VERTEILUNGSPLÄNE
Rechner zugegriffen wird. Diese Fernzugriffe können von mehreren Objekt-
en oder Aktivitäten stammen, die sich auf einem anderen Rechner befinden.
Aus technischen Gründen muss vor der Laufzeit entschieden werden, ob Ob-
jekte einer Klasse zur Laufzeit migriert werden dürfen und daher notwendige
Transformationsschritte durchzuführen sind. Zur Bestimmung des Migrati-
onsziels muss die Strategie notwendigerweise vor der Laufzeit alle möglichen
Zugriffe auf Objektrepräsentanten und die Art der Zugriffe ausrechnen. So-
mit kann abgeschätzt werden, welche Aktivitäts- bzw. anderen Objektreprä-
sentanten die meisten Zugriffe durchführen. So können Zielvorschläge für die
Migration als Lösung bestimmt werden. Diese Strategie enthält eine einzige
ODF und sei wie folgt definiert:
strategyMIGRATE(Oi, E, LOCAT E) :=
fMIGRATE(Oi, E, LOCAT E)(5.18)
Basierend auf den statischen Lösungsvorschlägen aus dieser Strategie wer-
den endgültige Migrationsentscheidungen durch die anderen Komponen-
ten getroffen, die in Abbildung 5.3 als übereinander gestaffelte Rechtecke
bei Migrations-/Replikationsstrategien visualisiert und ausführlich in der Di-
plomarbeit von Weißenborn [Wei03] beschrieben wurden. Die Informations-
erfassungskomponente sammelt zur Laufzeit zusätzlich zu den statischen
Entscheidungsvorschlägen echte Zugriffsdaten für die zu migrierenden Ob-
jekte und bestimmt daraus den eigentlichen Zielort für den Objektumzug.
Entscheidungen, ob Objekte nur einmal migriert werden dürfen oder die Re-
plikate durch Schreibzugriffe gelöscht oder aktualisiert werden, übernehmen
die Komponenten von Weißenborn. Die technischen Schritte zur Migration
bzw. Replikation werden auch von der Transfer-Komponente durchgeführt.
Wie die Replikationsstrategie ist dies auch eine partielle Funktion.
5.5. Konkrete Verteilungspläne
In diesem Abschnitt wird an einem vollständigen Verteilungsplan gezeigt,
wie im Allgemeinen konkrete Verteilungspläne, bestehend jeweils aus einigen
ausgewählten speziellen Strategien, formuliert werden können. Alle Berech-
nungen basieren auf denselben Datenbestandteilen: Das sind die Programm-
und Laufzeiteigenschaften, die Strategien und die Datensätze in der Ver-
teilungsdatenbank. Sie unterscheiden sich voneinander nur durch die darin
111
KAPITEL 5. VERTEILUNGSSTRATEGIEN UND VERTEILUNGSPLAN:
KONZEPT UND MODELLIERUNG
eingesetzten Verteilungsstrategien und können verschiedene Verteilungsent-
scheidungen zu einem Programm liefern. Im Prinzip haben alle Verteilungs-
pläne den kompletten Überblick über das Eingabeprogramm. Ein Plan wird
statisch und dynamisch evaluiert. Nach der statischen Evaluation liegen al-
le initialen Verteilungsentscheidungen vor und das Programm wird komplett
zur verteilten Ausführung transformiert. Zur Laufzeit wird dynamisch evalu-
iert, wobei die eigentlichen Platzierungshinweise vom Verteilungsplan gelie-
fert werden und ggf. einige Verteilungsentscheidungen erneut getroffen wer-
den.
Für den vorzustellenden Plan planWORKCOMM folgt zunächst eine textuelle Be-
schreibung:
Wenn die lokale Strategie keine Antwort liefert, verteile Objekte nach der Strate-
gie „verrichtete Workload und Kommunikation“. Strategien zur Migration und Re-
plikation werden ebenfalls angewandt: Wenn noans geliefert wird, wird das vorher
berechnete Verteilungsergebnis beibehalten.
Die Definition dieses Planes sieht folgendermaßen aus:
planCOMMWORK(Oi, E, LOCATE) :=
strategyTHREADS(Oi, E, LOCAT E)if OiAstatic
strategyLOCAL(Oi, E, LOCAT E)if strategyLOCAL(. . .)6=noans
strategyCOMMWORK(Oi, E, LOCAT E)else
(5.19)
In dieser Strategie werden vor der Laufzeit alle vorkommenden Aktivitätsre-
präsentanten auf die abstrakten JVMs verteilt. Somit hat man einen Überblick
über die Platzierungsziele der Threads. Das ist eine notwendige Vorausset-
zung für alle formulierten Verteilungspläne, bevor irgendwelche Verteilungs-
berechnung der normalen Objekte stattfindet. Alle Objekte bzw. Objektreprä-
sentanten, die bei allen vorgegebenen Strategien „durchkommen“, werden
mittels der strategyCOMMWORK() dorthin zu den Aktivitäten platziert, wo die
meiste Workload auf ihnen verrichtet und viel mit ihnen kommuniziert wird.
In der Funktionsdefinition des obigen Plans wurden keine der beiden Strate-
gien zur Migration und Replikation angegeben. Denn diese Programmeigen-
schaften werden nachträglich berechnet, nachdem Verteilungshinweise aller
Objektrepräsentanten vorlagen. Die Strategie zur Migration ist eine partielle
Funktion und kann auch noans als Ergebnis liefern. Daher wird die vorhe-
rige Entscheidung mittels strategyLOCAL und strategyCOMMWORK beibehalten,
wenn die Strategie zur Migration keine Aussagen macht. Außerdem muss be-
112
5.5. KONKRETE VERTEILUNGSPLÄNE
achtet werden, dass Objekte nach ihrer Replikation nicht mehr migriert wer-
den dürfen. Diese Strategien können eingesetzt werden, um eine Geschwin-
digkeitssteigerung der Programmausführung zu erzielen.
Dies ist ein vollständiger Verteilungsplan, der zur automatischen Verteilung
jedes mehrsträngigen Java-Programms eingesetzt werden kann. Im Allge-
meinen unterscheiden sich die Verteilungspläne durch die Anwendung ver-
schiedener Strategien und die übergebenen Programmeigenschaften sowie
Laufzeiteigenschaften. Das bedeutet, dass ein neuer Plan durch das Hinzufü-
gen, Entfernen und Ersetzen der verwendeten Strategien einfach formuliert
werden kann. Beim obigen Plan kann man beispielsweise die Strategien zur
Migration und Replikation weglassen und strategyCOMMWORK() durch eine
andere Basis-Strategie, z.B. strategyTHREADCOMM(), ersetzen. So entsteht ein
neuer, einfacherer Verteilungsplan, der evtl. genauso gut für alle multithreaded
Java-Anwendungen funktioniert.
Durch das vorgestellte Konzept ist man in der Lage, neue Objektverteilungs-
funktionen zu formulieren, verschiedene Varianten der Strategien anzuwen-
den und somit unterschiedliche Pläne zur Verteilung eines Programms einzu-
setzen. Dadurch kann die Güte der Verteilung evaluiert werden. Die Model-
lierung der Programmeigenschaften in Formeln, deren Bewertung die Vertei-
lungsergebnisse entscheiden, ermöglichen einem, die Programmanalyse von
der Berechnung der Verteilungsfunktion zu trennen, und dennoch statische
und dynamische Analysetechniken kombinieren zu können.
113
.
6. Analysetechniken zur
automatischen Verteilung
Inhalt
6.1. Charakteristiken der statischen Programm- und Laufzeitei-
genschaften . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
6.1.1. Statische Programmeigenschaften . . . . . . . . . . . . . 118
6.1.2. Architektur- und Laufzeiteigenschaften . . . . . . . . . . 121
6.2. Dynamische Programmeigenschaften und das Kostenmo-
dell zur Objektverteilung . . . . . . . . . . . . . . . . . . . . . . 123
6.2.1. Modellierung dynamischer Programmeigenschaften . . 123
6.2.2. Konzept zur hybriden Analyse . . . . . . . . . . . . . . . 130
6.3. Grundlegende statische Analysen . . . . . . . . . . . . . . . . 134
6.3.1. Aufrufgraph-Analyse zur Auflösung dynamischer Me-
thodenbindung . . . . . . . . . . . . . . . . . . . . . . . . . 135
6.3.2. Interprozedurale Def-Use-Analyse (GlobalDefUse-
Analyse)............................. 137
6.3.3. Analysetechniken zur Berechnung der Programmeigen-
schaften ............................. 141
6.4. Analyseverfahren zur stagewise-immutable Eigenschaft . . 145
6.4.1. Die stagewise-immutable Eigenschaft und ihre Anwen-
dung zur Objektreplikation . . . . . . . . . . . . . . . . . 146
6.4.2. Analysetechniken zur Berechnung der stagewise-
immutable Eigenschaft . . . . . . . . . . . . . . . . . . . . . 151
6.4.3. Analyse zur pure-immutable Eigenschaft . . . . . . . . . . 160
6.5. Verwandte Arbeiten . . . . . . . . . . . . . . . . . . . . . . . . . . 161
6.5.1. Immutable-Analysen . . . . . . . . . . . . . . . . . . . . . 161
6.5.2. Analysetechniken zur Objektverteilung . . . . . . . . . . 163
115
KAPITEL 6. ANALYSETECHNIKEN ZUR AUTOMATISCHEN VERTEILUNG
Statische Programmeigenschaften, welche zur Entscheidung der Transfor-
mation sowie der grundlegenden Verteilungsberechnung beitragen, weisen
bestimmte Charakteristiken auf. Sie können durch systematische Anwen-
dung geeigneter statischer Analyseverfahren bestimmt werden. Durch den
Einsatz der Analyseverfahren wie zum Beispiel Klassenhierarchie-, Escape-
oder Aufrufgraphanalyse sowie Analysen zur Berechnung von Definiti-
ons/Verwendungsketten können solche elementaren Charakteristiken ermit-
telt werden, auf denen viele statische Programmeigenschaften basieren. Da-
mit können vor der Laufzeit Entscheidungen getroffen werden, ob entste-
hende Objekte zusammen gruppiert und nur lokal erzeugt, oder ob sie we-
gen ihrer Beziehung zusammen platziert werden sollen. Die zur Migration
und Replikation erforderlichen Maßnahmen müssen statisch vorbereitet sein:
die Instrumentierungsmechanismen zum Zählen der Zugriffe und bestimmte
Migrations- oder/und Replikationshinweise sind beispielsweise in das Pro-
gramm einzutransformieren. Die elementaren Charakteristiken der statischen
Programmeigenschaften werden im ersten Abschnitt beschrieben.
Einige fundamentale Programmeigenschaften, die die Ausführungs- und
Kommunikationskosten charakterisieren, sind dynamische Eigenschaften.
Diese Kosten sind Grundsteine der Verteilungsberechnung für alle Objekte
und Aktivitäten und können vor der Laufzeit nicht genau berechnet werden.
Deshalb muss der dynamische Anteil dieser Kosten abgeschätzt werden. An-
hand des hier angegebenen Kostenmodells kann hybride Analyse angewandt
werden, um Ergebnisse der Kostenabschätzung, somit auch der Verteilung,
zur Laufzeit zu verbessern. Hybride Analyse ist eine Kombination statischer
und dynamischer Analyse. Statische Techniken werden dazu benutzt, um dy-
namische Anteile der Kosten vor der Laufzeit abzuschätzen. Zur Laufzeit
werden einige abgeschätzte Werte mittels dynamischer Analyse genau be-
stimmt. Das führt wiederum zur erneuten Berechnung der Ausführungskos-
ten, was die Verteilung zukünftig zu erzeugender Objekte positiv beeinflusst.
Dafür müssen die dynamischen und statischen Aspekte der Kosten genau
modelliert werden. Der zweite Abschnitt stellt dieses Vorhaben dar.
Anschließend werde ich einige zur automatischen Verteilung relevan-
te Analysetechniken vorstellen und ihre Anwendung auf die modellier-
ten Programmeigenschaften beschreiben. Das sind eine erweiterte Version
der Aufrufgraph-Analyse (PreciseCallGraph-Analyse) und die interprozedu-
rale Def-Use-Analyse (GlobalDefUse-Analyse). Mit Hilfe der PreciseCallGraph-
Analyse können dynamisch gebundene Aufrufziele mittels der verwen-
deten Information zur Typinferenz genauer ermittelt werden. Durch die
116
6.1. CHARAKTERISTIKEN DER STATISCHEN PROGRAMM- UND
LAUFZEITEIGENSCHAFTEN
GlobalDefUse-Analyse können zu den Erzeugungsstellen ihre Definition-
Verwendung-Ketten im gesamten Analysekontext berechnet werden. Diese
Information wird fluss- und kontext-insensitiv über Methodengrenzen hin-
aus verfolgt. Hierbei wird die Technik der copy-propagation angewandt. Für
formale Parameter und Ergebnisse werden erreichende Definitionen über al-
le relevanten Aufrufstellen gesammelt.
Der Abschnitt 6.4 beschäftigt sich mit einem besonderen Analyseverfahren,
der stagewise-immutable Analyse. Diese statische Analysetechnik habe ich ent-
worfen, um Erzeugungsstellen vor der Laufzeit zu erkennen, an denen ent-
stehende Objekte zur Laufzeit repliziert werden können. Bezogen auf den Pro-
grammablauf wird somit charakterisiert, in welchen bestimmten Phasen während
der Programmausführung die Lese- und Schreibzugriffe auf ein Objekt statt-
finden. Objekte mit der stagewise-immutable Eigenschaft werden ab einem be-
stimmten Zeitpunkt Tbei der Programmausführung nur lesend zugegriffen.
Nach diesem Zeitpunkt können sie dann repliziert werden. Nach der Replika-
tion werden Lesezugriffe zu lokalen Zugriffen, so dass entfernte Kommuni-
kation dadurch eingespart wird. Abschließend werde ich verwandte Arbeiten
im Umfeld der Programmanalysen, insbesondere für den Einsatz zur automa-
tischen Verteilung diskutieren.
Die statische Analyse berechnet die Programmeigenschaften auf der Grund-
lage von Objekterzeugungsstellen. An einer Erzeugungsstelle gelten die Er-
gebnisse für alle Objekte, die dort zur Laufzeit entstehen.
6.1. Charakteristiken der statischen Programm- und
Laufzeiteigenschaften
Wie im Abschnitt 5.1.1 schematisiert gliedern sich die Eigenschaften, die zur
Berechnung der Objektverteilung relevant sind, in drei Kategorien: statische
Programmeigenschaften, dynamische Programmeigenschaften und Laufzeiteigen-
schaften. Im Folgenden werde ich die Gemeinsamkeit sowie die Unterschiede
der statischen Programmeigenschaften beschreiben. Durch diese Identifizie-
rung und Klassifizierung können geeignete statische Analyseverfahren dafür
eingesetzt werden, die im folgenden Abschnitt ebenfalls beschrieben werden.
Im Gegensatz dazu stellen die dynamischen Programmeigenschaften die
Laufzeit- und Kommunikationskosten des Programms dar. Teile dieser Kos-
ten müssen vor der Laufzeit abgeschätzt werden. Daher ist eine geeignete Mo-
117
KAPITEL 6. ANALYSETECHNIKEN ZUR AUTOMATISCHEN VERTEILUNG
dellierung dieser Programmeigenschaften in Rahmen eines passenden Kos-
tenmodelles erforderlich. Dieses Modell spiegelt das Konzept hybrider Ana-
lyse - also eine Kombination statischer Analyseergebnisse mit dynamischen
Ergebnissen - wider. Der Übersichtlichkeit wegen wird diese Konzeption ein-
zeln im folgenden Abschnitt beschrieben.
Unabhängig vom Anwendungsprogramm gibt es eine Reihe von Eigenschaf-
ten, die die Verteilung der Objekte während der Ausführung beeinflussen. Sie
müssen bei der Verteilungsberechnung vor der Laufzeit ebenfalls mit berück-
sichtigt werden. Die Identifizierung und Charakterisierung solcher Laufzeit-
eigenschaften schließen diesen Abschnitt ab.
6.1.1. Statische Programmeigenschaften
Allen statischen Programmeigenschaften ist gemein, dass ihre Analyseergeb-
nisse vor der Laufzeit vollständig durch passende Verfahren ausgerechnet
bzw. abgeschätzt werden. So kann die Berechnung bestimmter ODFs erfol-
gen, deren Ergebnisse für die Transformation zur Verfügung stehen müssen.
Im Folgenden wird auf einzelne Merkmale der statischen Programmeigen-
schaften eingegangen.
Eigenschaften zur Lokalität
Die statischen Programmeigenschaften EisProxy(Oi)und
EisThreadLocal(Aj, Oi)weisen auf die Lokalität der Objekte hin. Objekte,
die diese Eigenschaften besitzen, werden mit anderen Objekten bzw. Threads
zusammen auf eine virtuelle Maschine platziert. Das heißt, dass alle Zugriffe
von umgebenden Objekten bzw. zugehörigen Threads darauf nur innerhalb
eines Adressraumes stattfinden. Alle Zugriffe auf Objekte mit derartigen
Eigenschaften werden lokal durchgeführt, so dass sie nicht remote-Objekte zu
sein brauchen.
Gemeinsamkeit und Unterschiede zwischen isThreadLocal und
isProxy Den beiden statischen Programmeigenschaften isProxy und
isThreadLocal ist gemein, dass Objekte mit diesen Eigenschaften ihrem
umgebenden Objekt bzw. ausführenden Thread nicht entkommen. Alle Me-
thodenaufrufe und direkte Feldzugriffe über ihre Referenzen müssen inner-
halb ihrer umgebenden Objekte bzw. Threads stattfinden.
118
6.1. CHARAKTERISTIKEN DER STATISCHEN PROGRAMM- UND
LAUFZEITEIGENSCHAFTEN
Die Referenz zu einem Objekt mit der isProxy-Eigenschaft darf nur seinem
umgebenden Objekt - also seinem Proxy-Objekt - bekannt sein. Diese Refe-
renz darf also auf keine Weise, z.B. durch Seiteneffekte eines Methodenauf-
rufs, nach außen sichtbar werden. Alle Zugriffe über diese Referenz müs-
sen vollständig vom Proxy-Objekt gekapselt werden. Diese Eigenschaft gilt
konservativerweise nicht für Objekte, die über den reflection-Mechanismus
erzeugt werden. Denn durch reflection wird sogar externer Zugriff auf priva-
te Instanzvariablen ermöglicht. Im Prinzip erfüllt ein Objekt die isProxy-
Eigenschaft nur, wenn sowohl seine Erzeugung (nicht über Reflexion) als
auch jeder Zugriff darauf nur innerhalb von Methoden des umgebenden Ob-
jekts erfolgt.
Bei der Eigenschaft EisThreadLocal(Aj, Oi)gilt die folgende Bedingung: Oiist
Ajthread-lokal, wenn es keine andere Aktivität Al6=Ajgibt, so dass AlZu-
griffe auf Oidurchführt. Im Vergleich zu der isProxy-Eigenschaft kann die
Referenz eines thread-lokalen Objekts zwar über Parameter oder das Ergeb-
nis eines Methodenaufrufs ihrem umgebeneden Objekt entkommen. Sie darf
aber nicht dem Thread entkommen, in dem es erzeugt wird. Aufgrund der
Gemeinsamkeit dieser beiden statischen Programmeigenschaften kann das
statische Verfahren zur Escape-Analyse eingesetzt werden.
EisPeer(Oi)Objekte, die die isPeer-Eigenschaft erfüllen, sind wegen
beispielsweise der Benutzung von Hardware-Ressourcen fest im Adress-
raum verankert, in dem sie erzeugt werden. Peer-Objekte stellen in der Re-
gel anwendungsspezifische Muster dar, die die Nutzung von Hardware-
Ressourcen (Netzwerk-Ressourcen), das Zeichnen von Grafiken oder GUI-
Komponenten (mittels Java-AWT), die Ausführung von Maschinencode oder
Funktionen dynamischer Bibliotheken (native code) oder die Benutzerinterak-
tionen beschreiben. Anhand dieser Muster kann eine statische Analysetech-
nik die Erzeugungsstellen für Objekte mit dieser Eigenschaft erkennen, indem
u.a. an allen Aufrufstellen ihrer Methoden überprüft wird, ob ein potenzielles
Aufrufziel eine als native deklarierte Methode ist. Die Methoden solcher Ob-
jekte sind aber trotzdem remote zugreifbar. Daher ist die Transformation der
zugehörigen Klasse erforderlich.
Relation zwischen Objekten
Die beiden elementaren, statischen Programmeigenschaften hasAccess und
hasParameter beschreiben die Beziehung zwischen verschiedenen Objekt-
119
KAPITEL 6. ANALYSETECHNIKEN ZUR AUTOMATISCHEN VERTEILUNG
en, die ggf. in einer konservativen Weise abgeschätzt wird. Anders als bei
den oben vorgestellten Programmeigenschaften werden die in Beziehung ste-
henden Objekte nicht notwendigerweise zusammen platziert. Die Eigenschaft
stellt ein schwächeres Kriterium zur Objektgruppierung dar.
EhasAccess(Oj, Oi)stellt die Relation zwischen Ojund Oidar, wobei Ojdi-
rekt auf Oizugreift. Die Zugriffsrelation wird auf eine konservative Weise wie
folgt festgelegt:
Oj„kennt“ Oi:Ojerhält die Referenz auf Oi, indem beispielsweise OjOier-
zeugt oder Ojdie Referenz zu Oials Parameter oder Ergebnis eines Metho-
denaufrufs bekommt und
Ojcalls Oi: In den verwendeten Methoden von Ojwird mindestens ei-
ne Aufrufstelle einer nicht-statischen Methode über die Referenz zu Oi
gefunden (fluss-insensitiv), oder
Ojgreift direkt auf ein Feld von Oizu.
EhasParameter(Oi,{Oj, . . . , Ok})ist ein spezieller Fall der Programmeigen-
schaft hasAccess. Hierbei wird an der Erzeugungsstelle von Oidie aufgeru-
fene Konstruktor-Methode darauf hin analysiert, ob Referenzen zu Oj. . . Ok
als Parameter übergeben werden. Hierfür benötigt man die Information über
interprozedurale erreichende Definition von Oj. . . Ok, welche mittels der in-
terprozeduralen Def-Use-Analyse (siehe Abschnitt 6.3.2) beschafft wird.
Eigenschaften zur Migration bzw. Replikation
Die notwendige Voraussetzung für eine Migration bzw. Replikation eines
Objekts bei diesem Konzept ist, dass es ein passives Objekt sein muss.
Das bedeutet, dass es keine Instanz von Thread oder eine Unterklasse da-
von sein darf. Genau dies beschreibt die statische Programmeigenschaft
EisMigrateable(Oi). Mit Hilfe von Klassenhierarchie-Information kann man
dies einfach prüfen. Auf Objekte mit dieser Eigenschaft können zur Laufzeit
Migrationsstrategien nach [Wei03] angewandt werden.
120
6.1. CHARAKTERISTIKEN DER STATISCHEN PROGRAMM- UND
LAUFZEITEIGENSCHAFTEN
EisStagewiseImmutable(Oi)Objekte, die diese Programmeigenschaft aufwei-
sen, werden ab einem bestimmten Zeitpunkt Twährend der gesamten Aus-
führung des Programms nur lesend zugegriffen. Die stagewise-immutable Ei-
genschaft wird in diesem Konzept in zwei verschiedene Kategorien unterteilt:
pure-immutable und postponed-immutable.
Der Zeitpunkt Tbei der pure-immutable Eigenschaft ist nach der Objektinitia-
lisierung. Das heißt, dass der Zustand eines pure-immutable Objekts bis zum
Ende der Ausführung unverändert bleibt, nachdem eine zur Erzeugung auf-
gerufene Konstruktor-Methode komplett abgearbeitet wurde, also nach sei-
ner Initialisierungsphase. Um diese Eigenschaft für ein Objekt feststellen zu
können, muss man vor der Laufzeit die Definitionsstelle dieses Objekts, al-
so seine Erzeugungsstelle, als Ausgangspunkt betrachten: Alle potentiellen
Verwendungsstellen des an dieser Stelle entstehenden Objekts, die auch über
Methodengrenzen hinaus gehen, werden darauf hin untersucht, ob die Zu-
griffe auf seine Felder dort, z.B. über Methodenaufrufe oder direkt zugegrif-
fen, lesende oder schreibende Zugriffe darstellen. Ein Objekt besitzt genau
dann die pure-immutable Eigenschaft, wenn alle Verwendungsstellen seine in
den Feldern gespeicherten Daten nur lesen.
Bei der postponed-immutable Eigenschaft wird ein Objekt nach seiner Initialisie-
rungsphase noch schreibend zugegriffen, ab einem Zeitpunkt Tin seiner Ver-
wendungsphase bis zum Programmende aber dann nur gelesen. Die Immu-
tabilität solcher Objekte wird sozusagen auf einen späteren Zeitpunkt nach
seiner Initialisierung verschoben. Auf Details über diese Eigenschaft und die
Analysetechnik dazu wird ausführlich im folgenden Abschnitt 6.4 eingegan-
gen.
Objekte, die eine dieser Eigenschaften erfüllen, können ab dem passenden
Zeitpunkt auf allen JVMs repliziert werden. Dadurch können viele entfernte
Kommunikationen gespart werden.
6.1.2. Architektur- und Laufzeiteigenschaften
Anzahl der verfügbaren JVMs
Die wichtigste Eigenschaft unter dieser Kategorie, die anwendungsunabhän-
gig ist und trotzdem großen Einfluss auf die eigentliche Platzierung der zu
erzeugenden Objekte zur Laufzeit hat, ist die Anzahl der verfügbaren virtuel-
len Maschinen. Die tatsächliche Anzahl der beteiligten JVMs ist erst zur Lauf-
zeit, unmittelbar vor dem Start der Programmausführung, bekannt. Dennoch
121
KAPITEL 6. ANALYSETECHNIKEN ZUR AUTOMATISCHEN VERTEILUNG
wird diese Architektureigenschaft bei der Verteilungsberechnung der Objekte
vor der Laufzeit in fast allen ODFs verwendet.
Statisch müssen alle ODFs die Laufzeitkonstante, die die Anzahl der abstrak-
ten JVMs darstellt, für ihre Berechnungen verwenden. Somit kann vor der
Laufzeit eine relative Zuordnung der Objekte und Threads zu den abstrakten
JVMs berechnet werden. Nachdem die verteilte Laufzeitumgebung gestartet
wurde, wird die Zahl der tatsächlich verfügbaren JVMs bekannt. So können
die abstrakten JVMs auf die realen abgebildet werden. Diese Laufzeitkonstan-
te bleibt während einer gesamten Programmausführung unverändert. Für die
nächste Ausführung kann sie aber einen anderen Wert annehmen, z.B. wenn
sich mehr Rechner an der verteilten Ausführung beteiligen sollen.
Rechner- und Netzwerkeigenschaften
Es gibt außerdem noch eine Reihe von Eigenschaften, die direkt die verteil-
te Ausführung des Programms beeinflussen, aber nicht in der Verteilungsbe-
rechnung der Objekte berücksichtigt werden können. Zu nennen sind u.a. die
Auslastung der CPUs, auf denen die JVMs gestartet werden. Die Latenz des
Netzes spielt bei der entfernten Kommunikation während der Ausführung ei-
ne wichtige Rolle, denn durch hohe Latenz kann die gesamte Performanz er-
heblich beeinträchtigt werden. Die Netzstruktur hat ebenfalls große Auswir-
kung auf die entfernte Kommunikation während der Programmausführung.
Da die Strukturierung des Netzes nicht bei der Platzierungsentscheidung be-
rücksichtigt wird, kostet die Kommunikation während des Programmablaufs
bei direkter Vernetzung der Rechner weniger als z.B. bei einer baumartigen.
Es ist technisch machbar, dass einige Faktoren wie die CPU-Last oder die
Netzwerktopologie in der verteilten Laufzeitumgebung zur Platzierung der
Objekte berücksichtigt werden. Hierbei könnten die verschiedenen Aktivitä-
ten basierend auf der vor der Laufzeit berechneten Workload auf die Rechner
je nach CPU-Last verteilt werden. So können zwei Aktivitäten mit geringem
Workload zusammen auf einem Rechner platziert, aber eine Aktivität mit grö-
ßerem Load allein auf einem Rechner erzeugt werden. Bei der empirischen
Untersuchung der Verteilung werden die Aktivitäten zusammen mit ihren
Datenobjekten einfach zyklisch auf die verfügbaren Rechner verteilt.
Man kann außerdem verschiedene Netztopologien über die Kommunikati-
onsschicht KaRMI, die der verteilten Laufzeitumgebung zugrunde liegt, kon-
figurieren. So könnte die Hardware-Infrastruktur der vernetzten Rechner wie
122
6.2. DYNAMISCHE PROGRAMMEIGENSCHAFTEN UND DAS
KOSTENMODELL ZUR OBJEKTVERTEILUNG
z.B. Myrinet/GM damit direkt ausgenutzt werden, so dass der Kommunikati-
onsaufwand reduziert wird. Es könnte einiges an Performanz für die verteilte
Ausführung bringen. Solche dynamischen Aspekte werden hierbei aber nicht
weiter vertieft untersucht.
6.2. Dynamische Programmeigenschaften und das
Kostenmodell zur Objektverteilung
Dynamische Programmeigenschaften modellieren die tatsächlichen Laufzeit-
und Kommunikationskosten, die während der Ausführung eines Programms
entstehen. Auf der Grundlage dieser Eigenschaften werden fundamenta-
le Verteilungsentscheidungen mittels Basis-Verteilungsfunktionen (siehe Ab-
schnitt 5.3.2) getroffen. Aufgrund der Tatsache, dass diese Programmeigen-
schaften zum größten Teil das Laufzeitverhalten des Programms wider-
spiegeln und deshalb vor der Laufzeit nicht komplett ausgerechnet wer-
den können, müssen Teile davon mit geeigneten Strategien abgeschätzt
werden. Das gilt für die Programmeigenschaften EWork(A, O),EexecCost(A),
EcommCost(Oi, Oj)und EcommCost(A, O), die in einigen vorgestellten ODFs ver-
wendet werden. Im Folgenden werde ich auf die Modellierung der obigen
Programmeigenschaften mit einem passenden Kostenmodell eingehen. Die
Ausführungs- und Kommunikationskosten werden dabei mit entsprechen-
den (noch) unbekannten Laufzeitfaktoren modelliert. Auf diesem Modell
werden diese Kosten vor der Laufzeit ausgerechnet, indem geeignete Ab-
schätzungsmethoden für die Laufzeitfaktoren angewandt werden. Anschlie-
ßend wird das Konzept zur hybriden Analyse anhand des zugrundliegenden
Kostenmodells zur Objektverteilung nach der Work-Eigenschaft dargestellt.
6.2.1. Modellierung dynamischer Programmeigenschaften
Zur Modellierung der obigen Programmeigenschaft EWork(A, O)und
EcommCost(A, O), auch als verrichteter Workload und verursachte Kommuni-
kation einer Aktivität auf einem Objekt bezeichnet, wird u.a. ein Modell ele-
mentarer Kosten benötigt. Sie werden im Folgenden modelliert und die erfor-
derlichen Laufzeitfaktoren werden hierbei definiert.
123
KAPITEL 6. ANALYSETECHNIKEN ZUR AUTOMATISCHEN VERTEILUNG
Ausführungskosten
Der Rumpf einer Methode kann aus einer Sequenz von einfachen Anweisun-
gen, Aufrufstellen, Schleifen und Verzweigungen bestehen. Die letzten drei
genannten stellen komplexere Strukturen dar. Deren Kosten sind laufzeitab-
hängig, daher müssen sie mit bestimmten Laufzeitfaktoren modelliert wer-
den.
Definition 6.0:
Es sei branch(instr)[0,1] die Ausführungswahrscheinlichkeit einer Instruktion
innerhalb einer Verzweigungsstruktur und loop(l)0die Ausführungshäufigkeit
einer Schleife l. Seien EexecCost(m)die Ausführungskosten einer Methode m.
Dann ist EexecCost(m)die Summe der Ausführungskosten aller Anweisungen der
Methode m. Die Ausführungskosten der verschiedenen Anweisungen werden wie
folgt festgelegt:
Für eine elementare Anweisung wird ihre Ausführungsdauer einfach (mit
Wert 1 gewichtet) ausgerechnet.
Für eine Anweisung, die eine Aufrufstelle für Unteraufrufe lokaler Methoden
(über this-Receiver) oder statischer Methoden darstellt, sind ihre Ausfüh-
rungskosten die Ausführungsdauer der aufgerufenen Methode.
Für eine Anweisung instr innerhalb einer Verzweigungsstruktur wird ihre
Ausführungsdauer mit dem Laufzeitfaktor branch(instr)multipliziert.
Für eine Anweisung instr innerhalb einer Schleife lwird ihre Ausführungs-
dauer mit dem Laufzeitfaktor loop(l)multipliziert.
Die Ausführungskosten der Unteraufrufe von Methoden anderer Objekte werden
ignoriert. 4
Zwei Laufzeitfaktoren branch(instr)und loop(l)wurden somit definiert. Bei ei-
ner Verzweigungsstruktur gibt der Faktor branch(instr)an, mit welcher Wahr-
scheinlichkeit eine Instruktion innerhalb eines Zweiges ausgeführt wird. Der
Laufzeitfaktor loop(l)modelliert die Anzahl der auszuführenden Iterationen
der Schleife lzur Laufzeit. Zur Auflösung der Unteraufrufe lokaler Methoden
liegt der Aufrufgraph zugrunde, der mittels der PreciseCallGraph-Analyse
124
6.2. DYNAMISCHE PROGRAMMEIGENSCHAFTEN UND DAS
KOSTENMODELL ZUR OBJEKTVERTEILUNG
(siehe Abschnitt 6.3.1) bestimmt wird. Für den Fall, dass starke Zusammen-
hangkomponenten oder Rekursion bei der Berechnung der Ausführungskos-
ten für die Unteraufrufe existieren, wird eine Fixpunkt-Berechnung verwen-
det. In diesem Fall werden die Kosten solange berechnet, bis die Lösung stabil
ist. Anderenfalls darf die Anzahl der durchgeführten Berechnungsiterationen
einen fest definierten Grenzwert nicht überschreiten.
In diesem Ansatz wird zur Vereinfachung angenommen, dass die Ausfüh-
rungshäufigkeiten elementarer Anweisungen (Instruktionen) gleich bleiben.
Mit dieser Definition kann also die Arbeitslast einer Methode mittels ihrer
Ausführungskosten bestimmt werden. Diese Kosten werden tatsächlich ver-
ursacht, wenn die Methode in einem Adressraum ausgeführt wird. Das ist
auch der Grund, weshalb die Ausführungskosten der Unteraufrufe von Me-
thoden anderer Objekte nicht mit gerechnet werden. Denn diese Kosten ent-
stehen nur im Adressraum, wo sich diese Objekte befinden.
Definition 6.1:
Sei mdie zur Laufzeit als Aufrufziel aufgelöste Methode an einer dynamisch
gebundenen Aufrufstelle i. Dann sind EexecCost(m)die Ausführungskosten der
Anweisung i.4
Nun kann die verrichtete Workload einer Aktivität Aiauf einer Methode m
Ojdefiniert werden, die von Aiaus direkt oder indirekt über andere Objekte
und/oder this-Receiver aufgerufen wird.
Definition 6.2:
Seien EexecCostm(Ai, Oj)die Ausführungskosten, die durch die Ausführung der
Methode mOjvon der Aktivität Aiaus entstehen. EexecCostm(Ai, Oj)ist das
Produkt aus der Anzahl der direkt und indirekt ausgeführten Aufrufe der Methode
mmit EexecCost(m). Alle betrachteten Aufrufe finden während der gesamten
Abarbeitung der run()-Methode von Aistatt. 4
Unter indirekten Aufrufen kann man sich vorstellen, dass Aieine Methode n
eines anderen Objekts Okaufruft und mwird innerhalb der Methode nauf-
gerufen. Statisch werden alle Aufrufstellen von Aiaus betrachtet, an denen m
direkt oder indirekt aufgerufen werden kann. Die Ausführungskosten an ein-
zelnen Aufrufstellen der Methode mwerden ggf. mit entsprechenden Lauf-
zeitfaktoren multipliziert. Somit ergibt sich die gesamte Anzahl der auszu-
führenden Methodenaufrufe.
125
KAPITEL 6. ANALYSETECHNIKEN ZUR AUTOMATISCHEN VERTEILUNG
Definition 6.3:
EWork(A, O)sei der verrichtete Workload einer Aktivität Aauf ein Objekt O. Dann
ist EWork(A, O) := PEexecCostm(A, O)für alle Methoden mO, die während der
gesamten Abarbeitung der run()-Methode von Aaufgerufen werden.
Seien EexecCost(A)die Ausführungskosten einer Aktivität A. Dann ist dies die
Summe der Ausführungskosten der run()-Methode von A,EexecCost(runA), und
der verrichteten Workload EWork(A, O)auf allen Objekten O, auf die Azugreift. 4
Mit diesen Definitionen kann die dynamische Programmeigenschaft zu
Workload genau modelliert werden. EexecCost(runA)ist also die Arbeitslast
einer Aktivität A. Wie im Abschnitt 6.1.2 beschrieben, könnte die vor der
Laufzeit berechneten Kosten verschiedener Aktivitäten zu ihrer Platzierung
unter Berücksichtigung der CPU-Load verwendet werden, anstatt sie einfach
zyklisch auf die verfügbaren Rechner zu verteilen. Die tatsächlichen Ausfüh-
rungskosten sind aber erst zur Laufzeit bekannt. Zur Berechnung der Vertei-
lungsentscheidung vor der Laufzeit, die sich auf die Workload bezieht, müs-
sen die Laufzeitfaktoren abgeschätzt werden.
Abschätzungen
Die vor der Laufzeit abzuschätzenden Faktoren sind die Ausführungswahr-
scheinlichkeit branch(instr)einer Instruktion instr in einer Verzweigungs-
struktur, die Ausführungshäufigkeit loop(l)einer Schleife lund Ausführungs-
kosten von Aufrufen dynamisch gebundener Methoden. So wird die Ausfüh-
rung einer Instruktion instr innerhalb einer n-Verzweigungsstruktur mit einer
Wahrscheinlichkeit branch(instr) = 1
nabgeschätzt. Die Häufigkeit zur Ausfüh-
rung einer Schleife lwird mit einem Faktor 10 abgeschätzt. Mittels hybrider
Analyse, die im Abschnitt 6.3.3 beschrieben wird, können diese abgeschätz-
ten Faktoren durch reale Werte zur Laufzeit ersetzt werden. Somit können
Verteilungsentscheidungen verbessert werden.
An einer Aufrufstelle, an der die statische Auflösung dynamischer Metho-
denbindung keine eindeutige Lösung liefert, müssen die Ausführungskosten
dort ebenfalls abgeschätzt werden. So werden die Ausführungskosten einer
solchen Aufrufstelle mit dem Mittelwert abgeschätzt, der aus den Ausfüh-
rungskosten der statisch aufgelösten Aufrufziele gebildet wird. Die gefun-
denen Aufrufziele sind das Ergebnis der PreciseCallGraph-Analyse, die im
Abschnitt 6.3.1 beschrieben wird.
126
6.2. DYNAMISCHE PROGRAMMEIGENSCHAFTEN UND DAS
KOSTENMODELL ZUR OBJEKTVERTEILUNG
Die Festlegung, dass die Ausführungsdauer einer elementaren Anweisung
nur einfach gerechnet wird (mit Wert „1“), ist ebenfalls eine Abschätzung.
Um aber die Präzision bei der Kostenberechnung vor der Laufzeit zu erhö-
hen, können diese elementaren Instruktionen in verschiedene Gruppen auf-
geteilt werden, die bei der realen Ausführung unterschiedliche Ausführungs-
dauer aufweisen [DPW01]. Nach dieser Gruppierung können Metriken zur
Ausführungsdauer der Instruktionen aufgestellt [HD04] und anhand derer
die Ausführungskosten genauer modelliert werden.
Mit solchen Abschätzungen können die dynamischen Programmeigen-
schaften vor der Laufzeit ausgerechnet werden, die die Ausführungskos-
ten des Programms modellieren und in einigen wichtigen Basis-ODFs,
fTHREADWORK() und fEX_DISTRIBUTEDTHREAD() verwendet werden.
Kommunikationskosten
Unter Kommunikation können alle Arten der Interaktion zwischen den Ob-
jekten verstanden werden, bei der Daten über das Netzwerk geschickt wer-
den. Das sind Methodenaufrufe und Feldzugriffe, die im Rahmen der Pro-
grammausführung stattfinden. Außerdem entstehen nur Kommunikations-
kosten, wenn ein Objekt von einem anderen oder von einer Aktivität entfernt
platziert wird. Die Modellierung der Kommunikationskosten beruht auf dem
Entwurfskonzept des JScatter-Systems: Die Feldzugriffe auf remote-Objekte
werden zu Methodenaufrufen transformiert. Außerdem wird das Handle-
Konzept hier verwendet, damit ein Handle anstatt das tatsächliche remote Ob-
jekt verwendet wird. Die tatsächlichen Aufrufe werden von Handles an die
richtigen Objekte weiter delegiert. Bei entfernten Methodenaufrufen werden
die Handles anstelle von remote Objekten als Parameter bzw. Ergebnis über
das Netzwerk gesendet. Im Gegensatz dazu müssen die nicht-remote-Objekte
oder lokalen Objekte in entfernten Methodenaufrufen selbst als Werte überge-
ben werden. Daher spielt die Größe solcher Objekte und der Handles, die al-
lerdings eine einheitliche Größe besitzen, bei der Kommunikation eine wich-
tige Rolle. Die Größe von Werten eines Grundtyps ist bekannt.
Abschätzung zu Objektgrößen für Kommunikation
Für Handles und normale Objekte gilt, dass deren abgeschätzte Größe die
Summe der abgeschätzten Größen seiner nicht-statischen Felder ist. Felder
127
KAPITEL 6. ANALYSETECHNIKEN ZUR AUTOMATISCHEN VERTEILUNG
eines Objekts haben entweder einen Grundtyp oder sind Referenzen auf Ob-
jekte. Wenn ein Feld die Referenz auf ein remote-Objekt enthält, dann kann die
Größe dessen Handles dafür angenommen werden. Im Fall eines lokalen Ob-
jekts wird dann wieder dessen geschätzte Größe verwendet. Wenn der Typ
für eine Instanzvariable nicht eindeutig ist, müssen die geschätzten Größen
aller Objekte berechnet werden, deren Typen vor der Laufzeit in Frage kom-
men. Dann wird der Mittelwert davon berechnet. Wenn ein Feld rekursive
Datenstrukturen enthält, muss ein fester Faktor bei der Berechnung verwen-
det werden.
Definition 6.4:
Seien EcommCostm(Oi, Oj)die Kommunikationskosten, die durch alle direkten Auf-
rufe der Methode mOjvon einem Objekt Oiaus verursacht werden. Die Kosten
für einen Methodenaufruf setzen sich aus den abgeschätzten Größen der Objekte, die
als Parameter und Rückgabe übergeben werden, und EremoteCallCost() zusammen.
Die Kommunikationskosten einer Aktivität Amit einem Objekt Oüber eine Methode
min O, bezeichnet als EcommCostm(A, O), setzen sich ebenfalls aus den abgeschätzten
Größen der Objekte, die als Parameter und Rückgabe beim Aufruf von mübergeben
werden, und EremoteCallCost() zusammen.
EremoteCallCost() seien die feste Kosten, die bei einem entfernten Methodenaufruf
immer verursacht werden. 4
Zur Berechnung dieser Programmeigenschaften vor der Laufzeit werden
alle Aufrufstellen der Methode m, ggf. mit einer Ausführungshäufigkeit
oder -wahrscheinlichkeit multipliziert, mit in die Betrachtung einbezogen.
So können statisch die Kommunikationskosten zweier Objekte über eine
Methode ermittelt werden. Im Fall einer dynamischen Methodenbindung
wird ähnlich wie bei EexecCostm(Ai, Oj)abgeschätzt, also der Mittelwert der
Kommunikationskosten aller gefundenen Aufrufziele an einer Aufrufstelle
ermittelt.
Da es sich bei Kommunikationskosten nur um entfernte Zugriffe handelt,
wird angenommen, dass lokal ausgeführte Methodenaufrufe keine Kommu-
nikation verursachen, obwohl lokale Kommunikation, z.B. zur verteilten
Laufzeitumgebung, nicht Null ist. Es gibt viele Faktoren, die direkt die Dauer
eines entfernten Methodenaufrufs beeinflussen: die Serialisierung der Para-
meter bzw. Rückgabewerte, Holen der Stubs, das eigentliche Senden über das
Netzwerk, der Kontakt mit dem Skeleton, die Deserialisierung usw. Die meis-
ten von ihnen hängen von den Größen der Parameter bzw. Rückgabewerte ab.
128
6.2. DYNAMISCHE PROGRAMMEIGENSCHAFTEN UND DAS
KOSTENMODELL ZUR OBJEKTVERTEILUNG
Zur Vereinfachung werden bei der Modellierung der Kommunikationskosten
nur die Größen der als Parameter bzw. Rückgabewerte versendeten Struk-
turen und ein fester Kostenfaktor für jeden entfernten Methodenaufruf, also
EremoteCallCost(), berücksichtigt. Die restlichen Faktoren werden ignoriert.
Definition 6.5:
EcommCost(Oi, Oj)seien die verursachten Kommunikationskosten eines Objekts Oi
auf ein Objekt Oj.m, n seien Methoden. Dann ist:
EcommCost(Oi, Oj) :=
Oicalls m
X
mOj
EcommCostm(Oi, Oj)+
X
OlM
Ojcalls n
X
nOl
EcommCostn(Oj, Ol),
wobei M={OlO|EcommCost(Oi, Ol) = 0 callsite(n) liegt in m}.
(6.1)
Analoges gilt für EcommCost(A, O).4
Die anfallenden Kommunikationskosten zwischen zwei voneinander ent-
fernt platzierten Objekten Oiund Ojergeben sich nicht nur aus den
Kommunikationskosten entfernter Methodenaufrufe von Oizu Ojüber eine
Methode m, sondern auch aus den anfallenden Kosten durch Aufrufe der Me-
thoden anderer zu Oilokalen Objekte während der Ausführung von m. Der
Grund für diese Modellierung der Kommunikationskosten liegt darin, dass
durch die Platzierung von Oiund Ojauf zwei verschiedenen JVMs die lo-
kalen Aufrufe von Methoden anderer Objekte während der Kommunikation
zwischen Oiund Ojzu entfernten Aufrufen werden. Diese Kosten müssen
auch berücksichtigt werden. Wenn eine zyklische Abhängigkeit der Aufruf-
beziehung existiert, dann wird die oben beschriebene Fixpunkt-Berechnung
für die Kommunikationskosten eingesetzt.
Durch die Abschätzung der Laufzeitfaktoren im letzten Abschnitt und der
Objektgrößen ist man in der Lage, dynamische Programmeigenschaften
zur Kommunikation vor der Laufzeit auszurechnen. So kann eine Vertei-
lungsentscheidung basierend auf diesen Eigenschaften sowie der Workload-
Eigenschaften für alle Aktivitäts- und Objektrepräsentanten berechnet wer-
den.
129
KAPITEL 6. ANALYSETECHNIKEN ZUR AUTOMATISCHEN VERTEILUNG
Regeln zur Anwendung dynamischer Programmeigenschaften
Dynamische Programmeigenschaften werden dazu verwendet, um grundle-
gende Verteilungsentscheidungen mittels Basis-ODFs zu berechnen. Durch
Abschätzungen der Ausführungs- und Kommunikationskosten können diese
Eigenschaften statisch bestimmt werden. Je nach Basis-ODFs werden die bei-
den Arten der Kosten einzeln oder auch kombiniert verwendet. Damit es bei
der Verwendung nicht zu ungünstigen Ergebnissen kommt, müssen einige
Berechnungsregeln beachtet werden.
Die Platzierungsentscheidung der Aktivitäten und Objekte erfolgt immer
nach dem Prinzip, dass zunächst alle Aktivitäten auf die abstrakten JVMs
verteilt werden. Objekte werden den Aktivitäten nach EWork(A, O)oder
EcommCost(A, O)zugeordnet, sobald die zugehörigen Programmeigenschaften
ausgerechnet werden. Zuletzt wird die Zuordnung der Objekte zu den Ob-
jekten bestimmt, die den Aktivitäten schon zugeordnet sind.
Bei der Platzierung eines Objekts Ozu einer Aktivität Aunter Verwen-
dung beider Arten der Kosten muss sichergestellt werden, dass Adie
meiste Rechenzeit mit Overbringt. Gleichzeitig müssen die anfallenden
Kommunikationskosten minimal gehalten werden. Denn durch diese Zu-
sammenplatzierung müssen andere Aktivitäten und Objekte, die in anderen
Adressräumen platziert sind, entfernt auf Ozugreifen. Dieses Berechnungs-
modell lehnt sich an das Lastmodell in [Hau98] an und wird auch bei den
ODFs fTHREADWORKCOMM und fEX_DISTRIBUTEDTHREAD eingesetzt.
6.2.2. Konzept zur hybriden Analyse
Die hybride Analyse ist eine Kombination aus statischer und dynami-
scher Analyse. Ziel der Analyse ist es, möglichst genaue Ausführungskos-
ten zur Platzierungsberechnung zu bestimmen. Die statischen Analysetech-
niken werden eingesetzt, um die Ausführungskosten einer Methode zu be-
rechnen. So können Anteile der Methode m, die nicht von einem Lauf-
zeitfaktor abhängen, komplett vor der Laufzeit ausgerechnet werden. Im
Folgenden werden die Kosten dieser Anteile als EfixedCost(m)bezeichnet.
Die von einem Laufzeitfaktor abhängenden Kosten einer Methode, auch als
EvariableCost(m)bezeichnet, werden dagegen vor der Laufzeit abgeschätzt.
Dynamische Analyse kommt zur Laufzeit zum Einsatz, um die tatsächlichen
Laufzeitwerte für solche Faktoren zu bestimmen. Sobald ein Laufzeitwert
130
6.2. DYNAMISCHE PROGRAMMEIGENSCHAFTEN UND DAS
KOSTENMODELL ZUR OBJEKTVERTEILUNG
vorliegt, wird der zugehörige Faktor aktualisiert. Dies löst erneute Berech-
nung von EvariableCost(m)und somit der Ausführungskosten der Methode
aus. Infolgedessen werden Verteilungsentscheidungen für die zukünftig zu
erzeugenden Objekte basierend auf den genaueren Kosten getroffen.
Da die Kommunikationskosten von vielen Laufzeitfaktoren abhängen, die
sich sehr schwierig oder überhaupt nicht modellieren lassen, kann dieses
Konzept nicht dafür verwendet werden. Beispiel hierfür ist das eigentliche
Versenden der Objektstrukturen über das Netzwerk. Dafür müssen dann Ab-
schätzungen verwendet werden. Als Abhilfe könnten Strategien zur dyna-
mischen Lastbalancierung zur Laufzeit zusätzlich angewandt werden. Somit
können viel miteinander kommunizierende Threads und Objekte zur Laufzeit
nachträglich zusammen platziert werden, so dass Kommunikationsaufwand
reduziert wird. Zur Motivation wird dieses Konzept an einem einfachen Bei-
spiel demonstriert, wobei die Ausführungshäufigkeit der Schleifen genau be-
rechnet wird.
Hybride Analyse für Schleifen
Class Worker1 extends Thread
{
...
public void run()
{
Tour t;
while ((t = _workQueue.receiveWork()) != null) {
solve(t);
}
}
...
}
Tour receiveWork()
{
Tour t;
// get a work from local queue
t = doReceiveWork(false);
if (t != null) return t;
// look for a work in other queues
int nextThread = threadNum;
for (; ;) {
for (int i = 1; i < TSP.numThreads; i++) {
nextThread = (nextThread + i) % TSP.numThreads;
WorkQueue nextQueue = QueueOfQueues[nextThread];
t = nextQueue.doReceiveWork(false);
if (t != null) return t;
}
// No work has been found. Traverse the queues once again,
// to suspend yourself on all queues until new work is created.
int timeOfLastNotify = WorkQueue.getTimeOfLastNotify();
nextThread = threadNum;
for (int i = 1; i < TSP.numThreads; i++) {
nextThread = (nextThread + i) % TSP.numThreads;
WorkQueue nextQueue = QueueOfQueues[nextThread];
t = nextQueue.doReceiveWork(true);
if (t != null) return t; // this time a work is found.
}
if (!WorkQueue.WaitForWork(timeOfLastNotify)) { // suspend yourself
return null; // no more work is left to be done.
}
} // try until you succeed.
}
VerzweigungVerzweigung
Schleife
Dyn. Methodenbindung
Abbildung 6.1.: TSP-Beispiel zur hybriden Analyse für Schleifen
In Abbildung 6.1 wird ein Ausschnitt eines Programms dargestellt (Trave-
ling Salemans Problem - TSP). In der run()-Methode des Worker1-Threads
131
KAPITEL 6. ANALYSETECHNIKEN ZUR AUTOMATISCHEN VERTEILUNG
wird die Methode receiveWork() aufgerufen, deren Implementierung in
der Abbildung ebenfalls dargestellt wird. Dort sind einige besonders gekenn-
zeichnete Stellen (mit einem Pfeil) zu erkennen, an denen die oben definier-
ten Laufzeitfaktoren vorkommen. Betrachten wir nun die drei vorkommen-
den Schleifen l1,l2und l3in der Methode, deren Rumpf mit entsprechenden
Rechtecken umfasst wird. Es seien hierbei die Ausführungswahrscheinlich-
keit der Verzweigungen und die Aufrufziele an den Aufrufstellen nicht lo-
kaler Methoden fest. Dann sieht die Formel für die Ausführungskosten der
Methode receiveWork() folgendermaßen aus:
EexecCost(receiveW ork()) :=EfixedCost(receiveW ork())+
loop(l1)(loop(l2)EexecCost(l2) + loop(l3)EexecCost(l3)+
EexecCost(l1))
(6.2)
Zur Verteilungsberechnung wird vor der Laufzeit die Anzahl der Durchläufe
aller Schleifen mit 10 abgeschätzt, also loop(l1) = loop(l2) = loop(l3) = 10. Durch
die Abhängigkeitsanalyse für Schleifengrenzen wird statisch festgestellt, dass
l2und l3einen gemeinsamen Grenzwert für ihre Iterationen besitzen. Die-
ser Grenzwert wird durch TSP.numThreads definiert. Für l1kann keine ge-
naue Obergrenze berechnet werden, daher bleibt weiterhin loop(l1) = 10. Nun
werden Äquivalenzklassen für die Schleifen gebildet, die gemeinsame Schlei-
fengrenzen besitzen. So werden l2und l3zu einer Äquivalenzklasse < l2>
zusammengefasst mit loop(< l2>) = TSP.numThreads (=loop(l2) = loop(l3))
,l1wird zu < l1>mit loop(< l1>) = 10. Die Kardinalität von < l2>ist 2, die
von < l1>ist 1. Der obige Term kann nun kompakter dargestellt werden:
EexecCost(receiveW ork()) :=EfixedCost(receiveW ork())+
loop(< l1>)(loop(< l2>)(EexecCost(l2) + EexecCost(l3))+
EexecCost(l1))
(6.3)
Für die nachträgliche Verteilungsberechnung wird nur Äquivalenzklasse mit
maximaler Kardinalität in Betracht gezogen, hier also für < l2>. Nachdem
der Wert der Variable TSP.numThreads (= 5) zur Laufzeit bekannt war,
wird EexecCost(receiveWork()) mit dem neuen Wert loop(< l2>)=5erneut
berechnet. Die Ausführungskosten der Methode receiveWork() werden
hiermit zur Laufzeit genauer ausgerechnet und die Verteilungsentscheidung
132
6.2. DYNAMISCHE PROGRAMMEIGENSCHAFTEN UND DAS
KOSTENMODELL ZUR OBJEKTVERTEILUNG
wird erneut getroffen. Dieses Beispiel zeigt eine günstige Situation, denn die
Laufzeitkonstante TSP.numThreads ist schon bekannt, bevor die Methode
receiveWork() zum ersten Mal ausgeführt wird.
Im Allgemeinen können die Ausführungskosten einer Methode erst vollstän-
dig berechnet werden, nachdem die erste Schleife der betrachteten Äquiva-
lenzklasse < l > vollständig durchlaufen wurde und somit der Wert des zu-
gehörigen Laufzeitfaktors loop(< l >)bekannt ist. So können Platzierungsent-
scheidungen weiterer zu erzeugender Objekte von solchen genaueren Kosten
profitieren.
Ausführungskosten einer Methode
Wenn man die anderen Laufzeitfaktoren betrachtet, können die Ausführungs-
kosten einer Methode im Allgemeinen so modelliert werden:
EexecCost(m) :=EfixedCost(m) + EvariableCost(m)
:=EfixedCost(m)+
X
i
branch(bi)EexecCost(bi) + X
j
loop(lj)EexecCost(lj)+
X
k
EexecCost(target(ck))
(6.4)
wobei bieine Verzweigungsstruktur, ljeine Schleife und ckeine Aufrufstel-
le in der Methode mmit den potentiellen Aufrufzielen mk1, . . . , mkjsei. In
JScatter habe ich das Konzept zur hybriden Analyse nur für Schleifen ange-
wandt und im Abschnitt 8.3.2 evaluiert. Die beiden anderen Faktoren werden
abgeschätzt. Im Folgenden wird ein grobes Verfahren zur hybriden Analyse
beschrieben.
Das Verfahren
Um die Ausführungskosten als Terme allgemein zu modellieren, wird zu-
nächst eine einfache statische Analyse [Muc97] zur Erkennung der Schlei-
fen durchgeführt. Wie im obigen Abschnitt über Abschätzungen beschrieben,
wird die Anzahl der Schleifendurchläufe zunächst mit einem Faktor 10 abge-
schätzt. Mit Hilfe des Verfahrens zur Induktionsvariable können Schleifen-
bedingungen und im günstigen Fall auch obere Grenzen erkannt werden.
133
KAPITEL 6. ANALYSETECHNIKEN ZUR AUTOMATISCHEN VERTEILUNG
Durch die interprozedurale Def-Use-Analyse können Abhängigkeiten zwi-
schen den Schleifengrenzen herausgefunden werden. Die Schleifen, die ge-
meinsame Grenzwerte besitzen, werden dann in Äquivalenzklassen zusam-
mengefasst. Die Terme werden danach entsprechend optimiert. Der Faktor
loop einer Äquivalenzklasse gilt für alle dazu gehörigen Schleifen, wie im obi-
gen Beispiel gezeigt wurde.
Um zur Laufzeit den tatsächlichen Grenzwert für eine Äquivalenzklasse zu
finden, muss das Programm an den richtigen Stellen instrumentiert werden.
Es sollte Instrumentierung so wenig wie möglich erfolgen, damit keine Zu-
satzkosten verursacht werden. Dazu werden vor der Laufzeit nur Äquiva-
lenzklassen mit maximaler Kardinalität betrachtet. Durch die interprozedu-
rale Kontrollabhängigkeitsanalyse können die zuerst ausgeführten Schleifen
erkannt werden, und dort wird Instrumentierungscode eingeführt.
Zur Laufzeit, nachdem die Ausführungskosten durch Bekanntgabe solcher
loop-Faktoren erneut ausgerechnet wurden, müssen die von solchen Kosten
abhängigen Verteilungsentscheidungen ebenfalls erneut bestimmt werden.
Im Abschnitt 6.3.3 werden einige Erweiterungen zur Erhöhung der Genau-
igkeit bei der hybriden Analyse diskutiert.
6.3. Grundlegende statische Analysen
Alle Programmeigenschaften, sowohl statische als auch dynamische, werden
mittels statischer Programmanalysen berechnet und ggf. abgeschätzt. Die Ein-
gabe liegt in Form von Java-Bytecode vor, worauf die Analysetechniken und
die Transformation angewandt werden. Zur Beschaffung elementarer Analy-
seergebnisse wie z.B. des Kontrollflussgraphen, des Dominatorbaumes oder
der Schleifenstruktur einer Methodeimplementierung werden viele funda-
mentale Analysetechniken eingesetzt. Sie sind als verschiedene Module un-
serer Analyseumgebung (siehe Abschnitt 6.1.2 in [Thi01]) realisiert.
Im Folgenden werde ich auf die Erweiterung der vorhandenen PreciseCall-
Graph-Analysetechnik und die GlobalDefUse-Analyse eingehen, die bei der
Verteilungsberechnung eine wichtige Rolle spielen. Abschließend wird be-
schrieben, wie einige ausgewählte Programmeigenschaften mittels vorhan-
dener Analysen berechnet werden.
134
6.3. GRUNDLEGENDE STATISCHE ANALYSEN
6.3.1. Aufrufgraph-Analyse zur Auflösung dynamischer
Methodenbindung
Abbildung 6.2.: Ergebnis der PreciseCallGraph-Analyse
Das statische Analyseverfahren zur Konstruktion eines Aufrufgraphen be-
rechnet für den Analysekontext die Aufrufbeziehung der Methoden auf
der Grundlage der Klassenhierarchie und Informationen zur Typinferenz.
Ein Analysekontext enthält die Anwendung und alle benutzten Bibliothek-
Klassen. Die dort verwendete Typinferenz-Analyse ist eine Variante der
Points-to-Analyse [Le00], mit deren Hilfe man den Receivertyp genauer be-
stimmen kann, um aufgerufene Ziele einzuschränken. In Abbildung 6.2 ist
ein Aufrufgraph als Ergebnis der Analyse zu sehen. Die grau markierten
Knoten repräsentieren Methoden, die nicht im Analysekontext oder native
sind und deren Implementierung daher nicht bestimmt werden kann. Die
Aufrufrelation zwischen diesen Knoten kann nicht vom ursprünglichen Ver-
fahren beschafft werden. Das sind Aufrufbeziehungen zwischen start()-
und run()-Methoden eines Threads sowie Relation einer Aufrufstelle zur
<clinit>-Methode.
Erweiterung des Konstruktionsverfahrens
Die genannte, fehlende Information ist wichtig für die Berechnung eini-
ger grundlegender Programmeigenschaften wie isStagewiseImmutable,
Work usw. Die Aufrufrelation zwischen der start()- und run()-Methode
der Thread-Klasse oder Unterklassen davon wurde nicht behandelt, weil
start() eine native-Methode ist. Nach spezieller Betrachtung deren Imple-
mentierung läßt sich feststellen, dass die zugehörige run()-Methode zur
135
KAPITEL 6. ANALYSETECHNIKEN ZUR AUTOMATISCHEN VERTEILUNG
Laufzeit von dort aus aufgerufen wird. Statisch müsste aber ganz konserva-
tiv angenommen werden, dass von jedem Knoten für eine start()-Methode
Kanten zu jedem run()-Knoten existieren sollen. Mit Hilfe der Typinferenz-
Analyse kann vor der Laufzeit der Typ des Receivers zum start()-Aufruf
genauer berechnet werden. Somit können die Aufrufziele eingeschränkt wer-
den und die Anzahl der von start()-Knoten ausgehenden Kanten wird da-
durch erheblich reduziert.
Wenn eine Klasse einen statischen Anteil, z.B. eine Klassenvariable, besitzt,
erzeugt der Compiler bei der Übersetzung automatisch eine so genannte
<clinit>-Methode, die für die Initialisierung statischer Anteile der Klas-
se zuständig ist. Nach der JVM-Spezifikation [T. 99] wird diese einmalig
aufgerufen und zwar bei der allerersten Benutzung des statischen Anteils
der Klasse. Der Aufruf der <clinit>-Methode kann durch die Instrukti-
on PUTSTATIC,GETSTATIC oder die Erzeugung eines Objekts der Klasse
durch die Instruktion NEW ausgelöst werden. Um zusätzlich Aufrufrelationen
zu solchen <clinit>-Knoten aufzubauen, werden bei der Konstruktion des
Aufrufgraphen nicht nur Aufrufstellen, also Invoke-Instruktionen, sondern
auch die oben genannten Instruktionen analysiert. Sie können den Aufruf der
zugehörigen <clinit>-Methode auslösen. Hierbei wird bei jeder solchen In-
struktion eine Kante zu der zugehörigen <clinit>-Methode konstruiert.
Die zugehörige <clinit>-Methode läßt sich einfach finden, denn die In-
formation darüber, welche Klasse bei GETSTATIC,PUTSTATIC oder NEW
verwendet wird, ist direkt bei diesen Instruktionen verfügbar. Außerdem
muss ggf. die <clinit>-Methode der Oberklasse aufgerufen und bearbei-
tet werden, bevor die erste Anweisung der aktuellen <clinit>-Methode
ausgeführt wird. Diese automatisch ausgelöste Aufrufkette muss bei der
Aufrufgraphen-Konstruktion berücksichtigt werden. So können Kanten zwi-
schen <clinit>-Knoten der Unter- und Oberklassen im Aufrufgraphen exis-
tieren.
In Abbildung 6.3 wird das Ergebnis der erweiterten Aufrufgraph-Analyse
dargestellt. In einem Aufrufgraphen können drei neue Sorten der Kanten
vorkommen: von start()-Knoten zu run()-Knoten, von einem Knoten zu
<clinit>-Knoten, wobei diese noch voneinander unterschieden werden: ei-
ne durch NEW und eine durch PUTSTATIC oder GETSTATIC aufgelöste Be-
ziehung. Der erweiterte Aufrufgraph kann außerdem auch Kanten enthalten,
die die <clinit>-Knoten mit <clinit>-Knoten der Oberklasse verbinden.
Das Beispiel in Abbildung besitzt solche Kanten nicht.
136
6.3. GRUNDLEGENDE STATISCHE ANALYSEN
Abbildung 6.3.: Ergebnis der erweiterten PreciseCallGraph-Analyse
Der erweiterte Aufrufgraph enthält außerdem zusätzliche Information dar-
über, von welcher Instruktion eine Kante ausgeht. Diese Information ist erfor-
derlich für die Berechnung der interprozeduralen Post-Dominator-Relationen
(siehe Abschnitt 6.4.2 und [P. 01]). Die üblichen native-Methoden oder diejeni-
gen außerhalb des Analysekontexts werden weiterhin als Knoten des Aufruf-
graphen modelliert. Nur detailierte Information dazu wie deren Implemen-
tierung oder davon ausgehende Kanten, wird nicht berechnet (in Abbildung
6.3 werden sie als graue Knoten dargestellt).
Der berechnete Aufrufgraph dient als Grundlage zur statischen Auflösung
der Aufrufziele, insbesondere dynamisch gebundener Methode. Durch die
Erweiterung des Konstruktionsverfahrens stehen die Informationen über
Aufrufbeziehung innerhalb eines Threads und zu Methoden zur statischen
Initialisierung zur Verfügung. Dieser Informationsgehalt ist ausreichend für
die hierauf aufbauende Berechnung der Programmeigenschaften.
6.3.2. Interprozedurale Def-Use-Analyse (GlobalDefUse-Analyse)
Definitions-/Verwendungsketten für lokale Variablen und stackslots innerhalb
einer Methode werden mittels der LocalDefUse-Analyse aufgebaut, die oben
erwähnt und in [Thi01] beschrieben wurde. Dieser Ansatz soll nun so erwei-
tert werden, dass von einer Definition Verbindungsketten zu allen ihrer Ver-
wendungen im gesamten Analysekontext und umgekehrt aufgebaut werden.
Solche Verbindungen, die über Methodengrenzen hinausgehen, werden im
Folgenden interprozedurale Definitions-/Verwendungsketten genannt, abge-
137
KAPITEL 6. ANALYSETECHNIKEN ZUR AUTOMATISCHEN VERTEILUNG
kürzt als globale Def-Use-Kette. Dieses Verfahren wurde in unserer Analyse-
umgebung als ein Analysemodul von Dr. Michael Thies [Thi01] realisiert.
Ein Beispiel
Zur Verdeutlichung betrachte man zunächst ein einfaches Beispiel, das in Ab-
bildung 6.4 angegeben ist. Es zeigt uns das Ergebnis der GlobalDefUse-Analyse
für eine Definition, repräsentiert an der Erzeugungsstelle des Objekts vom
Typ Binnerhalb der Konstruktor-Methode A() (die Stelle ist gesondert mar-
kiert). Die GlobalDefUse-Analyse findet viele (markierte) Programmstellen, an
denen sich Verwendungen von Referenzen auf Objekte befinden, die an der
Stelle bo = new B(ai) erzeugt werden. Der this-Receiver zu diesem Ob-
jekt (bo_object) wird innerhalb der eigenen Konstruktormethode B(int
i) und in den beiden Methoden getI() und setI() verwendet.
Außerdem referenziert eine Instanzvariable bo in der Klasse Adas Objekt
bo_object. Von dieser Variable aus kann das Objekt über die Methode
getBO() an anderen Stellen verwendet werden: Das Feld bin der Klasse
Cspeichert diese Referenz zu bo_object. Über bwird es dann zu Metho-
denaufrufen in der Methoden cMethode() benutzt.
class A {
private int ai;
private B bo;
A() {
...
bo = new B(ai);
...
}
B getBO() { return bo; }
}
class B {
private int bi;
B(int i) { this.bi = i; }
int getI() { return bi; }
void setI(int i) { bi = i; }
}
class C {
private B b;
private A a;
C() {
a = new A();
b = a.getBO();
...
}
void cMethod() {
...
if (bed)
x = b.getI();
else
b.setI(x);
}
}
Abbildung 6.4.: Beispiel für interprozedurale Definitions-
/Verwendungsketten
138
6.3. GRUNDLEGENDE STATISCHE ANALYSEN
Man kann erkennen, dass das Objekt bo_object nicht nur innerhalb der
Methode verwendet wird, in der es erzeugt wird, sondern über die Metho-
de getBO() entkommt. Dadurch kann es auch in Methoden anderer Klasse
benutzt werden. Die Analyse hat also die Aufgabe, solche interprozeduralen
Verbindungen zwischen den Verwendungsstellen und den Definitionsstellen
aufzubauen. Zur Vereinfachung wird im Folgenden das Verfahren anhand
des angegebenen Beispiels grob erläutert.
Verfahren
Die Kernidee des Verfahrens basiert auf einer Variante des FTA-Verfahrens
[TP00], [Wes04], welches zur Konstruktion des Aufrufgraphen eingesetzt
wird und die Verwendungsbeziehungen der Felder berechnet. Das Global-
DefUse-Verfahren geht noch einen Schritt weiter, so dass es den Informati-
onsfluss zu den Definitionsstellen von diesen Verwendungen, die im ganzen
Programm vorkommen können, weiter progagiert und daraus die Def-Use-
Ketten interprozedural bestimmt.
Die Berechnung der globalen Def-Use-Ketten wird prinzipiell in drei Phasen
aufgeteilt:
1. Phase: Für jede Methode werden die darin enthaltenen Def-Use-Ketten
lokal berechnet. Die Knoten einer lokalen Def-Use-Kette repräsentieren
lokale Zugriffe, so genannte LocalAccesses, und die Kanten die Def- oder
Use-Beziehung. Basiert auf dem Stack-Modell der JVM modelliert ein
LocalAccess einen einfachen Schreib/Lese-Zugriff auf Operanden bzw.
Ergebnis, der mittels einer Bytecode-Instruktion aufgelöst wird. In Ab-
bildung 6.5 wird die lokale Def-Use-Kette innerhalb der Konstruktorme-
thode A() dargestellt: So wird das Objekt bo_object von vier Local-
Accesses l1, l2, l3und bo verwendet. Ebenfalls wird die lokale Def-Use-
Kette der Methode cMethod() in der Abbildung angegeben. Solche lo-
kalen Ketten werden durch die LocalDefUse-Analyse bestimmt.
2. Phase: Die Abhängigkeit der Parameter, Rückgaben und Felder aller
Methoden wird berechnet und in einem Abhängigkeitsgraphen model-
liert. Dies ist ein gerichteter Graph. Die Knoten repräsentieren die Felder,
Parameter und Rückgaben. Die Kanten stellen die Flussinformation von
einem Knoten zu einem anderen dar. Für das obige Beispiel wird dieser
Graph in Abbildung 6.5 dargestellt: Eine Kante von einem Parameter p1
139
KAPITEL 6. ANALYSETECHNIKEN ZUR AUTOMATISCHEN VERTEILUNG
zu einem Feld fmodelliert eine potentielle Zuweisung des Wertes von p1
zu f. Die Propagation der Information, also auch der Aufbau des Abhän-
gigkeitsgraphen der Felder, Parameter und Rückgaben, findet auf fluss-
und kontext-insensitiver Weise statt. Diese Analyse ist eine einfache Va-
riante der Points-to-Analyse [Ryd03], [Ste96], [Le00].
3. Phase: Die Verbindungen zwischen den LocalAccesses der Methoden
und den Knoten des Abhängigkeitsgraphen werden in dieser Phase auf-
gebaut. Lokale Zugriffe auf Parameter, Rückgabewerte oder Felder in-
nerhalb einer Methode werden auf die zugehörigen Knoten des Abhän-
gigkeitsgraphen und umgekehrt abgebildet. Lokale Def-Use-Ketten wer-
den somit zu globalen zusammengesetzt. In Abbildung 6.5 stellen die
Kanten zwischen dem LocalAccess bo bzw. b(als Kreise) und dem Kno-
ten bo bzw. b(als Rechtecke) des Graphen solche Verbindungen dar.
new
l2
bo
l3
l1
bo
b
a
p1
f
r1
bl2
x
getI
setI
LocalAccess
...
...
A() {
}
void cMethod() {
}
Abhängigkeitsgraph für Felder,
Parameter und ckgabe
Def
Use
Use
Use
Use
Abbildung 6.5.: Verfahren zum Aufbau der interprozeduralen
Definitions-/Verwendungsketten
Als Ergebnis stehen alle interprozeduralen Def-Use-Ketten zur Verfügung.
Damit kann man zu einer Erzeugungsstelle, also einer Definition, alle Ver-
wendungsstellen im ganzen Programm bestimmen. Symmetrisch können zu
einer Verwendungsstelle ihre Definition(en) gefunden werden.
140
6.3. GRUNDLEGENDE STATISCHE ANALYSEN
6.3.3. Analysetechniken zur Berechnung der
Programmeigenschaften
Dieser Abschnitt beschreibt die Anwendung der vorgestellten Analysen so-
wie grundlegender Analysetechniken zur Berechnung und Abschätzung eini-
ger modellierter Programmeigenschaften. Durch das Bestimmen des Escape-
Status kann festgestellt werden, ob ein Objekt die Proxy- oder die Thread-
lokale Eigenschaft besitzt. Mittels einiger einfacher Techniken können eben-
falls einige Peer-Eigenschaften berechnet werden. Zur Vorbereitung der Ob-
jektmigration zur Laufzeit muss statisch bestimmt werden, ob Instanzen einer
Klasse migrierbar sind und welche ihrer Methoden Schreib- oder Nur-Lese-
Zugriffe darstellen. Diese Information ist vor der Laufzeit erforderlich zur
Transformation der Klassen, um bei potentiell schreibenden Methoden einen
Mechanismus für Schreibzähler einzubauen. All diese Berechnungen zu stati-
schen Programmeigenschaften können vollständig vor der Laufzeit durchge-
führt werden.
Escape-Eigenschaft
Ein typisches Muster für (gute) OO-Programmierung ist die Kapselung ei-
nes Objekts in ein anderes, so dass sämtliche Operationen auf das innere Ob-
jekt nur durch Aufrufe der Methoden des äußeren Objekts (Proxy-Objekts)
ermöglicht werden. Das bedeutet, dass sowohl die Objekterzeugung als auch
alle Zugriffe auf diese Objekte innerhalb ihrer Proxy-Objekte stattfinden müs-
sen. Als Zugriffe zählen hierbei Methodenaufrufe und direkte Feldzugriffe
über ihre Referenzen. Außerdem dürfen Methoden des inneren Objekts von
diesem selbst aufgerufen werden. Das bedeutet, dass die Referenzen der in-
neren Objekte ihren umgebenden Objekten nicht entkommen.
Im Thread-lokalen Fall wird die Escape-Umgebung der inneren Objekte „et-
was vergrößert“. Dort können Objekte ihren umgebenden Objekten über Pa-
rameter, Rückgabewerte einer Methode oder in einem für andere zugängig
abgespeicherten Feld entkommen, jedoch nicht den sie erzeugenden Threads.
Die Analysetechnik, die diese Bedingungen überprüft, muss dabei auch die
Seiteneffekte der Methodenaufrufe auf interprozeduraler Weise berücksichti-
gen. Außerdem kann zusätzliche Betrachtung von Zugriffen auf als private
deklarierte Felder viele Treffer für Objekte mit solcher Eigenschaft erzielen.
Denn einerseits sind die von privaten Feldern referenzierten Objekte Spitzen-
141
KAPITEL 6. ANALYSETECHNIKEN ZUR AUTOMATISCHEN VERTEILUNG
kandidaten mit dieser Eigenschaft, andererseits muss die Analyse für solche
Objekte nur die Methoden der deklarierenden Klasse untersuchen.
Um den Escape-Status statisch für alle Objektrepräsentanten im Programm
zu berechnen, wurden u.a. die obige interprozedurale GlobalDefUse- und die
Klassenhierarchie-Analyse eingesetzt und für jede Objekterzeugungsstelle
nach den obigen Kriterien überprüft. Weitere Details über diese Eigenschaft
finden sich in der Diplomarbeit von Karsten Klohs [Klo02].
Bestimmung der Peer-Eigenschaft
Zur Peer-Eigenschaft werden zunächst verschiedene Muster spefizifiert, die
diese Eigenschaft aufweisen. Mittels unterschiedlicher Analysen, von einfa-
cher Hierarchie-Information bis zu speziellen, komplexeren Techniken wer-
den die Objektrepräsentanten statisch überprüft, ob solche Muster in ihrem
Benutzungskontext vorkommen. Beispiele dafür sind Muster zur Erkennung
von Benutzerinteraktionen mit dem Programm, z.B. zur Eingabe oder Ausga-
be, Muster zur Ausführung maschinenspezifischen Codes (oder Ausführung
von als native definierter Methoden), Muster zum Netzwerkaufbau, Muster
zur grafischen Darstellung, Zeichnung usw. Im Allgemeinen stellen solche
Muster verschiedenartige Benutzungen der Ressourcen anderer Programme
oder der Hardware dar. Je nach Komplexität und Exaktheit der Berechnung
solcher Muster werden sehr einfache bis aufwändige Analysen verwendet.
Um diesen Ansatz zu evaluieren werden in dieser Arbeit nur einfache Muster
definiert.
Das einfachste Muster ist das zur Ausführung von Maschinencode durch
Aufrufe von Methoden, die als native definiert sind. Die eingesetzte Analyse
hat für jeden Objektrepräsentanten nachzuprüfen, ob bei allen potentiell auf-
zurufenden Methoden mindestens eine native-Methode vorkommt. Durch die
obige PreciseCallGraph-Analyse und Nachfrage nach dem vorhandenen Status
der betrachteten Methode kann dieses Muster einfach bestimmt werden. Es
gibt jedoch eine Ausnahme: Thread-Repräsentanten werden nicht untersucht,
denn die start()-Methode der Thread-Klasse ist eine native-Methode. Aber
die Aktivitäten sind Hauptbestandteile der Verteilung und werden hierbei
behandelt, als ob sie keine Peer-Eigenschaft besitzen würden. Dies ist auch
der Grund, weshalb eine nachträgliche Umverteilung eines schon platzier-
ten Threads mit sehr vielen Aufwand verbunden ist und bei diesem Konzept
keinen Sinn macht. Ein weiteres Muster, welches sich auch einfach erken-
nen läßt, stellt die Erkennung der Benutzerinteraktion mit dem Programm
142
6.3. GRUNDLEGENDE STATISCHE ANALYSEN
dar. Hierbei ist zu untersuchen, ob in den aufzurufenden Methoden eines
Objektrepräsentanten solche Eingabe/Ausgabe-Interaktionen enthalten sind.
Sie sind daran zu erkennen, dass bestimmte Klassen oder/und Methoden
des java.io-Pakets benutzt werden. Neben dem Aufrufgraphen wird auch
die MethodInspector-Analyse [Thi01] verwendet, um u.a. Details über In-
struktionen der Methode zu erfahren.
Um Muster zur Erkennung komplexerer Aufgaben, z.B. zur grafischen Dar-
stellung oder zum Netzwerk-Aufbau zu definieren und zu erkennen, wird
mehr Analyseaufwand benötigt und spezielle Kenntnisse über einige Klassen
des java.awt bzw. java.net Pakets vorausgesetzt. Solchen Muster wur-
den hier nicht evaluiert.
Lese/Schreib-Zugriffsanalyse zur Migration
Ein Kriterium zur Migrationsentscheidung während der Programmausfüh-
rung ist das Zugriffsverhalten auf Objekte, deshalb werden die tatsächlichen
Zugriffe zur Laufzeit gezählt. Es wird außerdem nach Schreib- und Lese-
Zugriffen unterschieden. Vor der Laufzeit muss an geeigneten Stellen in
entsprechenden Methoden Instrumentierungscode für passende Zähler ein-
transformiert werden. Dafür wird statisch bestimmt, welche Methoden den
Schreib- oder Lese-Status besitzen. Mit Hilfe der Information über die loka-
len Def-Use-Ketten kann fluss-insensitiv festgestellt werden, ob eine Methode
potentiell eine Instruktion enthält, die einen Schreibzugriff darstellt. Hierbei
gelten nicht nur PUTFIELD oder PUTSTATIC Instruktionen, die offentsichlich
Schreibzugriffe auf Felder verursachen. Es muss konservativ abgeschätzt wer-
den, dass die this-Referenz auch geschrieben werden kann, die innerhalb ei-
nes kritischen Abschnitts (durch die MONITORENTER Instruktion) vorkommt
oder in einem Array gespeichert wird. Auf diese Weise kann die Transforma-
tion korrekt durchgeführt werden.
Berechnung der Programmeigenschaften zu Workload
Die Berechnung der Ausführungskosten, die zu Anfang dieses Kapitels mo-
delliert wurden, basiert auf dem Objektflussgraphen [Klo02], [KLK02], der
das Ausführungsmodell darstellt. Im Wesentlichen wurde dort so genau wie
möglich die dynamische Methodenbindung, welche als Knoten in diesem
143
KAPITEL 6. ANALYSETECHNIKEN ZUR AUTOMATISCHEN VERTEILUNG
Graphen abgebildet wird, zusammen mit der Menge der Erzeugungsstel-
len als Typbeschränkung anhand Ergebnissen der intraprozeduralen Ana-
lyse modelliert. Hierbei dient das Ergebnis der GlobalDefUse-Analyse (sie-
he Abschnitt 6.3.2) als Grundlage. Basierend auf diesem Ausführungsmo-
dell werden die Ausführungs- sowie Kommunikationskosten der Methoden
unter Verwendung des Kostemmaßes, das im Abschnitt 6.2.1 definiert wur-
de, berechnet. Das angewandte Abschätzungsmodell lehnt sich an das im
JavaParty-System [Hau98] verwendete Berechnungsmodell an.
Erweiterungen zur hybriden Analyse
Zur Steigerung der Genauigkeit kann man weitere Analysen gezielt
einsetzen. Sie können z.B. als Schleifengrenzen verwendete Laufzeit-
konstanten identifizieren oder einige häufig verwendete Strukturen wie
java.util.Iteration oder Arrays speziell behandeln. Für Array
beispielsweise kann durch Feststellung der interen Variable length die
Obergrenze einer Schleife ermittelt oder abgeschätzt werden. Für eine
n-Verzweigungsstruktur können Analysetechniken für einige spezifische
Strukturen in Java-Programmen eingesetzt werden, um genauere Aussagen
über die Ausführungshäufigkeiten der Verzweigungen machen zu können.
Beispielsweise kann für den Zweig, der durch ein throw zum Exception
aufgelöst wird, der Faktor branch(throw)=0angenommen werden. Oder es
kann abgeschätzt werden: Die Wahrscheinlichkeit, dass die Bedingung der
Form (i == j) true ergibt, ist viel geringer als bei einer Bedingung der
Form (a<i&&i<b). Solche speziellen Kenntnisse können direkt be-
nutzt werden.
Bei der statischen Auflösung dynamischer Methodenbindung wird bisher
stets versucht, so genau wie möglich die Aufrufziele an einer dynamisch ge-
bundenen Aufrufstelle durch verschiedene Analysetechniken wie PreciseCall-
Graph-Analyse oder Points-to-Analyse [Ryd03], [Ste96], [WR99], [Le00] zu fin-
den. Wenn kein exaktes Aufrufziel bestimmt werden kann, wird zur Kosten-
berechnung der Mittelwert der Ausführungs- bzw. Kommunikationskosten
der gefundenen Ziele genommen. Ein erweiterter Ansatz zur hybriden Analy-
se für ein solches Problem ist, vor der Laufzeit mit üblichen Techniken soweit
wie möglich die dynamisch gebundenen Aufrufziele einzuschränken. Nur an
denjenigen Stellen, wo mehr als 4 Ziele gefunden werden, wird Instrumen-
tierungscode eingefügt, so dass zur Laufzeit die 4 ersten tatsächlichen Auf-
rufziele gepuffert werden. Aus den vorberechneten Ausführungskosten die-
144
6.4. ANALYSEVERFAHREN ZUR STAGEWISE-IMMUTABLE
EIGENSCHAFT
ser ausgewählten Methoden kann der Mittelwert erneut gebildet werden, der
dann zur verbesserten Verteilungsberechnung verwendet wird. Dieser An-
satz ist im Smalltalk-Umfeld bekannt unter dem Begriff polymorphic in-line ca-
che (PIC) [DS84], [HCU91], [Höl94] wobei sich aus praktischen Untersuchun-
gen der Grenzwert 4 als sinnvoll ergeben hat.
Die Kommunikationskosten entstehen genau dort, wo entfernte Methoden-
aufrufe stattfinden. Statisch wird herausgefunden, welche zur Laufzeit ent-
stehenden Objekte remote-Objekte werden. Somit wird vor der Laufzeit schon
festgelegt, an welchen Aufrufstellen entfernte Kommunikation entsteht. Da-
für werden statisch Kommunikationskosten an solchen Stellen ausgerech-
net. Diese Kosten werden dann einerseits direkt bei der Verteilungsberech-
nung in einigen Basis-ODFs verwendet, können andererseits aber zur Lauf-
zeit als Ausgangsgröße zur nachträglichen Kommunikationsberechnung be-
nutzt werden. Denn die vor der Laufzeit berechneten bzw. abgeschätzten Grö-
ßen der Objektstrukturen bleiben auch für die Berechnungen zur Laufzeit gül-
tig. Nur dynamisch gebundene Aufrufziele lassen sich zur Laufzeit noch ver-
bessern.
6.4. Analyseverfahren zur stagewise-immutable
Eigenschaft
Ein wichtiges Ziel bei der automatischen Verteilung ist es, Objekte so zu plat-
zieren, dass sie nur lokal miteinander kommunizieren. Messungen in [HP01]
zeigten, dass bis zu drei Größenordnungen zwischen einem entfernten und
einem konventionellen Methodenaufruf liegen. Neben den vorgestellten Stra-
tegien zur Zusammengruppierung der Objekte mittels ihrer isProxy- oder
isPeer-Eigenschaft spielt die Immutabilität der Objekte bei der Lokalitäts-
optimierung eine wichtige Rolle. Immutable Objekte können zur Laufzeit re-
pliziert werden. Entfernte Zugriffe darauf finden dann lokal statt.
Unter diesem Aspekt möchte ich in diesem Abschnitt eine neuartige Eigen-
schaft, die stagewise-immutable Eigenschaft, vorstellen. Bezogen auf den Pro-
grammablauf wird dadurch charakterisiert, in welchen bestimmten Phasen wäh-
rend der Programmausführung die Lese- und Schreibzugriffe auf ein Objekt
stattfinden. Objekte mit der stagewise-immutable Eigenschaft werden ab einem
bestimmten Zeitpunkt Tbei der Programmausführung nur lesend zugegrif-
fen. Nach diesem Zeitpunkt können sie dann repliziert werden. Ziel ist es,
145
KAPITEL 6. ANALYSETECHNIKEN ZUR AUTOMATISCHEN VERTEILUNG
zu erzeugende Objekte mit dieser Eigenschaft vor der Laufzeit zu erkennen
und dafür geeignete Programmstellen als Replikationspunkte zu finden. Da-
zu habe ich ein statisches Analyseverfahren namens stagewise-immutable Ana-
lyse entwickelt, das die Objektrepräsentanten (Erzeugungsstellen) nach dieser
Eigenschaft untersucht und Programmstellen als Replikationspunkte identi-
fiziert. In diesem Abschnitt stelle ich die ablaufbezogene stagewise-immutable
Eigenschaft dar und beschreibe anschließend das Analyseverfahren stagewise-
immutable Analyse.
6.4.1. Die stagewise-immutable Eigenschaft und ihre
Anwendung zur Objektreplikation
Konzeptionell gibt es für ein Laufzeitobjekt verschiedene Phasen bei einer Pro-
grammausführung: die Initialisierungsphase und die Verwendungsphase. Bei der
Initialisierungsphase werden die Felder des Objekts mit Anfangswerten in-
itialisiert. Nach dieser Phase ist der Zustand des Objekts komplett initialisiert.
Diese Phase endet, nachdem eine aufgerufene Konstruktor-Methode vollstän-
dig abgearbeitet wurde. Anschließend beginnt die Verwendungsphase, die bis
zum Ende der Programmausführung dauert. Während dieser Phase werden
die Daten des Objekts benutzt, also gelesen oder/und geschrieben.
Definition 6.6:
Ein Laufzeitobjekt ist genau dann pure-immutable, wenn seine Daten in der
gesamten Verwendungsphase nur gelesen werden. 4
Ein solches Objekt kann zum Beginn der Verwendungsphase repliziert wer-
den, da diese pure-immutable Eigenschaft gewährleistet, dass sein Zustand ab
diesem Zeitpunkt bis zum Programmende unverändert bleibt. Es beschränkt
sich also darauf, dass Objekte nach ihrer Initialisierung nur noch lesend zu-
gegriffen werden dürfen.
Man kann die Immutabilität weiter fassen: Es gibt Objekte, die zwar nach der
Initialisierungsphase noch schreibend zugegriffen werden, aber ab einem spä-
teren Zeitpunkt nur noch gelesen werden. Solche Objekte können ab diesem
späteren Zeitpunkt repliziert werden. Diese Eigenschaft wird als postponed-
immutable Eigenschaft bezeichnet. Sie kann wie folgt definiert werden:
Definition 6.7:
Ein Laufzeitobjekt ist genau dann postponed-immutable, wenn seine Daten in der
146
6.4. ANALYSEVERFAHREN ZUR STAGEWISE-IMMUTABLE
EIGENSCHAFT
Verwendungsphase noch geschrieben, aber ab einem bestimmten Zeitpunkt Tbis
zum Programmende dann nur lesend zugegriffen werden.
Ein Laufzeitobjekt ist genau dann stagewise-immutable, wenn es die Eigenschaft
pure-immutable oder postponed-immutable erfüllt. 4
T
JVM 1
JVM 2
JVM 3
Zeit
Erzeugung und
Initialisierung Schreibzugriffe Lesezugriffe
T
JVM 1
JVM 2
JVM 3
Zeit
Abbildung 6.6.: Anwendung der pure-immutable und postponed-immutable
Eigenschaft zur Replikation
Stagewise-immutable ist also der Oberbegriff für die beiden speziellen Fälle.
Der Zeitpunkt Tkennzeichnet im Prinzip das Ende aller Schreibzugriffe auf
ein Laufzeitobjekt während des Programmablaufs.
Um die stagewise-immutable Eigenschaft eines Objektrepräsentanten zu be-
rechnen, braucht man nur die Zugriffe nach der Initialisierungsphase zu be-
trachten. Denn Objekte können während ihrer Initialisierung nicht repliziert
werden. Ein stagewise-immutable Objekt kann nach einem geeigneten Zeit-
punkt T, also unmittelbar nach der Initialisierungsphase oder irgendwann
in der Verwendungsphase, repliziert werden. Danach stattfindende Lesezu-
147
KAPITEL 6. ANALYSETECHNIKEN ZUR AUTOMATISCHEN VERTEILUNG
griffe werden dann nur lokal durchgeführt. Abbildung 6.6 verdeutlicht diese
beiden Fälle.
Die obigen Definitionen beziehen sich auf Objekte, die erst zur Laufzeit exis-
tieren. Ziel ist es aber, die stagewise-immutable Eigenschaft der Objekte vor
der Laufzeit durch statische Analyse nachzuweisen und dann Programmstel-
len als Replikationspunkte ebenfalls vor der Laufzeit zu erkennen. Die sta-
tische stagewise-immutable Analyse muss auf Objektrepräsentanten, also Er-
zeugungsstellen, und allen Stellen arbeiten, an denen sie potenziell verwen-
det werden. Wenn ein Objektrepräsentant diese Eigenschaft erfüllt, dann gilt
dies für alle Laufzeitobjekte, die an dieser Stelle während der Programmaus-
führung erzeugt werden. Wegen konservativer statischer Analyse muss ein
Objektrepräsentant die stagewise-immutable Eigenschaft nicht erfüllen, obwohl
die dort zur Laufzeit erzeugten Objekte unter bestimmten Laufzeitbedingun-
gen diese Eigenschaft besitzen können. Die im Folgenden eingeführten Be-
griffe und Aspekte beziehen sich auf die statische Ablaufstruktur des Pro-
gramms, auch als interprozeduralen Ablaufgraphen bekannt. Dieser Graph ent-
hält alle potenziellen Programmabläufe.
Definition 6.8:
Zu einem Objektrepräsentanten sei der interprozedurale Ablaufgraph in drei dis-
junkte, aufeinander folgende Phasen eingeteilt: Initialisierungsphase,Schreib-
phase und NurLesePhase. Es sei dann Verwendungsphase := (Schreibpha-
se,NurLesePhase). Alle Kontrollfluss-Knoten (CF-Knoten) in der Verwendungs-
phase, die potenzielle Zugriffe auf diesen Objektrepräsentanten darstellen, gehö-
ren entweder zur Schreibphase oder NurLesePhase. Die Schreibphase kann CF-
Knoten enthalten, die Lese- oder Schreibzugriffe darstellen. In der NurLesePhase
dürfen nur CF-Knoten enthalten sein, die Lesezugriffe repräsentieren. Außerdem
muss die NurLesePhase mindestens einen CF-Knoten enthalten, der das Programm-
ende (exit) darstellt.
4
Diese Unterteilung innerhalb der Verwendungsphase stellt somit einen
Schnitt im Ablaufgraphen dar. Nach diesem Schnitt, also nach der Schreib-
phase, dürfen keine weiteren Schreibzugriffe mehr erfolgen. Dies gilt für al-
le Abläufe des Programms. Zu einem Objektrepräsentanten kann seine Nur-
LesePhase nur einen einzigen Knoten enthalten. Abbildung 6.7 stellt die Pha-
senaufteilung zum Beispielprogramm aus Abbildung 6.8 grafisch dar.
148
6.4. ANALYSEVERFAHREN ZUR STAGEWISE-IMMUTABLE
EIGENSCHAFT
TZeit
Initialisierungsphase
Schreibphase NurLesePhase
Verwendungsphase
entry
entryentry
exit
exit
p()
new A()
if
a
b()
r()
s()
q()
setI()
a()
r()
q()
r
p
q
A
endif
p()
b
main
e1
init
w1
w1
w2 r1 r2
exit
s
Aufrufkante CF-Kante
Erzeugung und
Initialisierung
Schreibzugriffe Lesezugriffe
Abbildung 6.7.: Aufteilung des interprozeduralen Ablaufgraphen in drei
Phasen
Die Abbildung 6.7 zeigt einen interprozeduralen Ablaufgraphen. Hier finden
sich die lokalen Kontrollflussgraphen (in einem Rechteck eingegrenzt) ein-
zelner Methoden sowie ihre Aufrufbeziehungen (gestrichelte Linien). Dies
ist auch das Ergebnis der angewandten statischen Analysen. Das bedeutet,
dass diese statische Ablaufstruktur alle möglichen Ausführungspfade des
Programms enthält, von denen nur einige zur Laufzeit tatsächlich ausgeführt
werden. Die stagewise-immutable Analyse muss alle diese Pfade vor der Lauf-
zeit betrachten.
Bei diesem Szenario (in Abbildung 6.7) besitzt der Objektrepräsentant e1 die
postponed-immutable Eigenschaft. Das heißt, dass alle dort zur Laufzeit entste-
149
KAPITEL 6. ANALYSETECHNIKEN ZUR AUTOMATISCHEN VERTEILUNG
public class Test {
static A ao;
... main(String...) {
ao = new A(20);
if (...) { a(); }
else b();
}
static void b() {
ao.setI(100);
r(); p(); q();
}
static void a() {
r(); s(); q(); p();
}
static void r() {ao.setI(42);}
static void q() {ao.getI();}
static void p() {ao.getI();}
static void s() { do_thing(); }
}
class A {
int i = 10;
A(int j) {i = j;}
int getI() {return i;}
void setI(int j) {i = j;}
}
Abbildung 6.8.: Beispielprogramm zur stagewise-immutable Eigenschaft
henden Objekte auch diese Eigenschaft erfüllen. Nach seiner Initialisierung
kann e1 innerhalb der Methode a() oder b() geschrieben werden, nämlich
durch die aufgerufenen Methoden r() und/oder setI(). Danach werden
nur Lesezugriffe mittels der Methoden q() und p() darauf durchgeführt.
Der Schnitt (dicke Linie in Abbildung 6.7) trennt die Verwendungsphase von
e1 in zwei disjunkte Phasen: Die Schreibphase enthält die CF-Knoten, die die
Aufrufstellen zu r() und setI() repräsentieren und die NurLesePhase ent-
hält die Lesezugriffe auf e1. Mit diesem Ergebnis können die an e1 erzeugten
Objekte an geeigneten Programmstellen innerhalb der NurLesePhase wäh-
rend der Ausführung repliziert werden. Im besten Fall soll die Replikation
nach den letzten Schreibzugriffen erfolgen. Dann erzielt man maximalen Ge-
winn an lokalen Zugriffen auf e1.
150
6.4. ANALYSEVERFAHREN ZUR STAGEWISE-IMMUTABLE
EIGENSCHAFT
Um die Verwendungsphase im Allgemeinen zu trennen, braucht man einen
legalen Schnitt des interprozeduralen Ablaufgraphen. Dieser wird wie folgt
definiert:
Definition 6.9:
Ein Programmschnitt, der alle gültigen Ausführungspfade in der Verwendungs-
phase eines Objektrepräsentanten schneidet, heißt ein legaler Schnitt, wenn auf
solchen Pfaden nach dem Schnittpunkt kein Schreibzugriff stattfindet. 4
Ein trivialer, legaler Schnitt ist die Trennung des exit-Knotens von den rest-
lichen des interprozeduralen Ablaufgraphen. Nach diesem Schnitt wird auf
jedem Fall der exit-Knoten ausgeführt, der weder Schreib- noch Lesezu-
griffe enthält. Im pure-immutable Fall wäre ein legaler Schnitt genau dort, wo
die Initialisierungs- und Verwendungsphase getrennt werden. In diesem Fall
ist der Replikationspunkt genau die Programmstelle unmittelbar nach der
Aufrufstelle der verwendeten Konstruktor-Methode. In Abbildung 6.7 ist ein
solcher legaler Schnitt (dicke Linie) angegeben. Dieser ist sogar ein „guter“
Schnitt, denn er liegt unmittelbar nach den „letzten“ Schreibzugriffen.
Intuitiv braucht man für einen Objektrepräsentanten den Schnitt „einfach“
vom exit-Knoten des Programms sukzessiv „nach links“ zu schieben, bis
ein Schreibzugriff „vom Schnitt getroffen wird“, dann hört man auf. Dabei
muss beachtet werden, dass bei der Ausführung der CF-Knoten nach dem
Schnitt keine Schreibzugriffe mehr stattfinden. Ein legaler Schnitt unterteilt
im Prinzip die Verwendungsphase in die oben genannte Schreib- und Nur-
LesePhase.
Auch wenn es einfach erscheinen mag, ist es eine große Herausforderung,
diese Eigenschaft für Objektrepräsentanten (Erzeugungsstellen) vor der Lauf-
zeit nachzuprüfen. Außerdem müssen ebenfalls vor der Laufzeit die gültigen
Programmstellen zur Anwendung der Replikation gefunden werden, so dass
zur Laufzeit nach der Replikation nur Lesezugriffe durchgeführt werden, und
zwar für jede Programmausführung.
6.4.2. Analysetechniken zur Berechnung der
stagewise-immutable Eigenschaft
Die Aufgabe der stagewise-immutable Analyse ist es, für jeden Objektrepräsen-
tanten (Erzeugungsstelle) einen legalen, „guten“ Schnitt zu finden, so dass
151
KAPITEL 6. ANALYSETECHNIKEN ZUR AUTOMATISCHEN VERTEILUNG
nach diesem Schnitt Lesezugriffe vorkommen. Dann müssen geeignete Pro-
grammstellen nach dem Schnitt als Replikationspunkte identifiziert werden,
so dass nach der Replikation der Objekte zur Laufzeit so viel Lesezugriffe wie
möglich durchgeführt werden. Replikation wird für Objekte nicht durchge-
führt, wenn kein einziger Lesezugriff nach einem gefundenen Schnitt statt-
findet.
Kriterien für einen guten Schnitt Ein legaler Schnitt ist ein guter Schnitt,
wenn er alle CF-Kanten mit folgenden Eigenschaften schneidet: Diese CF-Kanten
werden zum ersten Mal ausgeführt, nachdem die Schreibzugriffe zum letzten
Mal ausgeführt wurden.
Ein guter Schnitt liegt also unmittelbar nach den CF-Knoten, die die als Letzte
ausgeführten Schreibzugriffe darstellen. Die NurLesePhase enthält somit alle
potenziellen Lesezugriffe, die nach dem letzten Schreiben statttfinden kön-
nen.
Bevor die stagewise-immutable Eigenschaft eines Objektrepräsentanten nach-
geprüft wird, müssen grundlegende Informationen zum Programm vorlie-
gen. Das sind u.a. die Kontrollfluss-Struktur sowie Aufrufbeziehungen der
Methoden des vollständigen Programms. Um diese ablaufbasierte Eigen-
schaft vor der Laufzeit zu berechnen, braucht man eine statische Programm-
analyse, die die Ausführungsreihenfolge der CF-Knoten ermittelt. Diese Rei-
henfolge wird durch die interprozeduralen Post-Dominator-Relationen zwi-
schen einzelnen CF-Knoten des gesamten Programms modelliert. Mit die-
ser Information kann statisch bewiesen werden, dass beispielsweise ein CF-
Knoten cf1zur Laufzeit ausgeführt wird, falls cf2ausgeführt worden ist. Für
die Berechnung eines guten Schnittes kann mit Hilfe solcher Relation vor der
Laufzeit gezeigt werden, dass die erste Ausführung von einem CF-Knoten
immer nach der letzten Ausführung von einem anderen CF-Knoten erfolgt.
Außerdem müssen Schreib- und Lese-Zugriffe zu einer Erzeugungsstelle sta-
tisch erkannt werden, die in Abbildung 6.7 als Kreise symbolisiert wurden
(markierte Kreise für Schreib-Zugriffe). Diese Information wird von der oben
vorgestellten GlobalDefUse-Analyse beschafft. Die Verfügbarkeit derartiger In-
formation ist eine notwendige Voraussetzung zur stagewise-immutable Analy-
se.
Im Folgenden werde ich kurz auf die interprozedurale Post-Dominator-
Relation eingehen, wobei einige für die stagewise-immutable Analyse relevan-
te Details ausführlich erläutert werden. Das Verfahren selbst wird sehr aus-
152
6.4. ANALYSEVERFAHREN ZUR STAGEWISE-IMMUTABLE
EIGENSCHAFT
führlich in [P. 01] beschrieben. Anschließend wird die Analysetechnik zur
stagewise-immutable Eigenschaft sowie das Finden der Replikationspunkte
dargestellt.
Interprozedurale Post-Dominator-Relationen
Die Relationen zwischen den Knoten innerhalb eines Kontrollflussgraphen
(CFG) einer Methode wie die dom-, idom- oder pdom-Beziehung können mit-
tels klassischer Analyseverfahren [Muc97] bestimmt werden. Seien cf1und
cf2zwei Kontrollflussknoten innerhalb einer Methode. cf1post-dominiert cf2
im klassischen Sinn, wenn cf1auf jedem Ausführungspfad von cf2zum exit-
Knoten des zugehörigen CFG liegt. Nach [P. 01] wird hier die Definition für
die interprozedurale Post-Dominator-Relation zwischen zwei CF-Knoten an-
gegeben:
Definition 6.10:
Seien cf1und cf2zwei CF-Knoten, die in verschiedenen CFGs, also in verschie-
denen Methoden liegen können. cf1post-dominiert den Knoten cf2interprozedural,
wenn cf1auf jedem Ausführungspfad von cf2bis zum Programmende programExit
liegt. Diese Relation sei mit cf1IP OST DOMMust cf2bezeichnet und als Must-
IPOSTDOM-Relation definiert.
Ein CF-Knoten cf1may-post-dominiert einen anderen Knoten cf2interprozedural,
wenn cf1auf mindestens einem Ausführungspfad von cf2bis zum Programmende
programExit liegt. Diese Relation sei als cf1IPOSTDOMMay cf2gekennzeichnet
und als May-IPOSTDOM-Relation definiert.
Die Must-IPOSTDOM-Relation und May-IPOSTDOM-Relation stellen
partielle Ordnungen dar. 4
Das Verfahren in [P. 01] nimmt als Eingabe den gesamten Analysekontext und
berechnet solche interprozeduralen (Must-)Post-Dominator-Beziehungen.
Durch Änderung des Meet-Operators (Θ) in der Phase 2 des Verfahrens
kann anstatt das All-Problem das Existenz-Problem gelöst werden. Das heißt,
dass die May-IPOSTDOM-Relationen mit Θ = und die Must-IPOSTDOM-
Relationen mit Θ = berechnet werden können. Die Ergebnisse zur May- und
Must-IPOSTDOM-Relationen werden als DAGs dargestellt. Mit diesen Infor-
mationen ist man nun in der Lage, die stagewise-immutable Analyse durchzu-
führen.
153
KAPITEL 6. ANALYSETECHNIKEN ZUR AUTOMATISCHEN VERTEILUNG
Die stagewise-immutable Analyse
Basierend auf der interprozeduralen Post-Dominator-Relation kann die Ana-
lyse für eine Erzeugungsstelle die Menge der CF-Knoten im Programm be-
stimmen, die zur NurLesePhase gehören und nach deren Ausführung keine
Schreibzugriffe stattfinden. Die Trennung zwischen den CF-Knoten der Nur-
LesePhase und den restlichen Knoten wird durch einen legalen Schnitt defi-
niert. Die notwendige Bedingung für einen solchen CF-Knoten der NurLese-
Phase lautet, dass nach seiner Ausführung bis zum Programmende kein einzi-
ger Schreibzugriff mehr durchgeführt wird. Das heißt, dass keiner der Schreib-
zugriffe diesen CF-Knoten may-post-dominiert. Die Kriterien für einen gu-
ten legalen Schnitt können mit Hilfe der interprozeduralen Post-Dominator-
Relation so umformuliert werden:
Ein legaler Schnitt ist ein guter Schnitt, wenn er alle CF-Kanten schneidet, die zu
CF-Knoten mit folgenden Eigenschaften führen: Diese CF-Knoten müssen die vor
ihnen auszuführenden Schreibzugriffe must-post-dominieren, und von keinem
der Schreibzugriffe may-post-dominiert werden.
Mit dieser Bedingung befinden sich also alle CF-Knoten direkt hinter dem gu-
ten Schnitt, die zum ersten Mal ausgeführt werden, nachdem die Schreibzu-
griffe zum letzten Mal ausgeführt wurden. Durch die Verwendung der Must-
IPOSTDOM-Relation wird zum einen die Korrektheit des guten Schnitt auf
jedem Fall garantiert. Beim sukzessiven „nach links“ Schieben des Schnit-
tes werden nur die CF-Knoten nach der Must-IPOSTDOM-Relation betrach-
tet. Dadurch können irrelevante CF-Kanten (z.B. Schleifenkanten) übersprun-
gen werden. Außerdem verursacht die Berechnung für die Must-IPOSTDOM-
Beziehung keinen zusätzlichen Aufwand.
Unter Berücksichtigung dieser Kriterien kann man nun einen legalen guten
Schnitt finden. Wenn zu einem Objektrepräsentanten ein solcher Schnitt ge-
funden wird, nach dem auch potentielle Lesezugriffe existieren, dann besit-
zen alle dort zur Laufzeit entstehenden Objekte die stagewise-immutable Ei-
genschaft. Dafür kann dann nach geeigneten Programmstellen als Replikations-
punkte gesucht werden.
Beispiel Zur Verdeutlichung der Verwendung der Post-Dominator-
Replation zur Ermittlung der stagewise-immutable Eigenschaft sowie der
Replikationsstellen wird zunächst das Ergebnis der stagewise-immutable
Analyse zum obigen Programmbeispiel in Abbildung 6.9 gezeigt.
154
6.4. ANALYSEVERFAHREN ZUR STAGEWISE-IMMUTABLE
EIGENSCHAFT
TZeit
Initialisierungsphase
Schreibphase NurLesePhase
Verwendungsphase
entry
entryentry
exit
exit
p()
new A()
if
a
b()
r()
q()
setI()
a()
r()
q()
r
p
q
A
endif
b
main
e1
init
w1
w1
w2 r1 r2
exit
s
Aufrufkante CF-Kante Must-Postdom-KanteCF-Kante
p()
s()
Replikationspunkt
...()
Erzeugung und
Initialisierung
Schreibzugriffe Lesezugriffe
Abbildung 6.9.: Gefundene Replikationspunkte mit Must-Post-
Dominator-Bedingung
In Abbildung 6.9 wird der Schnitt unmittelbar nach den als letzten aus-
geführten Schreibzugriff-Knoten definiert. Wenn die CF-Knoten hinter dem
Schnitt bis zum Programmende ausgeführt werden, findet auch kein einzi-
ger Schreibzugriff mehr statt. Außerdem werden am Beispiel auch zwei Stel-
len als Replikationspunkte (in Weiß hervorgehoben) gefunden, die die Auf-
rufstelle der Methoden s() und p() darstellen. Das sind günstige Stellen,
weil sie zum einen alle potenziellen Schreibzugriffe must-post-dominieren.
Zum anderen befinden sie sich direkt hinter dem Schnitt. Das zweite Kriteri-
um garantiert, dass zur Laufzeit das Objekt nur nach dem letzten Schreibzu-
griff repliziert wird und somit sein Zustand konsistent bleibt. Mit dem ersten
155
KAPITEL 6. ANALYSETECHNIKEN ZUR AUTOMATISCHEN VERTEILUNG
Kriterium können alle nach dem Schnitt stattfindenden Lesezugriffe auf dem
Replikat durchgeführt werden.
Nun kann das Verfahren zum Finden eines guten legalen Schnitts und zum
Identifizieren günstiger Replikationsstellen allgemein beschrieben werden.
Finden eines guten, legalen Schnitts Das Finden eines legalen guten
Schnitts für einen Objektrepräsentanten läuft folgendermaßen ab: exit - al-
so der CF-Knoten für das Programmende - wird in die Ergebnismenge ein-
gefügt. Ausgehend vom exit betrachtet die Analyse die Nachfolger-Knoten
des DAGs, der die interprozedurale Must-Post-Dominator-Relation repräsentiert.
Zu beachten ist, dass die Kanten dieses Graphen die Gegenrichtung des in-
terprozeduralen Ablaufgraphen zeigen. Dabei wird überprüft, ob diese CF-
Knoten von irgendeinem Schreibzugriff may-post-dominiert werden. Wenn das
nicht der Fall ist, dann werden sie in die Ergebnismenge aufgenommen. Die-
se Menge enthält also nur die CF-Knoten, die zur NurLesePhase gehören
und hinter dem Schnitt liegen. Sukzessiv für die in die Ergebnismenge auf-
genommenen Knoten werden ihre Nachfolger im obigen DAG so weiter ana-
lysiert. Der Schnitt wird somit immer weiter nach links verschoben. Wenn ein
betrachteter Knoten einen Schreibzugriff darstellt, dann wird dieser Knoten
nicht in die Ergebnismenge aufgenommen. Das Verfahren terminiert, wenn
der Schnitt unmittelbar vor allen als letzten identifizierten Schreibzugriffen
liegt, oder wenn die Initialisierungsphase erreicht wird.
Günstige Replikationspunkte Das beschriebene Verfahren findet dabei
auch günstige Replikationspunkte. Das sind Programmstellen, die zur Lauf-
zeit nach den letzten Schreibzugriffen ausgeführt werden und danach mög-
lichst noch viele Lesezugriffe stattfinden. Diese Stellen sollen also direkt hin-
ter dem Schnitt liegen. Außerdem muss die Menge der Replikationspunkte
die Menge der Schreibzugriffe must-post-dominieren. Diese Mengenrelation
bedeutet, dass alle gefundenen Replikationsstellen zusammen alle Schreib-
zugriffe must-post-dominieren, also nach diesen Schreibzugriffen ausgeführt
werden müssen.
Verfahren Zur Vereinfachung wird das beschriebene Verfahren zur Berech-
nung der stagewise-immutable Eigenschaft und zum Finden der Repliaktions-
punkte für eine Erzeugungsstelle eim Folgenden in einer Pseudo-Form be-
schrieben. Seien IMMIP OST DOMMust(b)die Menge der CF-Knoten, die von b
156
6.4. ANALYSEVERFAHREN ZUR STAGEWISE-IMMUTABLE
EIGENSCHAFT
direkt interprozedural must-post-dominiert werden. Das sind also die direkten
Nachfolger-Knoten im Graphen (DAG), der die interprozeduralen Must-Post-
Dominator-Beziehungen darstellt. exit sei der CF-Knoten, der das Programm-
ende repräsentiert.
157
KAPITEL 6. ANALYSETECHNIKEN ZUR AUTOMATISCHEN VERTEILUNG
stagewise-immutable Verfahren
EINGABE: Erzeugungsstelle e
/* Initialisierung */
resultSet = empty;
worklist = empty;
replicationSet = empty;
/* exit gehört zum Ergebnis, und von exit wird gestartet */
resultSet.insert(exit);
worklist.insert(exit);
WHILE (worklist not empty) {
b = worklist.removeFirst();
M = IMMIPOSTDOM_Must(b);
FORALL m in M DO {
/* b interprozedural must-post-dominiert m */
W = WRITESET(e);
IF (m is not in W) THEN {
/* m stellt keinen Schreibzugriff dar */
/* wenn keiner der Schreibzugriffe m may-postdominert,
füge m in resultSet */
boolean flag = true;
FORALL w in W DO {
IF (w inter may-post-dom m) THEN {
flag = false;
break;
}
}
IF (flag) THEN {
resultSet.insert(m);
/* Nachfolger von m werden weiter betrachtet,
um den Schnitt weiter nach links zu schieben */
worklist.insert(m);
}
}
ELSE {
/* m stellt einen Schreibzugriff dar */
replicationSet.insert(b);
}
}
}
158
6.4. ANALYSEVERFAHREN ZUR STAGEWISE-IMMUTABLE
EIGENSCHAFT
Die günstigen Replikationspunkte werden dabei so gewählt, dass das Objekt
nach der letzten Ausführung des letzten Schreibzugriffs repliziert wird. Beim
Einfügen des Replikationscodes während der Transformationsphase müssen
zusätzlich noch einige Schritte durchgeführt werden. So soll der Replikati-
onscode nicht für ein Objekt mehrfach ausgeführt werden, z.B. innerhalb ei-
ner Schleife oder an zwei Programmstellen, die auf jedem Fall hintereinander
ausgeführt werden.
Das vorgestellte Verfahren kann einfach für alle Erzeugungsstellen erweitert
werden.
TZeit
Initialisierungsphase
Schreibphase NurLesePhase
Verwendungsphase
entry
entryentry
exit
exit
p()
new A()
if
a
b()
r()
s()
setI()
a()
r()
q()
r
p
q
A
endif
b
main
e1
init
w1
w1
w2 r1 r2
p()
q()
exit
s
Aufrufkante CF-Kante Must-Postdom-KanteCF-Kante May-Postdom-Kante
Replikationspunkt
...()
Erzeugung und
Initialisierung Schreibzugriffe Lesezugriffe
Abbildung 6.10.: MAY- und MUST-POSTDOM
Abbildung 6.10 zeigt ein Beispiel, das die Korrektheit des Verfahrens unter
der Verwendung der may- und must-post-dominator-Relation zeigt. Die Auf-
159
KAPITEL 6. ANALYSETECHNIKEN ZUR AUTOMATISCHEN VERTEILUNG
rufstelle zur Methode s() in der Methode a() kann hier nicht mehr als Repli-
kationspunkt definiert werden. Denn die Aufrufstellen zur Methode r() und
s() befinden sich in einer Schleife und der r()-Knoten, der einen Schreib-
zugriff darstellt, den s()-Knoten may-post-dominiert. Das heißt, dass nach
der Ausführung von s() die Ausführung von r() stattfinden kann. Dage-
gen erfüllt die Aufrufstelle zu q() die Bedingung, dass diese nach der letzten
Ausführung von r() ausgeführt wird. Dort kann das Objekt e1 repliziert
werden.
6.4.3. Analyse zur pure-immutable Eigenschaft
Das oben vorgestellte Analyseverfahren kann im Allgemeinen auch dazu ver-
wendet werden, um Objektrepräsentanten mit der pure-immutable Eigenschaft
zu erkennen. Der Schnitt würde dann die Trennung der Initialisierungs-
und der Verwendungsphase definieren. Aufgrund der konservativen Ab-
schätzung über dynamische Methodenbindung kann die obige stagewise-
immutable Analyse allerdings nicht alle Erzeugungsstellen erkennen, die die
pure-immutable Eigenschaft besitzen. Deshalb wurde dieses statische Analyse-
verfahren speziell entwickelt, um Objektrepräsentanten anhand ihrer Erzeu-
gungsstellen und zugehöriger Zugriffe nach der pure-immutable-Eigenschaft
zu finden. Das Verfahren startet „vom anderen Ende“, nämlich an der Erzeu-
gungsstelle eines Objekts.
Mittels der interprozeduralen Def-Use-Analyse (siehe Abschnitt 6.3.2) wer-
den zu jeder Erzeugungsstelle (Def), die als Definition betrachtet wird, al-
le zugehörigen Verwendungen (Uses) im ganzen Analysekontext gefunden.
Diese Verwendungen werden einzeln unter die Lupe genommen und da-
nach untersucht, ob es sich hierbei um einen Schreib- oder Lesezugriff han-
delt. Die Schreibzugriffe werden außerdem genauer analysiert, ob sie zur In-
itialisierung in Konstruktoren verwendet werden oder nicht. Wenn alle Ver-
wendungen, außer denjenigen zur Initialisierung, nur Lesezugriffe darstellen,
dann besitzen die an dieser Erzeugungsstelle entstehenden Objekte die pure-
immutable-Eigenschaft.
Um festzustellen, ob ein Use zur Initialisierung im Konstruktor verwen-
det wird, muss das Vorwärts-Datenflussproblem innerhalb der Konstruktor-
Methode interprozedural gelöst werden. Der Grund dafür ist , dass aus
dem Konstruktor auch Methoden mit Zugriffen auf das erzeugte Objekt auf-
gerufen werden können. Diese statische Analysetechnik ist eine einfachere
160
6.5. VERWANDTE ARBEITEN
Variante der immutable-fields Analysis in [Thi01], wobei aufgerufene Super-
Konstruktor(en) und Methoden innerhalb des betrachteten Konstruktors mit
ihren Seiteneffekten berücksichtigt werden, insbesondere der Escape-Status
der this-Referenz.
Diese Analyse wird zusätzlich angewandt, um die pure-immutable Eigenschaft
für Objektrepräsentanten zu berechnen, die Teil der stagewise-immutable Ei-
genschaft ist und mittels der oben beschriebenen stagewise-immutable Ana-
lyse aufgrund der konservativen Abschätzung nicht entdeckt werden kann.
Im Abschnitt 8.2 in Kapitel 8 wird eine empirische Untersuchung nach
der stagewise-immutable Eigenschaft einiger ausgewählter Anwendungen und
Benchmarks beschrieben.
6.5. Verwandte Arbeiten
In diesem Abschnitt werden speziell die verwandten Arbeiten der Analy-
setechniken diskutiert, die zur Berechnung der Objektverteilung eingesetzt
werden, insbesondere zur stagewise-immutable Analyse. Hierbei werden die
verschiedenen Konzepte zur Objektreplikation in anderen Systemen beschrie-
ben.
Diskussionen und konzeptionelle Vergleiche zwischen den verschie-
denen statischen Analyseverfahren wie z.B. Points-to-Analyse, Call-Graph-
Konstruktion, Escape-Analyse usw., die in dieser Arbeit als Standardtechni-
ken betrachtet werden, wurden schon in sehr vielen Veröffentlichungen sowie
im Kapitel „verwandte Arbeiten bzw. related works angestellt. Einige sind
[Thi01], [Klo02], [KLK02], [P. 01], [Le00]. Deshalb werde ich auf diese nicht
nochmal eingehen. Hier werde ich nur die besonderen Aspekte der verwen-
deten bzw. entworfenen Verfahren zur automatischen Verteilung beschreiben.
6.5.1. Immutable-Analysen
Statische Analysen
Es gibt ein ablaufbasiertes, statisches Analyseverfahren, das die Immutabilität
der Objekte, aber nicht die postponed-immutable Eigenschaft untersucht. Das
ist die die immutable-fields Analysis in [Thi01]. Sonst sind mir Verfahren zur
stagewise-immutable Analyse nicht bekannt. Bei der genannten immutable-fields
161
KAPITEL 6. ANALYSETECHNIKEN ZUR AUTOMATISCHEN VERTEILUNG
Analysis werden die Felder einer Klasse bezüglich Immutabilität untersucht.
Wenn alle Felder einer Klasse immutable sind, dann besitzen alle Objekte die-
ser Klasse die immutable Eigenschaft. Meine Analyse zur pure-immutable Ei-
genschaft basiert auf einer einfachen Variante dieser immutable-field Analyse,
die zur speziellen Behandlung von Konstruktoren eingesetzt wurde.
Ein zu meiner stagewise-immutable Analyse vergleichbarer Ansatz ist die trust
and dependence analysis [P. 97]. Zur dependence analysis muss das Programm zu-
nächst in die dort entworfene Abhängigkeitsalgebra transformiert werden.
Mit dieser Algebra können die Datenflüsse in einem Programm als Kanäle
mit festgelegten Eigenschaften ausgedrückt werden. Die Daten dürfen über
diese Kanäle nur geschickt werden, wenn diese Eigenschaften erfüllt sind.
Wenn die aufgestellten Eigenschaften verletzt werden, werden die Program-
mabschnitte dort als kritisch markiert. Die trust analysis wird eingesetzt, um
sog. gefährliche Operationen datenflussbasiert aufzusprüren und zu validie-
ren. Gefährliche Operationen, die die Bedingungen verletzen, können dieje-
nigen sein, die beispielsweise den Wert einer Variable ändern, welcher aber
nicht geändert werden darf.
Konzeptionell basiert die trust and dependence analysis auch auf dem Ablauf
und den Datenfluss-Informationen des Programms. Die stagewise-immutable
sowie pure-immutable Analyse entdecken in diesem Sinne auch verschiede-
ne Phasen im Programmablauf, in denen bestimmte Operationen ausgeführt
werden, nämlich Schreib- oder Lesezugriffe.
Objektreplikation
Der Ansatz, Objekte in einer verteilten Laufzeitumgebung auf mehreren
Rechnern zu replizieren, ist nicht neu. Damit werden sonst entfernte Zugriffe
zu lokalen und Kommunikationskosten eingespart.
So können im Manta-System [vNMB+99], [MKB00], [MKB01] Objekt-Cluster
(auch als Wolken bezeichnet), die nur Verweise auf Objekte desselben Clus-
ters enthält, effizient repliziert werden. Zugriffe auf die Objekte einer Wol-
ke finden mittels eines ausgewählten Objekts, des Root-Objekts, statt. Die re-
plizierten Objekte brauchen nicht konstante Objekte zu sein. Durch statische
Programmanalyse werden vor der Laufzeit Methoden identifiziert, die nur
Lesezugriffe darstellen. Diese Methoden werden lokal innerhalb der Objekte
eines Clusters ausgeführt. Schreibende Methoden werden durch die sog. func-
tion shipping Technik auf allen Replikaten durchgeführt. Um die Aktualisie-
162
6.5. VERWANDTE ARBEITEN
rung aller Replikate konsistent zu halten, werden die Schreibzugriffe in einer
totalen Ordnung mit Hilfe der totally-ordered group communication [BBH+98]
auf allen Maschinen ausgeführt. Bei diesem Ansatz muss eine Wolke, also
ausgewählte Objekte mit einem einzigen Zugang als Root, zur Replikation
durch den Programmierer festgelegt werden. In meinem Konzept werden
Objektrepräsentanten mit der stagewise-immutable Eigenschaft automatisch er-
kannt und die Objekte zur Laufzeit zum geeigneten Zeitpunkt automatisch
repliziert.
Javanaise [HL98] verwendet eine ähnliche Technik, hier muss der Anwen-
der aber selber die zusammenhängenden Objektgruppen identifizieren. Das
Mocha-System [TAS98] unterstützt auch Objektreplikation, hier wird aber auf
die strikte Konsistenz der Replizierten verzichtet.
Im JavaParty-System wird ebenfalls Replikation unterstützt. Hierbei werden
statische Konstanten [PZ97], [Bor02] und konstante Arrays repliziert, welche
im Quellcode als static final bzw. mit einem eingeführten Modifizierer
const deklariert werden müssen. Mittels meiner Analysen können nicht nur
solche speziellen Objekte (als static final deklariert), sondern auch an-
dere nicht explizit als konstant deklarierte Objekte automatisch als stagewise-
immutable erkannt werden.
Die Diplomarbeit von Weißenborn [Wei03] beschäftigte sich mit Migrations-
und Replikationsstrategien. Hierbei wurden auch statische Analysen (siehe
Abschnitt 6.3.3) eingesetzt, um den Schreib- und Lese-Status der Methoden
zu ermitteln und diese zur Transformation zur Replikation bzw. Migration zu
verwenden. Es kann vorkommen, dass einige zu replizierende Objekte nicht
konstant sind. Bei nachträglichen Schreibzugriffen werden die Replikate nach
der dort entworfenen Strategie aktualisiert. Juggle [SH98] (siehe Abschnitt
4.1.4) setzte Techniken zur Objektreplikation in ähnlicher Weise ein, wenn die
Anzahl der Lesezugriffe nach einem Schreibzugriff einen vorkonfigurierbaren
Wert überschreitet.
6.5.2. Analysetechniken zur Objektverteilung
Um statische Programmeigenschaften wie isProxy bzw. isThreadLocal
zu bestimmen oder Abschätzung zu Ausführungskosten bzw.
Kommunikationskosten durchzuführen, wurde die Escape-Analyse bzw.
einige Varianten davon [Thi01], [WR99], [BH99], [CGS+99], [BCCH95],
[HBCC99] eingesetzt. Die dort beschriebenen Ansätze wurden für mehr-
163
KAPITEL 6. ANALYSETECHNIKEN ZUR AUTOMATISCHEN VERTEILUNG
strängige Java-Programme entworfen, werden aber meist zu anderen
Optimierungszwecken verwendet, z.B. zum Eliminieren überflüssiger Syn-
chronisation in Java-Programmen oder zur Beschleunigung des sequentiellen
Ablaufs durch das Verlagern lokaler Objekte vom Heap auf den Stack.
Diese Optimierung - also die Erkennung stack-lokaler Objekte - wurde auch
für die Lokalitätsoptimierung zur Verteilung benutzt, damit Proxy- oder
Thread-lokale Objekte nur lokal erzeugt werden.
In [Rin01a] wurde allgemein über die Points-to-Analyse für multithreaded
Programme zusammen mit ihren Anwendungsgebieten diskutiert und es
wurden Vergleiche zu zahlreichen verwandten Arbeiten gezogen. Wir ha-
ben eine einfache Variante der Points-to-Analyse [WR99], [SH97], [Ste96],
[Rin01b],[RR99], [Le00] in unserer Analyseumgebung [Thi01] realisiert. Unse-
re Points-to-Version funktioniert auch für multithreaded Java-Programme und
ist fluss- und kontext-insensitiv. Sie liefert Informationen zur Typinferenz des
gesamten Analysekontextes, welche als Grundlage für weitere Analysetech-
niken wie die vorgestellte PreciseCallGraph-Analyse (siehe Abschnitt 6.3.1)
und die interprozedurale Def-Use-Analyse (im Abschnitt 6.3.2) dienen.
Bei der Konstruktion eines Aufrufgraphen in unserer PreciseCallGraph-
Analyse wird das Ergebnis dieser Typinferenz dazu benutzt, um möglichst
genau dynamisch gebundene Aufrufziele für den Receiver statisch aufzulö-
sen. Die Problematik bezüglich spezieller Verbindungen im Aufrufgraphen
wie Kanten zu <clinit>-Methoden oder von start()-Methode zu run()-
Methode wurde in [Wes04] diskutiert. Die Arbeit von Wessels beschäftigte
sich ebenfalls mit der Konstruktion von Aufrufgraphen, die auf der Grund-
lage der Kombination mehrerer, in Schwierigkeitsstufen aufgeteilter Verfah-
ren [TP00] beruht. Der Ansatz zur FTA-Analyse in [TP00] wurde auch in
unserer GlobalDefUse-Analyse benutzt, wobei die Analyseinformation über
Definitions-/Verwendungsketten fluss- und kontext-insensitiv über alle Me-
thodenaufrufstellen hinaus aufgebaut wird. Hier werden erreichende Defini-
tionen für formale Parameter der Methoden über alle relevanten Aufrufstel-
len gesammelt. Copy-Propagation wird ebenfalls berücksichtigt und Defini-
tionen werden sowohl zu direkten als auch zu indirekten Benutzungen ver-
kettet.
Die statische Objektgraph-Analyse [Spi99b] stellt eine Approximation
der Laufzeitobjekte als Knoten dar und beschreibt die Erzeugungs-,
Verwendungs- und Referenzrelationen zwischen diesen durch Kanten. Durch
die vom Anwender selbst festgelegten Kriterien kann eine Verteilung daraus
abgeleitet werden. Dies ist eine quasi-automatische Verteilung, bei der der
164
6.5. VERWANDTE ARBEITEN
Anwender selbst das Programm partitionieren muss. Außerdem ist die Ab-
schätzung der Objektbeziehungen hierbei sehr konservativ.
Wie im Abschnitt 6.3.3 beschrieben, werden bei der Berechnung und Ab-
schätzung der Workload und Kommunikationskosten verschiedene Analy-
setechniken verwendet, die das Objektflussgraph-Modell [Klo02] als Berech-
nungsstruktur benutzen. Anders als bei JavaParty, wo Klassen und Metho-
den in sog. Konturen aufgesplittet werden und daher die polymorphen Ver-
wendungen von ihnen in monomorphe überführt werden können [Hau98],
[PH00], [HP01], werden in meinem Konzept verschiedene Analysetechniken,
u.a. die Points-to-, PreciseCallGraph-Analyse verwendet, um die Information
zur Typinferenz zu bestimmen.
Das Abschätzungsverfahren zu Workload und zur Kommunikation lehnt sich
stark an das Modell der Verteilungsanalyse in [Hau98] an. Das in diesem Kon-
zept verwendete Kostenmodell (siehe Abschnitt 6.2.2) zur hybriden Analyse
ist aber flexibel, dass dynamische Aspekte eingebracht werden können und
es eine Verbesserung der Verteilungsentscheidung zur Laufzeit ermöglicht.
Die polymorphic in-line cache (PIC)-Technik [DS84], [HCU91], ist eine bekannte
Technik aus Smalltalk-Implementierungen (und in Self [Höl94] ). Hier werden
an einer dynamisch gebundenen Aufrufstelle zur Laufzeit die tatsächlichen
Aufrufziele gespeichert und beim nächsten Aufruf werden die benutzten Me-
thoden mittels lookup-Technik aus dem Cache geholt und wiederverwendet.
Diese Idee kann in dem im Abschnitt 6.2.2 beschriebenen Kostenmodell ver-
wendet werden, um die Kosten an den dynamisch gebundenen Aufrufstel-
len besser zu berechnen. Dieses Konzept wurde aber noch nicht im JScatter-
System erprobt.
All diese dynamischen Aspekte, die bei der hybriden Analyse betrachtet und
zur Verbesserung der Verteilungsentscheidung zur Laufzeit angewandt wer-
den, haben ein ähnliches Ziel wie die Konzepte zur dynamischen Lastbalan-
cierung, die in Systemen wie Juggle (beschrieben in Abschnitt 4.1.4, [SH98])
oder cJVM [AFT99], [AFT+00] verwendet werden. Die automatische Lastver-
teilung wird in der System-eigenen JVM realisiert: das Lastmaß bei Juggle
richtet sich nach der Anzahl der Zugriffe aller Threads auf ein Objekt, die in
einer bestimmten Zeitspanne gezählt werden. Die Auslastung der beteiligten
Rechnerknoten wird periodisch überprüft und nach jeder Objektplatzierung
korrigiert.
Mit diesem Ansatz werden zur Laufzeit anhand der gesammelten Laufzeitda-
ten sowie der ständig aktualisierten Rechnerlasten die Verteilungsentschei-
165
KAPITEL 6. ANALYSETECHNIKEN ZUR AUTOMATISCHEN VERTEILUNG
dungen verbessert und die Objekte danach entsprechend (um)platziert. Bei
der hybriden Analyse werden vor der Laufzeit alle Programmeigenschaften
soweit wie möglich berechnet. Die fehlende Information wird nachträglich
zur Laufzeit beschafft und zur Verbesserung der Entscheidung verwendet.
Im Vergleich mit der Lastbalancierungstechnik ist die Methodik und die Art
und Weise der Informationsverwendung im JScatter dennoch sehr verschie-
den, obwohl die Ziele sich ähneln.
166
7. Infrastruktur und Realisierung des
Systems JScatter
Inhalt
7.1. Die PAULI - Analyseumgebung . . . . . . . . . . . . . . . . . . . 169
7.1.1. Struktur der Analyseumgebung . . . . . . . . . . . . . . . 169
7.1.2. Vorteile durch den Einsatz der Analyseumgebung . . . 171
7.2. Programmtransformation . . . . . . . . . . . . . . . . . . . . . . 172
7.2.1. Transformationskonzept . . . . . . . . . . . . . . . . . . . 172
7.2.2. Transformation . . . . . . . . . . . . . . . . . . . . . . . . . 175
7.3. Berechnung der Programmeigenschaften . . . . . . . . . . . . 176
7.3.1. Verteilungsberechnung der Verteilungspläne . . . . . . . 178
7.3.2. Programmanalyse . . . . . . . . . . . . . . . . . . . . . . . 179
7.4. Verteilte Laufzeitumgebung . . . . . . . . . . . . . . . . . . . . . 180
7.4.1. Zusammenarbeit der Komponenten . . . . . . . . . . . . 180
7.4.2. Verwendung des Verteilungsplans . . . . . . . . . . . . . 181
7.5. Systemvoraussetzungen und Verbesserungen . . . . . . . . 183
7.5.1. Systemanforderungen . . . . . . . . . . . . . . . . . . . . . 183
7.5.2. Verbesserungen . . . . . . . . . . . . . . . . . . . . . . . . . 184
167
KAPITEL 7. INFRASTRUKTUR UND REALISIERUNG DES SYSTEMS
JSCATTER
Das JScatter-System wurde entwickelt, um mehrsträngige Java-
Anwendungen auf die virtuellen Maschinen in verschiedenen miteinander
vernetzten Rechnern automatisch zu verteilen. Es entstand ursprünglich im
Rahmen der Projektgruppe „Verteilung von parallelen Java-Anwendungen“
[FFF+02] an der Universität Paderborn unter dem Namen ParJava und wird
seitdem weiterentwickelt.
Verteilte Laufzeitumgebung
Bytecode-Form
while (...) {
Object o = new Object();
}O1 , ... , On
Bytecode-Form
while (...) {
Object o = new RemoteObject();
}RO1 , ... , ROn
Programmanalyse
Verteilungsplan
JVM 1
Automatische Transformation
Mehrsträngiges
Java-Programm
Verteilbares Programm
Vor der Laufzeit
Zur Laufzeit
RO1
T1
Plan
JVM 2
ROi
T2 ROj
JVM 3
ROn
T3
Programm-
eigenschaften
Plan Plan
Programmanalyse
automatische Verteilung
Hüllenbildung
Transformator
Abbildung 7.1.: Das JScatter-System mit seinen Komponenten
Abbildung 7.1 stellt die gesamte Infrastruktur des Systems mit seinen Kom-
ponenten grafisch dar. Als Eingabe liegt ein multithreaded Java-Programm
in Java-Bytecode vor. Zusammen mit den verwendeten Bibliotheken wird eine
transitive Hülle [Wes04] gebildet. Sie enthält alle Methoden und Klassen der
Anwendung und diejenigen der verwendeten Bibliotheken, die zur Ausfüh-
rung des Programms benötigt werden. Darauf wird die statische Programm-
analyse angewandt, um die für die Transformation relevanten Eigenschaf-
ten des Eingabeprogramms sowie diejenigen, die zur Berechnung der Ver-
teilungsentscheidung benötigt werden, vor der Laufzeit zu bestimmen. An-
schließend wird eine verteilte Version des Programms mittels des Transfor-
168
7.1. DIE PAULI - ANALYSEUMGEBUNG
mators, ebenfalls in Bytecode, generiert, die dann auf der verteilten Laufzeit-
umgebung des Systems mit den Platzierungshinweisen aus dem Verteilungs-
plan ausgeführt werden kann. Während der Ausführung werden Laufzeitda-
ten mittels dynamischer Analyse bestimmt und dann zur Verbesserung der
Verteilungsentscheidung eingesetzt.
Dieses Kapitel beschreibt einige ausgewählte Realisierungsaspekte des ge-
samten Systems. Die Analyseverfahren, die zur Berechnung der statischen
Programmeigenschaften im Verteilungsplan zum Einsatz kommen, werden
in unserer Analyseumgebung realisiert. Ihre Struktur und Funktionalität wer-
den im ersten Abschnitt vorgestellt. Anschließend wird das Transformati-
onskonzept beschrieben, mit dem das Programm in eine verteilte Version
transformiert wird. Zur Platzierung der entstehenden Objekte werden zu-
nächst die für die Verteilung relevanten Programmeigenschaften berechnet.
Sie werden unmittelbar in den ODFs der verwendeten Strategien und somit
im Verteilungsplan verwendet. Die Realisierung und das Zusammenwirken
der Strategien innerhalb eines Verteilungsplanes werden hierbei dargestellt.
Das transformierte Programm wird in einer verteilten Laufzeitumgebung, ei-
ner Komponente des JScatter-Systems, ausgeführt. Die Entwurfsaspekte da-
zu werden im folgenden Abschnitt beschrieben. Abschließend werden einige
Verbesserungsmöglichkeiten für das System diskutiert.
7.1. Die PAULI - Analyseumgebung
Die Analyseverfahren zur Berechnung der Programmeigenschaften wurden
als Module unserer Analyseumgebung realisiert. Diese von unserer Fach-
gruppe „Programmiersprachen und Übersetzer“ entwickelte Analyseumge-
bung enthält viele grundlegende Analysen. Sie lässt sich einfach um weitere
Analysen erweitern. Im Folgenden werde ich die Struktur und die Funktio-
nalität der Analyseumgebung beschreiben. Weshalb sie hier verwendet wird,
wird anschließend begründet.
7.1.1. Struktur der Analyseumgebung
Abbildung 7.2 stellt die PAULI-Struktur dar. Das Anwendungsprogramm
zusammen mit seinen benutzten Bibliothek-Klassen (transitive Hülle) bildet
einen Analysekontext und wird der Analyseumgebung in Bytecode-Form
169
KAPITEL 7. INFRASTRUKTUR UND REALISIERUNG DES SYSTEMS
JSCATTER
database for
analysis results
AnalysisManager
textual
output
graphical
output
AnalysisPlugin
request
input
result
external
request
available
analysis
context
ClassUniverse
class
method
method
method
package
class class
package
delegated
requests
Abbildung 7.2.: Die Infrastruktur der Analyseumgebung [Thi01]
als Eingabe übergeben. Der Bytecode wird zunächst untersucht und mittels
BCEL [Daha] - eine an der Freien Universität Berlin entwickelte Byte Code En-
gineering Library - in interne Datenstrukturen überführt, auf denen sämtliche
Analysen arbeiten. Abbildung 7.3 stellt das Objektmodell der zu analysieren-
den Software in Form eines UML-Klassendiagramms dar. Die Assoziationen
beschreiben die Zuordnung der erzeugten Repräsentanten für die elementa-
ren Strukturen eines Java-Programms wie Pakete, Klassen, Methoden oder
Felder. JavaClass library ist hier die Bezeichnung für die BCEL-Bibliothek.
In einem so genannten ClassUniverse in Abbildung 7.2 können neben den
Grundstrukturen auch mehrere Pakete zusammengefasst werden. Der ge-
samte Analysekontext wird somit in mehrere Teilmengen eingeteilt und für
bestimmte Analysen eingeschränkt, während andere Analysen ggf. den ge-
samten Kontext betrachten müssen. Die Komponente AnalysisManager ist für
die Verwaltung und Steuerung der AnalysisPlugins zuständig. Ein Analyse-
verfahren wird durch ein AnalysisPlugin, also ein Analyse-Modul, realisiert.
Ergebnisse verschiedener Analysen werden in einer Datenbank verwaltet, da-
mit bestimmte Analysen auf Ergebnisse anderer zugreifen und diese wieder-
verwenden können. Liegt das angefragte Ergebnis noch nicht vor, wird der
AnalysisManager informiert, so dass dieser das erforderliche Modul aufruft.
170
7.1. DIE PAULI - ANALYSEUMGEBUNG
passed to
constructor
Analysis framework (excerpt)
JavaClass library (excerpt)
passed to
constructor
Analysis framework (excerpt)
JavaClass library (excerpt)
InstructionHandle
InstructionList
JavaPackage
JavaMethod
1
111 MethodGen
111
JavaField Field
Method
Instruction *111111
1
******
1 111111
1 111
JavaClassMember FieldOrMethod
1
111
JavaClass
111
111
ProgramObjectLocator
111
***
1 111111
111
1 111111
1
111
***
***
JavaPackageMember
***
ClassUniverse
Abbildung 7.3.: Objektmodell für die zu analysierende Software [Thi01]
Mit diesem Mechanismus findet der gesamte Ablauf für den Benutzer völlig
transparent statt und es wird dadurch sichergestellt, dass jedes Analyseergeb-
nis nur einmal berechnet wird. Eine detaillierte Beschreibung der Analyseum-
gebung findet sich in [Thi01] im Kapitel 6.
7.1.2. Vorteile durch den Einsatz der Analyseumgebung
Mit PAULI werden elementare Teile eines Java-Programms in entsprechender
Form repräsentiert, wobei sich alle wichtigen Programmeigenschaften finden
lassen, auch diejenigen für die Verteilungsberechnung. Zur Berechnung sol-
cher Eigenschaften werden neue statische Analysen auf einfache Weise imple-
171
KAPITEL 7. INFRASTRUKTUR UND REALISIERUNG DES SYSTEMS
JSCATTER
mentiert, die noch den Vorteil zur Wiederverwendung vorhandener, grund-
legender Analyseergebnisse sowie elementarer Datenstrukturen wie z.B. ge-
richteter Graphen nutzen.
Durch die internen Strukturen zur Repräsentation von Java-
Programmobjekten und passenden Analyseergebnissen für die Verteilungs-
strategien, sowie Zugriffsmöglichkeiten auf die interne Verwaltungsstruk-
turen wird die Transformation des Programms in eine verteilte Version auf
Bytecode-Ebene erheblich erleichtert. Die Analyse sowie die Transformation
des Programms auf Bytecode-Ebene haben außerdem den Vorteil, dass
man unabhängig davon ist, ob der Quellcode eines Programms oder der
verwendeten Bibliotheken zur Verfügung steht oder nicht. So können z.B.
Erzeugungsstellen und Verwendungsstellen für die dort zu erzeugenden
Objekte transformiert werden.
7.2. Programmtransformation
Ziel ist es, das ursprüngliche Programm so zu transformieren, dass die trans-
formierte Anwendung in der verteilten Laufzeitumgebung ausgeführt wer-
den kann. Damit das Programm mit eingebautem RMI-Mechanismus verteilt
abläuft, wird jede Klasse des Originalprogramms in mehrere Klassen zerlegt.
Hierbei muss beachtet werden, dass bei der verteilten Ausführung die Seman-
tik der neu entstehenden Klassen die Semantik der ursprünglichen Klasse
nicht verletzen darf. Wie bei der sequentiellen Ausführung darf es den stati-
schen Anteil, im Folgenden Klassenanteil genannt, auch nur einmal während
des gesamten verteilten Ablaufs geben. Im Gegensatz dazu kann der Instan-
zenanteil jedoch mehrfach auf jedem beteiligten Rechner vorhanden sein, von
jedem entfernten Rechner aus zugegriffen werden und muss eine eindeutige
Identität besitzen. Aus diesem Grund wurde ein Konzept zur Transformation
entwickelt, das aber auch die Infrastruktur der verteilten Laufzeitumgebung
beeinflusst.
7.2.1. Transformationskonzept
Angelehnt am verwendeten Konzept in JavaParty-System (siehe Abschnitt
4.1.1), bei dem aus einer Klasse sechs Klassen und sieben Interfaces entstehen,
wird eine Java-Klasse im JScatter-System in vier Klassen und zwei Interfaces
zerlegt. Abbildung 7.4 zeigt die Klassenaufteilung grafisch.
172
7.2. PROGRAMMTRANSFORMATION
A
(Originalklasse)
A
(Instanzanteil von A)
_AInstanceManager
(Instanzverwalter auf einem Knoten)
_Ahandle
(dient als Proxy für remote Zugriffe)
_AClassObject
(statischer Anteil)
_AInstanceManagerInterface
(entspricht RMI-Kondition)
_AClassObjectInterface
(entspricht RMI-Kondition)
Generierung
Vererbung
Implementierung
Abbildung 7.4.: Klassenaufteilung einer Klasse
Die neue gleichnamige Klasse repräsentiert den Instanzenanteil (sog. Cuckoo-
Klasse) der ursprünglichen Klasse. Statische Felder und Methoden der Origi-
nalklasse bilden den statischen Anteil, der im Beispiel als _AClassObject
zu erkennen ist. In dieser Klasse sind jedoch die ursprünglich als statisch
deklarierten Felder und Methoden nicht mehr statisch, sondern als Instan-
zenfelder und -methoden definiert. Dieser Transformationsschritt ist erfor-
derlich, denn der statische Bestandteil einer Klasse existiert nur einmal in ei-
ner JVM. Die verteilte Laufzeitumgebung besteht aber aus mehreren JVMs,
die sich auf verschiedenen Rechnern aufhalten. Daher wird zur Laufzeit ei-
ne einzige den statischen Anteil einer Klasse darstellende Instanz einmal
auf einer bestimmten JVM erzeugt und existiert auch nur einmal für die
gesamte Umgebung. Um aber Zugriffe auf diese Instanz aus verschiedenen
Adressräumen zu ermöglichen, steht in allen JVMs ein Stellvertreter auf die-
se eindeutige Instanz zur Verfügung. Für die Kommunikation über RMI
zwischen dem Stellvertreter und dem Original wird ein Interface namens
_AClassObjectInterface generiert, das die entfernte Schnittstelle auf
_AClassObject definiert.
173
KAPITEL 7. INFRASTRUKTUR UND REALISIERUNG DES SYSTEMS
JSCATTER
Object
Serializable
A
_AHandel
_AInstanceManager
ClassObject
UnicastRemoteObject
InstanceManager
_AClassObject
_AInstanceManagerInterface
Remote
Handle
_AClassObjectInterface
1 ..*
1
n
besitzt
besitzt
n-Assoziation
Vererbung
Implementierung
Anwendungsklasse
Klasse von JScatter
Bibliothek-Klasse
Abbildung 7.5.: Hierarchie der aufgeteilten Klassen
Die generierte Klasse _AInstanceManager verwaltet alle Instanzenantei-
le repräsentierende Objekte der ursprünglichen Klasse innerhalb einer JVM.
Damit ist es wie beim sequentiellen Ablauf möglich, mehrere Instanzen einer
Klasse in der gesamten verteilten Umgebung zu erzeugen und jede von ih-
nen lässt sich dadurch eindeutig identifizieren. So existiert zur Laufzeit auf
jeder beteiligten JVM genau eine Verwaltungsinstanz pro Originalklasse. Die
Zugriffe auf den Instanzenanteil eines remote-Objekts finden bei diesem Kon-
zept durch Handles statt. Hier wird der Delegationsmechanismus verwen-
det, um alle Zugriffe auf ein remote-Objekt über das zugehörige Handle zu
ermöglichen. Methodenaufrufe und Feldzugriffe werden vom Handle an die
Verwaltungsinstanz weitergeleitet, die diese dann an das richtige Objekt wei-
tergibt. So wird für eine Klasse Adie Handle-Klasse _AHandle generiert. Ein
Handle eines Objekts kennt seine Verwaltungsinstanz und damit auch das auf
einem Rechner platzierte Objekt. Die Handles werden ebenfalls als Parameter
174
7.2. PROGRAMMTRANSFORMATION
und Rückgabeergebnis verwendet. Abbildung 7.5 zeigt die generierten Klas-
sen mit ihrer hierarchischen Beziehung zu denjenigen des JScatter-Systems.
7.2.2. Transformation
Transformator
PAULI
(Programmanalyse)
Verteilte Version
Class C
................
................ Class B
................
................Stub
................
................
Terface
................
................
Skeleton
................
................
Manager
................
................
Class A
................
................
_CClassObject
..............................
..............................
..............................
Transitive Hülle (Pauli intere Repsentation)
Class C
................
................
Class B
................
................
Class A
................
................
Lib-Klasse D
................
................
Lib-Klasse E
................
................
Lib-Klasse F
................
................
Java-Anwendung
Class C
................
................
Class B
................
................
Class A
................
................
Phase 1: Hüllenbildung JODE
Phase 2: Stellenersetzung
Phase 3: Klassen erzeugen
Phase 4: Stubs und Skeleton erzeugen
Verteilungsplan
Programm-
eigenschaften
BCEL
KaRMI-Tool
Abbildung 7.6.: Transformationsschritte mit verwendeter Software
Die Transformation besteht im Wesentlichen aus 4 Phasen. Die erste Phase ist
die Hüllenbildung. Aus der eingegebenen Anwendung wird zunächst mittels
JODE (Java Optimize and Decompile Environment) [Hoe] die zugehörige transi-
tive Hülle [Wes04] gebildet. Die Analyseumgebung PAULI nimmt diese als
Analysekontext und überführt sie in eine interne Darstellungsstruktur. Dar-
auf werden die eigentlichen Transformationsschritte durchgeführt.
Unter Verwendung der Platzierungsstrategien in einem Verteilungsplan wer-
den Erzeugungsstellen zu Objekten transformiert, die mittels statischer Ana-
lyse als remote gekennzeichnet werden. So werden Instruktionen zur Abfra-
ge an den Verteilungsplan zur Erzeugungsinstruktion hinzugefügt, damit die
175
KAPITEL 7. INFRASTRUKTUR UND REALISIERUNG DES SYSTEMS
JSCATTER
zur Laufzeit getroffene Entscheidung zur entfernten Erzeugung der Objekte
genutzt wird. Außerdem werden alle betroffenen Stellen wie z.B. Verwen-
dungsstellen der remote Objekte, deren Methoden usw. entsprechend so geän-
dert, dass entfernte Zugriffe darauf zur Laufzeit durchgeführt werden kön-
nen. Anschließend werden Klassen und Interfaces entsprechend dem obigen
Konzept generiert. Mittels Werkzeugen von KaRMI werden die für entfernte
Kommunikation benötigten Stubs und Skeletons erzeugt.
Die Phasen 2 bis 4 arbeiten prinzipiell auf den internen Strukturen der
Analyseumgebung und somit BCEL. Dadurch wird die Transformation auf
Bytecode-Ebene erleichtert. Außerdem müssen während der Transformation
bestimmte statische Analysen durchgeführt werden. So müssen Instruktio-
nen zur Definition aktuell übergebener Parameter einer Methodenaufrufstel-
le sowie ihre in Frage kommenden Typen gefunden werden, damit z.B. ent-
sprechende Handle-Typen erzeugt werden. Bei Verwendungsstellen muss
festgestellt werden, ob ein Zugriff hier ein lokaler oder entfernter ist und
dementsprechend müssen Instruktionen zu Handle-Aufrufen generiert wer-
den. Durch Verwendung der Analyseumgebung hat man den Vorteil, dass
vorhandene Analyseergebnisse wiederverwendet und ggf. auch notwendige
Analysen durchgeführt werden. Mittels BCEL lassen sich die Transformatio-
nen der Stellen im Programm sowie die Manipulation, Erzeugung neuer In-
struktionen und neuer Klassen einfacher handhaben.
7.3. Berechnung der Programmeigenschaften
Verantwortlich für die Verteilungsberechnung sind die ODFs. Durch Anwen-
dung einiger von ihnen in bestimmter Reihenfolge können Ergebnisse der
verwendeten Strategien, somit der eingesetzten Verteilungspläne ermittelt
werden. Abbildung 7.7 stellt die gesamte Struktur der ODFs, ihre Zusam-
menhänge bei der Berechnung der Verteilungsentscheidung in den Strategien
sowie in den Verteilungsplänen dar. Dort sind auch einige ausgewählte Ana-
lyseverfahren angegeben, die zur Berechnung der Programmeigenschaften
verwendet und in der oben vorgestellten Analyseumgebung implementiert
wurden. Diese Komponenten wurden realisiert, um das Konzept zur auto-
matischen Verteilung unter Einsatz der Programmanalysen zu evaluieren.
176
7.3. BERECHNUNG DER PROGRAMMEIGENSCHAFTEN










Verteilungsstrategien
Zusammensetzung
bestimmter ODFs
Verteilungspläne
Zusammensetzung
bestimmter Strategien
 !"#$%
!&'$% ()*
$%

 
 $%
 +'!,-$%
+ ,, $%
+ .$%
,!..! $%
 / ..01-$%2
3343 0..01-$%
3343 ! 5!6'..01-$%
",! '$% 06.3,!6 6

 !"#

50..


Funktionen zur Berechnung
der Programmeigenschaften
Laufzeiteigenschaft
Objektverteilungs-
funktionen (ODFs)
Werte-
Bindung
Statische P-Eigenschaft
Dynamische P-Eigenschaft
Verteilungs-
datenbank
Plan-Evaluator Anfragen
Ergebnisse
+!' ',, $%
5! 5!6'..
 1-$%

+'-!,-
!&$%
",! $%
7


8
9
Plan A
 7 
Plan B
 7  
Plan C
8
CHA
PreciseCallGraph-
Analyse
Escape-
Analyse
Stagewise-Immutable-
Analyse
Global Def-Use
Analyse
Local Def-Use
Analyse
Peer-
Analyse
Hybride Analyse
Instruktion-Def-Use
Analyse
Programmanalyse
Statische Verfahren
Dynamische Verfahren
Programmanalyse
Objectflussgraph-
Analyse
Loop-Def-
Analyse
Induction
Variable
execCost_d( )
run_factor
Loop-Iter-
count
Abbildung 7.7.: Infrastruktur der verwendeten ODFs, Strategien und
Verteilungspläne
177
KAPITEL 7. INFRASTRUKTUR UND REALISIERUNG DES SYSTEMS
JSCATTER
7.3.1. Verteilungsberechnung der Verteilungspläne
Modellierte ODFs
In Abbildung 7.7 sind die realisierten ODFs angegeben, die innerhalb der zur
Evaluation eingesetzten Verteilungspläne verwendet wurden. Das sind einige
statische ODFs, z.B. zur Berechnung der ThreadLocal- oder stagewise-immutable
Eigenschaften. Zur grundlegenden Verteilungsentscheidung der Aktivitäten
sowie der Datenobjekte benötigte man Basis-Verteilungsfunktionen, die die
dynamischen Programmeigenschaften zum Workload und Kommunikations-
aufwand vor der Laufzeit berechnen und ggf. abschätzen.
In diesem Konzept wird die Berechnung der Programmeigenschaften mittels
Programmanalyse von der Verteilungsberechnung mittels der ODFs deutlich
getrennt. Somit hängt also die Güte der Ergebnisse nur davon ab, mit wel-
chen Analysen man solche Programmeigenschaften bestimmt. Sie werden bei
der Realisierung mittels Funktionen (siehe zweite Schicht von unten in Abbil-
dung 7.7) berechnet. Ihre Ergebnisse werden direkt in den ODFs verwendet,
um dort elementare Verteilungsentscheidung ermitteln zu können.
Neben den statischen Programmeigenschaften gibt es einige Eigenschaften,
die vom Laufzeitverhalten des Programms abhängig sind oder Architektur-
information beschreiben. Diese werden als variable Laufzeitfaktoren in den
Berechnungen modelliert. Sobald ihre konkreten Werte, z.B. die Anzahl der
JVMs oder die Anzahl der schon durchgelaufenen Iterationen einer Schleife,
während der Programmausführung bekannt sind, werden sie an ihren Ver-
wendungsstellen gebunden.
Verteilungsstrategien
Die Zusammenfassung bestimmter ODFs definiert dann eine Verteilungsstra-
tegie. Die bei der empirischen Untersuchung im nächsten Kapitel verwende-
ten Strategien zur Verteilung der Objekte sind in Abbildung 7.7 angegeben.
Das sind die Strategien zur Lokalitätsoptimierung (Strategie1), zur Verteilung
der Aktivitäten (Strategie3), zur Gruppierung der Datenobjekte mit ihren Ak-
tivitäten anhand Workload und Kommunikation (Strategie2) und zur Replika-
tion (Strategie4) nach der besonderen Eigenschaft stagewise-immutable. (Strate-
gie5) definiert eine einfache zyklische Verteilung aller remote-Objekte.
Ein Verteilungsplan wird durch Verwendung bestimmter Strategien definiert.
Die vor der Laufzeit getroffenen Platzierungsentscheidungen für Objektre-
178
7.3. BERECHNUNG DER PROGRAMMEIGENSCHAFTEN
präsentanten eines Planes werden in eine Datenbank zur Verwaltung einge-
tragen. Ebenfalls wird benötigte Information für jede Erzeugung zur Laufzeit
hierin aufgenommen, z.B. welches Objekt (also mit welchem ID) aus welcher
Erzeugunsstelle auf welchen Rechner platziert wird.
Plan-Evaluator
Bei der Transformation werden Erzeugungsstellen für remote-Objekte um
Anfragen an den Plan so erweitert, dass unmittelbar vor der tatsächlichen
entfernten Erzeugung der Verteilungshinweis abgefragt werden kann. So
hat der Plan-Evaluator die Aufgabe, vor und zur Laufzeit Anfragen nach
Transformations- und Verteilungshinweisen vom Programm zu beantworten.
Vor der Laufzeit erhält er Fragen zur Transformationsentscheidung, ob eine
Erzeugungsstelle zu remote transformiert wird oder nicht. Nach der Planeva-
luation liefert er neben Transformationsergebnissen auch Codesegmente zur
Planabfrage, die an einer Erzeugungsstelle eingebaut werden müssen, wenn
das zu erzeugende Objekt ein remote-Objekt wird. Zur Laufzeit nimmt der
Plan-Evaluator die gestellte Anfrage entgegen, leitet sie an den Verteilungs-
plan weiter und gibt die Entscheidung des Planes an den Fragenden zurück.
Wie eine entfernte Erzeugung unter Verwendung des Verteilungshinweises
abläuft, wird im folgenden Abschnitt 7.4 beschrieben.
7.3.2. Programmanalyse
Alle Analyseverfahren, die zur Berechnung der Eigenschaften zum Einsatz
kommen, wurden in der Analyseumgebung PAULI realisiert. In Abbildung
7.7 sind einige von ihnen dargestellt. Jede Analyse wurde als ein Analyse-
Modul (Analysis-Plugin) in PAULI implementiert. Durch die Infrastruktur
der Analyseumgebung können Ergebnisse vorhandener Analysen wie z.B.
CHA, Local-Def-Use-Analyse usw. innerhalb der Zwischenschritte der neu-
en Verfahren wiederverwendet werden. Das Konzept zur hybriden Analyse
wurde in dieser Arbeit zur Workload-Berechnung für Schleifenstrukturen er-
probt, wie es in Abbildung 7.7 gezeigt wird: Hier findet sich ein unbekann-
ter Laufzeitfaktor in den Berechnungsschritten zum dynamischen Workload
(run_factor). Dieser Faktor modelliert die Anzahl der auszuführenden Itera-
tionen einer Schleife. Sein Wert ergibt sich zur Laufzeit und wird in die Be-
rechnung direkt eingesetzt. Somit wird die Funktion execCost_d() zum
179
KAPITEL 7. INFRASTRUKTUR UND REALISIERUNG DES SYSTEMS
JSCATTER
Workload erneut berechnet. Verteilungsstrategien, die auf diesen Kosten ba-
sieren, verbessern dann ihre Platzierungsentscheidungen für zukünftig ent-
fernt zu erzeugende Objekte. Durch das hier benutzte Bindungskonzept kann
diese Berechnungskette automatisch ausgelöst werden.
7.4. Verteilte Laufzeitumgebung
RuntimeManager
(RMI-Server) - Verwaltungsaufgaben
- Logging
...
Zwischenschicht zw.
Programm und LZU
JVM
Programm
Plan
RuntimeEnvironment
Node
(RMI-Client)
JVM
Programm
Plan
RuntimeEnvironment
Node
(RMI-Client)
...
Abbildung 7.8.: Abstraktion der verteilten Laufzeitumgebung
Die für den Benutzer transparente Ausführung der verteilten Version des Pro-
gramms übernimmt die verteilte Laufzeitumgebung des JScatter-Systems. Sie
verbindet die auf den beteiligten Rechnern vorhandenen JVMs miteinander.
Abbildung 7.8 zeigt die Abstraktion der verteilten Laufzeitumgebung (LZU).
7.4.1. Zusammenarbeit der Komponenten
Zur entfernten Kommunikation wird hierbei KaRMI - eine schnellere RMI-
Version der Universität Karlsruhe - verwendet. Ein Node dient als RMI-Client
und repräsentiert eine JVM. Zur Verwaltung der mit der LZU verbundenen
Nodes ist eine Instanz der Klasse RuntimeManager zuständig. So werden
Fehlermeldungen der JVMs sowie Ausgaben des verteilten Programms an
180
7.4. VERTEILTE LAUFZEITUMGEBUNG
diese Instanz weitergeleitet und dort ausgegeben. Sie fungiert auch als der
RMI-Server. Als Zwischenschicht oder Schnittstelle zwischen der LZU und
dem ausgeführten Programm wird die Klasse RuntimeEnvironment ver-
wendet. Zur Laufzeit existiert pro JVM genau eine Instanz dieser Klasse, in
der die Instanzen- und Klassenanteile repräsentierenden Objekte verwaltet
werden. Über diese Schnittstelle kann das verteilte Programm auf diese An-
teile zur Laufzeit zurückgreifen und entfernte bzw. lokale Zugriffe durchfüh-
ren.
Verteilte Laufzeitumgebung
JVMJVM
Node2_Skeleton
Node1_Stub
RuntimeManager_Skeleton
Klassenanteil-
Objekte
Instanzenanteil-
Objekte
Node1
Node1_Skeleton
Node2_Stub
RuntimeEnvironment
RuntimeManager_Stub
Node1_Stub
Node2_Stub
RuntimeManager_Stub
Verteilungsplan
Programm-
eigenschaften
Verteilungsplan
Programm-
eigenschaften
Klassenanteil-
Objekte
Instanzenanteil-
Objekte
Node2 RuntimeEnvironment
RuntimeManager
Klasse von JScatter-Laufzeitumgebung
Generierte Klassen
Referenz
Remote Referenz
Abbildung 7.9.: Komponenten der verteilten Laufzeitumgebung
Zur Verteilung der erzeugten Objekte existiert auf jeder JVM eine Instanz
des verwendeten Verteilungsplans. Somit kann für jede (entfernte) Objekter-
zeugung im Programm die Planabfrage durchgeführt werden. Abbildung 7.9
stellt die Zusammenarbeit der vorgestellten Komponenten der LZU grafisch
dar.
7.4.2. Verwendung des Verteilungsplans
Nach der Transformation des Programms befindet sich an jeder Erzeugungs-
stelle für remote-Objekte eine Planabfrage an die lokale Instanz des Vertei-
181
KAPITEL 7. INFRASTRUKTUR UND REALISIERUNG DES SYSTEMS
JSCATTER
lungsplans. Mittels dieses Aufrufs kann vor der entfernten Erzeugung eines
Objekts die Zielmaschine bestimmt werden. Danach führt die LZU die Plat-
zierung durch. Abbildung 7.10 stellt dieses Szenario grafisch dar.
Verteilte Laufzeitumgebung
JVMJVM
vor der Laufzeit
während der Transformation
zur Laufzeit
RuntimeEnvironment
Node
Class A
................
................
................
RuntimeEnvironment
Node
Class B
................
................
................
Java-Anwendung
Class C
................
................
Class B
................
................
Class A
................
................
Verteilungsplan
Programm-
eigenschaften
C
Verteilungsplan
Programm-
eigenschaften
Verteilungsplan
Programm-
eigenschaften
Klasse von JScatter-Laufzeitumgebung
Generierte Klassen
Referenz
Remote Referenz
Instruktion zur Verteilungsplan-Anfrage
Abbildung 7.10.: remote NEW
Hierbei soll bei der Abarbeitung einer Methode von Objekt Aein Objekt
der Klasse Cauf einem anderen Rechner entfernt erzeugt werden. An der
zugehörigen Erzeugungsstelle für das Objekt Cwird die Abfrage an den
lokalen Verteilungsplan über die RuntimeEnvironment-Instanz gestellt.
Dort wird entschieden, dass das Objekt Cauf dem zweiten Rechner erzeugt
werden soll. Die Laufzeitumgebung führt diese Aufgabe über die lokale
RuntimeEnvironment und über die Nodes durch, die die beiden JVMs re-
präsentieren. Nachdem das Objekt erzeugt wurde, wird die Referenz darauf
182
7.5. SYSTEMVORAUSSETZUNGEN UND VERBESSERUNGEN
über einen zugehörigen Handle zurückgegeben. Über diesen Handle kann
das Objekt während der Programmausführung angesprochen werden.
Technische Details sowie Entwurfsentscheidungen des Systems wurden aus-
führlich in [FFF+02] beschrieben. Die Realisierung der Komponenten zur Mi-
gration und Replikation wurde ausführlich in der Diplomarbeit von Weißen-
born [Wei03] beschrieben. Ein zu erwähnender Aspekt der Laufzeitumge-
bung ist der Folgende: Wenn während der Programmausführung ein Kno-
ten ausfällt oder das Programm abbricht, z.B. aufgrund einer Exception,
muss die gesamte Umgebung mit dem Programm erneut gestartet werden.
Dies macht die Realisierung der verteilten Laufzeitumgebung einfacher. Sonst
müsste man im Fall des Ausfall eines Knotens u.a. die Laufzeitobjekte mit ih-
ren aktuellen Zuständen retten und diese auf einen anderen Knoten verteilen.
Dies ist vergleichbar mit dem Mechanismus zum Retten der Prozesse bei ver-
teilten Betriebssystemen.
7.5. Systemvoraussetzungen und Verbesserungen
7.5.1. Systemanforderungen
Das JScatter-System wurde entworfen, um das Konzept zur automatischen
Verteilung mehrsträngiger Java-Anwendungen zu erproben. Zusammen mit
PAULI ist es komplett in Java implementiert und kann deshalb plattformun-
abhängig verwendet werden.
Die Grundvoraussetzung für den Einsatz des Systems ist eine vorinstallierte
JDK ab Version 1.3.1. Zur Analyse, Hüllenbildung sowie Transformation des
multithreaded Java-Programms in eine verteilte Version mit (Ka)RMI werden
zusätzlich Java-Bibliotheken zu PAULI, JODE, KaRMI und JScatter selbst be-
nötigt. Um das transformierte Programm verteilt auszuführen, müssen neben
der Standard-Java-Bibliothek das JScatter-System, KaRMI und das eigentliche
transformierte Programm (in Form von JAR-Dateien) vorliegen. Zur Ausfüh-
rung müssen die Klassen aus den verwendeten Bibliotheken sowie aus der
Anwendung selbst auf jedem Rechner-Knoten geladen werden können. Das
heißt, dass die in JAR-Dateien vorliegenden Klassen jedem Rechner-Knoten
auf irgendeine Weise zur Verfügung stehen müssen. Diese JAR-Dateien müs-
sen beispielsweise über NFS, AFS usw. zugreifbar sein oder sich lokal auf
jedem Knoten befinden.
183
KAPITEL 7. INFRASTRUKTUR UND REALISIERUNG DES SYSTEMS
JSCATTER
Zur Kommunikation während der verteilten Ausführung wird (Ka)RMI, also
Remote Method Invocation, eingesetzt. Die auszutauschenden Stromdaten wer-
den über das Netzwerk mit Hilfe eines von KaRMI unterstützten Netzwerk-
Protokolls verschickt. Die Verbindung zwischen den verwendeten Rechnern
zur verteilten Berechnung muss also über dieses Protokoll aufgebaut wer-
den. Derzeit unterstützt KaRMI zwei Netzwerk-Technologien: Die Standard-
Übertragung über TCP/IP (Socket-Kommunikation) und Myrinet/GM.
KaRMI bietet aber auch die Möglichkeit, weitere Netzwerk-Technologien für
spezielle Protokolle oder Übertragungstechniken zu entwickeln.
Man kann also das JScatter-System zur automatischen Verteilung mehrsträn-
giger Java-Anwendungen überall benutzen, wo die oben genannten, einfa-
chen Voraussetzungen erfüllt werden. So wird im Kapitel 8 gezeigt, dass das
JScatter-System zur empirischen Untersuchung nicht nur auf einem norma-
len PC-Cluster, sondern auch auf einem speziellen parallelen Rechner-System
(ARMINIUS - Compute and Visualization Cluster) unseres Paderborn Center
for Parallel Computing (PC2)[Pad] eingesetzt wurde.
7.5.2. Verbesserungen
Viele Entwurfsentscheidungen des JScatter-Systems basieren darauf, dass die
Komponenten einfach realisiert werden sollten. Die Effizienz spielte dabei nur
eine Nebenrolle. Deshalb gibt es noch viel Potenzial zur Verbesserung der
Effizienz.
Zur Steigerung der Effizienz des Systems können quantitative Untersuchun-
gen durchgeführt werden. Dabei kann die verteilte Laufzeitumgebung so in-
strumentiert werden, dass wichtige Performanz-Daten bei der verteilten Aus-
führung der Anwendungen erhoben werden. Dazu zählen z.B., wie oft ent-
fernte Methodenaufrufe stattfinden, wie viele Bytes an Daten dabei verschickt
werden, wie viele Handles für entfernte Objekte erzeugt werden etc. Somit
können Schwächen und Bottlenecks in der aktuellen Realisierung aufgedeckt
und verbessert werden.
Der Transformator ist zurzeit noch nicht in der Lage, alle Java-Anwendungen
zu transformieren. So können Arrays im Programm noch nicht behandelt
werden. Es existiert ein Entwurf zum Umgang mit Arrays. Dieser wurde aber
zur Evaluationszeit nicht erprobt. Da KaRMI zur entfernten Kommunikation
verwendet wird, wirken sich die dort enthaltenen Fehler auch auf JScatter aus.
184
8. Evaluation
Inhalt
8.1. Vorstellung der Anwendungen und Benchmarks - Vorberei-
tung zur Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . 187
8.1.1. Ein Anwendungsgebiet: diskrete ereignisbasierte Simu-
lation............................... 187
8.1.2. Untersuchte Simulations- und Benchmark-Programme 190
8.1.3. Eingesetzte Hardware und Software . . . . . . . . . . . . 192
8.2. Empirische Untersuchung der stagewise-immutable Ei-
genschaften . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
8.2.1. Analyseergebnisse der Anwendungen . . . . . . . . . . . 196
8.2.2. Analyseergebnisse der Anwendungen zusammen mit
ihren transitiven Hüllen . . . . . . . . . . . . . . . . . . . . 199
8.3. Empirische Untersuchungen zur Performanz der verteilten
Anwendungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
8.3.1. Grocery-Simulation . . . . . . . . . . . . . . . . . . . . . . 203
8.3.2. Multithreaded Raytracer (mtrt) aus dem SPECjvm98 . . 212
8.3.3. Das Traveling Salesperson-Problem (TSP) . . . . . . . . . . 226
8.4. Die Erkenntnisse . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
185
KAPITEL 8. EVALUATION
Dieses Kapitel umfasst die Evaluation meines Konzepts zur automatischen
Verteilung mehrsträngiger Programme. So wird die Güte der Verteilung un-
ter Einsatz von Verteilungsplänen mit geschickten Strategien anhand einiger
Anwendungsprogramme evaluiert. Zunächst werden die verwendeten An-
wendungen bzw. Benchmarks vorgestellt. Diskrete ereignisbasierte Simula-
tionen sind besonders geeignet für verteilte Berechnungen. Hier werde ich
einen kleinen Überblick über dieses Anwendungsgebiet geben und seine Eig-
nung zur Verteilung zeigen. Außerdem wird die bei der Untersuchung einge-
setzte Hard- und Software vorgestellt.
Im Abschnitt 6.4.1 wurde ein neues Analyseverfahren vorgestellt, das entwor-
fen wurde, um die stagewise-immutable-Eigenschaft der zu erzeugenden Ob-
jekte zu berechnen. Solche Objekte können zur Laufzeit repliziert werden, da-
mit die Lesezugriffe nach der Replikation nur lokal stattfinden. Im folgenden
Abschnitt werde ich die Messergebnisse zu dieser Eigenschaft der verwende-
ten Anwendungsprogramme vorstellen. Der folgende Abschnitt beschäftigt
sich dann mit der empirischen Untersuchung des Konzepts. Hierbei werden
verschiedene Aspekte zur Performanz, Kommunikation sowie zum Speicher-
verbrauch evaluiert. Besonders wird die Verteilungsgüte zur Geschwindig-
keitssteigerung unter Einsatz der Verteilungspläne mit verschiedenen Strate-
gien untersucht, u.a. eine Strategie zur stagewise-immutable Eigenschaft sowie
die Auswirkung auf die Verteilung bei Anwendung hybrider Analyse.
Die Laufzeitmessung wurde sowohl auf einem normalen Pool-Cluster, der
aus Standard-PCs (Pentium 4 mit 1.7GHz) mit Ethernet-100MBit-Verbindung
besteht, als auch auf einem speziellen Cluster aus unserem parallelen Rechen-
zentrum PC2durchgeführt. Dieser Arminius-Cluster umfasst 200 Rechen-
Knoten (Dual-Systeme mit INTEL Xeon 3.2GHz), die über Ethernet-GBit-
Verbindung und auch über ein schnelleres Netzwerk - Infiniband HCA - kom-
munizieren.
Aus dieser empirischen Untersuchung können viele Erfahrungen zur au-
tomatischen Verteilung und Erkenntnisse über die Vor- und Nachteile der
Verteilungsstrategien für bestimmte Fälle gewonnen werden. Somit können
Beurteilungen über die eingesetzten Strategien und allgemein über das Kon-
zept gemacht werden. Das Ganze zusammen mit einigen Erweiterungsansät-
zen wird abschließend präsentiert.
186
8.1. VORSTELLUNG DER ANWENDUNGEN UND BENCHMARKS -
VORBEREITUNG ZUR EVALUATION
8.1. Vorstellung der Anwendungen und Benchmarks -
Vorbereitung zur Evaluation
Zur Evaluation des gesamten Konzepts habe ich einige Anwendungspro-
gramme und Benchmarks als Untersuchungsgegenstand ausgesucht. Diskre-
te ereignisbasierte Simulation scheint für meinen automatischen Verteilungs-
ansatz am besten geeignet zu sein. Sie besitzen passende Berechnungsstruktu-
ren, wobei die Abarbeitung der Ereignisse aus einem Ausschnitt der Simulati-
on viel Rechenaufwand erfordert, aber wenig Kommunikation zwischen den
beteiligten Simulationsobjekten benötigt. In diesem Abschnitt werde ich einen
kleinen Überblick über das Anwendungsgebiet der diskreten ereignisbasier-
ten Simulation geben. Mit dieser kleinen Einführung können Programm- und
Verteilungseigenschaften eines Untersuchungsgegenstands, also einer mittel-
großen diskreten ereignisbasierten Simulation, einfacher verstanden werden.
Anschließend werden einige Charakteristiken der untersuchten Programme
beschrieben, die direkte Auswirkung auf die Analyse- und infolge dessen auf
die Verteilungsergebnisse haben. Außerdem werden Testbedingungen vorge-
stellt, z.B. mit welcher Software und Hardware sowie unter welchen Netz-
werkinfrastrukturen die Anwendungen evaluiert werden.
8.1.1. Ein Anwendungsgebiet: diskrete ereignisbasierte
Simulation
Java findet immer mehr Anwendung in der Welt des High-Performance Compu-
ting and Communications. Die High-Performance Anwendungen aus HP-Fortran
oder C bzw. C++ unter Verwendung der message passing-Technik werden nach
und nach in Java übertragen. Typische Anwendungen hierfür stammen z.B.
aus dem Bereich der geophysikalischen Verfahren und der Finanzmathema-
tik, numerischen Berechnungen, Raytracer und Renderer. Man kann sie als
reguläre Anwendungen klassifizieren. Eine andere Art von Anwendungen,
die zum Teil nicht ganz regulär sind und meist als Benchmarks verwendet
werden, sind klassische Probleme wie TSP (Traveling Salesperson Problem),
SOR (Successive Overrelaxation), ASP (All-pairs Shortest Paths), IDA(Iterative
Deepening A) oder einige Suchstrategien eingesetzt im Data Mining-Bereich.
Ein anderes Gebiet, wo Java zur Implementierung von parallelen Berech-
nungen eingesetzt wird, ist die wissenschaftliche und technische Simulation
und Modellierung. Die Probleme auf diesem Gebiet lassen sich in vier Ty-
187
KAPITEL 8. EVALUATION
pen klassifizieren: Datenparallelität, funktionale Parallelität, Objektparalleli-
tät und Metaprobleme.
Klassifizierung
Realistische irreguläre OO-Anwendungen finden sich u.a. im Bereich der Mo-
dellierung und Simulation, vor allem bei parallelen und verteilten diskreten
ereignisbasierten Simulationen. Abbildung 8.1 zeigt eine Einordnung dieser
Art der Simulation. Sie gehört zur Kategorie „funktionale Parallelität“.

 



Abbildung 8.1.: Klassifikation verschiedener Arten von Simulationen
Bei der diskreten Simulation beschränkt man sich auf abhängige Größen, die
nur diskrete Werte annehmen. Diese Werte verändern sich sprunghaft in ei-
nem bestimmten Intervall im Zeitablauf. Es gibt hierbei keine Zwischengrö-
ßen. Beispiele für solche diskreten Größen sind die Anzahl von Kassen in ei-
nem Geschäft oder der Zustand einer Ampel an einer Kreuzung. Dies ist der
Unterschied zur kontinuierlichen Simulation.
Diskrete ereignisbasierte Simulation
Unter Simulation eines zeitdiskreten dynamischen Systems kann man das Be-
rechnen der Ausgabedaten aus den vorgegebenen Eingabedaten durch das
fortlaufende Verfolgen der Übergänge verstehen. Eine diskrete ereignisba-
sierte Simulation ist die Simulation eines zeitdiskreten dynamischen Systems
durch Abarbeiten von Ereignissen in der Reihenfolge ihres Eintreffens. Die
188
8.1. VORSTELLUNG DER ANWENDUNGEN UND BENCHMARKS -
VORBEREITUNG ZUR EVALUATION
Abarbeitung eines Ereignisses bedeutet, dass der Zustand des Systems durch
Systemübergänge geändert wird und die Nachfolgeereignisse und ihre Zeiten
zum Eintreffen erzeugt werden.
Ein solches System enthält sowohl statische als auch dynamische Strukturen.
Das bedeutet, dass eine Anordnung von Objekten - also Simulationsobjek-
te, nicht Java-Objekte - existiert. Diese Objekte beeinflussen sich gegenseitig
und der Zustand des Systems wird durch Objekte, Attribute und Aktionen zu
einer gegebenen Zeit bestimmt. Damit sich man diese Abstraktion einfacher
vorstellen kann, wird im Folgenden ein kleines Beispiel derartiger Simulatio-
nen gegeben.
Ein Beispiel
Um die Wartezeit an einer Supermarktkasse zu untersuchen, betrachtet man
den Ausschnitt „Kassenbereich“ des Supermarktes. Hierbei handelt es sich
um ein zeitdiskretes dynamisches System. Es ändert sich nur zu den Zeit-
punkten, zu denen ein Kunde den Kassenbereich zum Bezahlen betritt und
zu denen ein Kunde den Kassenbereich verläßt, nachdem er bezahlt hat.
Die Komponenten des System sind „Kasse“ und „Kunde“, die Komponenten-
daten sind: geöffnet oder geschlossen, Kassiererin bzw. Kassierer schnell oder
langsam, Länge der Schlange für die Kasse; Eintrittszeitpunkt, Waren im Wa-
renkorb, Kassenauswahlstrategie, Austrittszeitpunkt der Kunden. Die Einga-
ben für die Supermarktkasse sind Kunden, die bezahlen wollen. Ausgaben
sind die Kunden, die bezahlt haben. Aus diesen Ein- und Ausgabedaten kann
man interessante Zusammenhänge untersuchen und ermitteln, wie beispiels-
weise die durchschnittliche oder maximale Wartezeit der Kunden oder die
durchschnittliche oder maximale Länge der Warteschlangen.
Für dieses Beispiel kann man das Ereignis „ein Kunde stellt sich an Kasse i
an“ betrachten, das wie folgt definiert ist:
Im neuen Systemzustand ist die Warteschlange der Kasse i um Eins ver-
längert.
Es gibt kein Folgeereignis.
Ein anderes Ereignis „ein Kunde wird an Kasse i bedient“ kann wie folgt
definiert werden:
189
KAPITEL 8. EVALUATION
Im neuen Systemzustand ist die Warteschlange der Kasse i um Eins ver-
kürzt.
Das Folgeereignis ist definiert als „ein Kunde verläßt den Kassenbe-
reich“ zu der Zeit „aktuelle Zeit plus Anzahl der Artikel im Warenkorb
des Kunden mal fünf Sekunden“.
Zum Beginn der Simulation wird das Ereignis „ein Kunde kommt in den
Kassenbereich“ als Eingabe abgearbeitet. Dieses Ereignis wird dann wie folgt
definiert:
Der Systemzustand bleibt gleich.
Als Folgeereignis wird durch die Kassenauswahlstrategie des Kunden
die Kasse i ausgewählt. Dann wird das Folgeereignis „ein Kunde stellt
sich an Kasse i an“ zur Zeit „eine Sekunde später“ erzeugt.
Solche Art der Simulation kann eingesetzt werden, um quantitative Aussa-
gen über das Verhaltens eines Ausschnitts der realen Welt zu machen. Es gibt
aber unzählig viele solcher Ausschnitte. Viele bekannte und sinnvolle An-
wendungen dafür finden sich in der Betriebswirtschaftslehre, insbesondere in
der Produktionswirtschaft, Verkehrsteuerung, im Computerbereich wie Pro-
zessnetzwerke, Spiele usw. Dadurch bietet sich eine Vielzahl von irregulären
OO-Anwendungen, in denen viel Rechenleistung in möglichst kurzer Zeit be-
nötigt wird. Sie eignen sich daher sehr gut für das verteilte Rechnen.
8.1.2. Untersuchte Simulations- und Benchmark-Programme
Für die empirische Untersuchung zur automatischen Verteilung wurden
drei Anwendungsprogramme aus drei verschiedenen Kategorien ausge-
sucht: Eine mittelgroße diskrete ereignisbasierte Simulation eines Gemüsela-
dens (grocery), das multithreaded Raytracer-Programm aus dem SPECjvm98
mtrt [Cor] und das klassische TSP-Programm von IBM. Zur Evaluation der
stagewise-immutable Eigenschaft werden außerdem zusätzlich eine von uns
selbst entwickelte diskrete ereignisbasierte Simulation (wash&wax) mit ihrem
Simulationsframework und einige bekannte Benchmark-Programme betrach-
tet. Das sind die n-Körper-Simulation von IBM (tnbody) und die drei Program-
me aus section3 des Multithreaded-JavaGrande-Benchmarks in der Version 1.0
(mt_moldyn, mt_montecarlo und mt_raytracer) [Cen].
190
8.1. VORSTELLUNG DER ANWENDUNGEN UND BENCHMARKS -
VORBEREITUNG ZUR EVALUATION
Klassen Methoden Bytecodes
grocery 9 55 1536
mtrt 34 326 9619
tsp 5 30 1070
tnbody 7 30 1412
wash&wax 19 110 1998
mt_moldyn 12 29 2688
mt_montecarlo 20 182 3032
mt_raytracer 21 103 2968
Tabelle 8.1.: Kenndaten der Anwendungsprogramme
Die Grocery-Anwendung simuliert das Einkaufverhalten der Kunden eines
Gemüseladens, den Laden selbst während der Einkaufzeit und den Kalorien-
verbrauch der Kunden. Die Komponenten des Systems sind der Laden, die
Produkte, das Produktlager, Regale mit einer bestimmten Anzahl der Pro-
dukte bzw. nachzufüllenden Produkten, Einkaufswagen, Kassen sowie die
Kunden mit ihren Einkaufslisten. Ereignisse während der Simulation sind u.a.
„ein Kunde kommt einen Einkaufswagen holen“, „ein Kunde kauft die Pro-
dukte nach seiner Einkaufsliste“, „ein Kunde kommt in den Kassenbereich“,
„Nachfüllen bestimmter Produkte für ein Regal“, „ein Kunde verbraucht Ka-
lorien aus seinen eingekauften Produkten“ usw. Das Ereignis „ein Kunde
kommt einen Einkaufswagen holen“ wird z.B. wie folgt definiert: Im neu-
en Systemzustand ist die Menge der verfügbaren Einkaufswagen um Eins
kleiner oder bleibt Null, falls kein Wagen mehr dort steht. Als Folgeereig-
nisse werden „ein Kunde verläßt den Wagenbereich“ und „ein Kunde war-
tet auf einen freien Wagen“ definiert. Die Simulationszeit wird entsprechend
mit einigen Details angepasst. Das System wurde so modelliert, dass viel Re-
chenaufwand bei einigen dieser Ereignisse im Vergleich zum Kommunikati-
onsaufwand gebraucht wird. Hierbei werden der Laden und die Kunden als
Threads modelliert. Alle anderen Komponenten sind normale Objekte.
Die Wash&Wax-Anwendung simuliert eine Autowaschanlage, wobei ver-
schiedene Autotypen wie Geländewagen, Stadtautos usw. je nach ihren
Nutzungseigenschaften in der Anlage gewaschen bzw. gewachst werden.
Der Berechnungs- und Kommunikationsaufwand dieser Simulation sowie
die Simulation insgesamt stellt ein ähnliches Muster dar, wie die Grocery-
Simulation. In Tabelle 8.1 sind einige Kenndaten der vorgestellten Anwen-
dungen und der Benchmark-Programme angegeben.
191
KAPITEL 8. EVALUATION
Beim mtrt ist ein Unterpaket dabei, das u.a. die Gültigkeitsprüfung der ge-
renderten Daten ermöglicht. Durch die Hinzunahme dieses Unterpakets wer-
den die zugehörigen Kenndaten etwas vergrößert, nämlich die Anzahl der
Methoden und somit der Bytecode-Instruktionen. Es wurde mit mtrt zu-
sammen als eine Einheit analysiert und verteilt. Die Implementierung des
TSP-Programms von IBM basiert auf dem Lösungsansatz nach dem Branch-
and-Bound-Verfahren. Jeder Worker-Thread besitzt eine eigene WorkQueue,
welche die zu evaluierenden Routen enthält. Wenn die lokale Queue kei-
ne Arbeitspakete mehr enthält, sucht der zugehörige Worker in den ande-
ren Queues nach Arbeit und nimmt ggf. diese den anderen Workern ab. Im
folgenden Abschnitt über die empirischen Untersuchung werde ich auf die
einzelnen Eigenschaften sowie die dort verwendeten Implementierungstech-
niken detaillierter eingehen, welche auch das jeweilige Laufzeitverhalten der
untersuchten Programme beeinflussen.
Die Anwendungen und Benchmark-Programme wurden zusammen mit ihrer
transitiven Hülle [Wes04] nach Verteilungseigenschaften analysiert. Das be-
deutet, dass alle Verwendungen der im Programm zu erzeugenden Objekte
nicht nur innerhalb der Anwendung selbst, sondern auch in den aufgeru-
fenen Methoden der Java-Standard-Bibliothek betrachtet werden. Z.B. wur-
den auch benutzte Methoden der Klasse java.util.HashMap analysiert,
die zum Speichern der Objektreferenzen des Anwendungsprogramms ver-
wendet wurden. So wird gewährleistet, dass alle möglichen Seiteneffekte auf
die entstehenden Objekte berücksichtigt werden.
8.1.3. Eingesetzte Hardware und Software
Es wurde die Standard JDK von Sun in der Version 1.4.2_07 unter Redhat
Enterprise Linux WS Release 3 verwendet, um die untersuchten Programme
verteilt ausführen zu lassen. Dem JScatter-System (siehe Kapitel 7) und der
Analyseumgebung [Thi01], die zur Programmanalyse nach Verteilungseigen-
schaften und zur Transformation sowie zur verteilten Ausführung der An-
wendungen zum Einsatz kommen, liegt auch diese JDK-Version zugrunde.
Für die darunter liegende Kommunikation wurde die schnelle RMI von der
Universität Karlsruhe [HMP] in der Version 1.07i mit der JDK-Serialisierung
verwendet. Jedoch habe ich zur Bildung der transitiven Hülle des jeweiligen
Programms die Standard-Bibliothek der jdk1.3.1 benutzt. Sie ist vom Um-
fang her viel kleiner als die der jdk1.4.x, stellt aber alle erforderlichen Klassen
und Methoden für die zu analysierenden Anwendungsprogramme zur Ver-
192
8.1. VORSTELLUNG DER ANWENDUNGEN UND BENCHMARKS -
VORBEREITUNG ZUR EVALUATION
fügung. Das bedeutet, dass ein untersuchtes Programm zusammen mit der
gebildeten Hülle als „komplett“ darstellt wird. Es kann dann mit seiner Hülle
ausgeführt werden. Eine Anwendung zusammen mit ihrer Hülle ist auch der
Ausgangspunkt der stagewise-immutable Analyse, wobei für die Objekterzeu-
gungsstellen im Anwendungsprogramm alle ihre Verwendungen inklusive
derjenigen in der Standard-Bibliothek nach Lese- und Schreibzugriffen un-
tersucht werden. Im Allgemeinen bleibt die Ausführungssemantik des Pro-
gramms bei Verwendung der jdk1.3.1 erhalten, somit auch die Analyseergeb-
nisse.
Die Verteilung der Anwendungen fand zunächst auf einem PC-Cluster statt,
dessen beteiligte Rechner über Standard-Ethernet 100MBit gemeinsam an ei-
nem Switch angeschlossen sind und die zusammen zu einem Subnetz ge-
hören. Mit dieser Art der Vernetzung findet die Kommunikation von einem
Rechner zum anderen direkt statt und durch die Subnetz-Infrastruktur wer-
den die Datenpakete nur innerhalb dieses Subnetzes verschickt. Außerdem
wurde der Cluster so konfiguriert, dass der Datenverkehr während der ver-
teilten Ausführung nicht von außen gestört wird. Jeder PC im Cluster ist mit
einem Pentium 4 1.7GHz und 1GB Hauptspeicher ausgerüstet. Das Betriebs-
system ist ebenfalls Redhat Enterprise Linux WS Release 3, der Kern hat die
Version 2.4.21-37.EL.
Bei den Performance-Messungen wurde außerdem eine spezielle
Mehrprozessor-Maschine verwendet, um die Verteilung der Anwendung auf
mehreren PCs mit der Ausführung auf solch einem Mehrprozessor-Rechner
zu vergleichen und zu evaluieren. Die hier genannte Maschine ist die
Sun-Fire der Firma Sun. Sie verfügt in der Hardware-Konfiguration über
8 Sparc-Prozessoren, jeweils mit 900MHz getaktet und besitzt insgesamt
8GB Hauptspeicher. Ein solcher Sparc-Prozessor ist bezüglich der Rechen-
leistung mit einem doppelt so schnell getakteten Pentium 4 vergleichbar,
also etwa 1.7GHz, wie die Cluster-PC. Die Sun-Fire läuft unter SunOS der
Version 5.9 und die dort verwendete JVM hat die JDK-Version 1.3.1_06,
welche die Abbildung der Java-Threads auf die Betriebssystem-Prozesse
unterstützt und somit die Verteilung dieser Prozesse auf die Prozessoren
mit dem gemeinsamen Hauptspeicher vom Betriebssystem aus automatisch
durchführt.
Das JScatter-System wurde außerdem in einem speziellen parallelen Rechner-
system unseres Rechenzentrums PC2an der Universität Paderborn [Pad] zur
verteilten Ausführung der untersuchten Programme eingesetzt. Dieses Sys-
tem - Arminius Fujitsu Siemens Computers - ist ein spezieller Cluster, der
193
KAPITEL 8. EVALUATION
200 Rechen-Knoten zur Berechnung und 8 Knoten zur Visualisierung um-
fasst. Die Konfiguration jedes Rechen-Knotens besteht aus Dual-INTEL Xe-
on 3.2 GHz EM64T mit 4 GByte (DDR-2) Hauptspeicher. Diese Knoten kön-
nen über ein Hochgeschwindigkeitsnetzwerk InfiniBand HCA PCI-e und auch
über Standard-Ethernet 1000MBit kommunizieren. Als Betriebssystem wird
64-bit Linux Redhat AS 4 mit dem Kern in der Version 2.6.9 verwendet. Auf
den Knoten ist eine JDK in der Version 1.5.0_06-b05 installiert, die speziell für
solche Mehrprozessor-Maschinen konstruiert wurde. Die Java-Threads hier-
bei werden auf die Betriebssystem-Prozesse direkt abgebildet, die dann auto-
matisch vom Betriebssystem auf die zwei Prozessoren verteilt werden.
8.2. Empirische Untersuchung der
stagewise-immutable Eigenschaften
Die erste Evaluation beschäftigt sich mit der stagewise-immutable Eigenschaft
der untersuchten Anwendungsprogramme. Als Analysekontext, also die Ein-
gabe für die Analyse, wurde jeweils eine Anwendung zusammen mit ihrer
transitiven Hülle übergeben. Dadurch wird gewährleistet, dass alle potenzi-
ellen Zugriffe und Seiteneffekte auf die entstehenden Objekte innerhalb der
Anwendung nicht nur im Anwendungsprogramm selbst, sondern auch in
den benutzten Methoden der verwendeten Bibliotheken berücksichtigt wer-
den. Die Analyse sowie die Transformation des ursprünglichen Programms
zu einer verteilten Version finden auf Bytecode-Ebene vor der Laufzeit statt.
Innerhalb des Anwendungsprogramms wurden zunächst alle Erzeugungs-
stellen identifiziert. Die Analyse untersucht dann alle Verwendungen zur
jeweiligen Erzeugungsstelle über den gesamten Analysekontext und ermit-
telt die Information über die stagewise-immutable Eigenschaft zu den Erzeu-
gungsstellen. Konzeptionell besagt die stagewise-immutable Eigenschaft, dass
ein Objekt ab einem bestimmten Zeitpunkt während des Programmablaufs
nur lesend zugegriffen wird. Bei der Evaluation wird die stagewise-immutable
Eigenschaft in zwei feinere Stufen unterteilt, und zwar in pure-immutable und
postponed-immutable. Wie im Abschnitt 6.4 definiert bedeutet pure-immutable,
dass ein Objekt nach seiner Initialisierungsphase - also nachdem eine zur
Erzeugung aufgerufene Konstruktormethode komplett abgearbeitet wurde
- bis zum Programmende nur lesend zugegriffen wird. Postponed-immutable
Objekte werden nach ihrer Initialisierung noch schreibend zugegriffen, wer-
den aber irgendwann in der Verwendungsphase bis zum Programmende
194
8.2. EMPIRISCHE UNTERSUCHUNG DER STAGEWISE-IMMUTABLE
EIGENSCHAFTEN
CF-Nodes C2M M2C M2M
grocery 15391 141 176 110
mtrt 27000 512 499 1053
tsp 15000 89 101 87
tnbody 15109 127 137 102
wash&wax 19371 50 113 106
mt_moldyn 15375 41 75 88
mt_montecarlo 16297 68 140 214
mt_raytracer 15511 56 113 116
Tabelle 8.2.: Einige Kenndaten über Ergebnisse der stagewise-immutable
Analyse
nur gelesen. pure-immutable Objekte können sofort nach der Aufrufstelle der
Konstruktormethode repliziert werden. Technisch ist das Finden solcher Re-
plikationspunkte viel einfacher. Außerdem braucht man dafür nur einmal
die Replikationsanweisung nach der Konstruktoraufrufstelle einzufügen. Für
postponed-immutable Objekte muss die Analyse geeignete Replikationsstellen
finden und Replikationscode wird, wenn nötig, an mehreren Stellen einge-
fügt.
Die Tabelle 8.2 zeigt zusätzlich einige Kenndaten über die Ergebnisse der
interprozeduralen Post-Dominator-Analyse. Die azyklischen Graphen, die
diese interprozeduralen Post-Dominator-Beziehungen repräsentieren, dienen
als Eingabe der stagewise-immutable Analyse. Ein solcher Graph enthält die
Kontrollfluss-Knoten aller Methoden der Anwendungs- und Bibliotheksklas-
sen, deren Anzahl in der ersten Spalte (CF-Nodes) angegeben ist. Die Kanten
sind die normalen Kontrollfluss-Kanten und zusätzlich diejenigen, die inter-
prozedurale Post-Dominator-Aufrufbeziehungen darstellen. Ihre Anzahl im
jeweiligen Programm ist in der Tabelle 8.2 unter den Namen C2M (Call-2-
Method), M2C (Method-2-Call) und M2M (Method-2-Method) zu finden. Zu
bemerken ist, dass eine Aufrufstelle bei der Analyse als ein eigenständiger
Kontrollflussknoten (Basisblock) modelliert wurde. Ein urprüngliches Basis-
block kann aber mehrere Aufrufstellen enthalten. Dadurch wird die Genauig-
keit der interprozeduralen Post-Dominator-Relation erhöht.
So durchlief die stagewise-immutable Analyse diesen Graphen, gestartet vom
jeweiligen Programm-Exit-Knoten, und berechnete für jede Erzeugungsstelle
die stagewise-immutable Eigenschaft. Aus den Kenndaten kann man erkennen,
dass die Anzahl der interprozeduralen Post-Dominator-Kanten sehr klein ist
gegenüber der Anzahl derjenigen, die die CF-Knoten verbinden. Dies be-
195
KAPITEL 8. EVALUATION
deutet, dass die stagewise-immutable Analyse die Überprüfung der stagewise-
immutable Eigenschaft sowie die Suche nach geeigneten Replikationsstellen
für jede Erzeugungsstelle sehr schnell durchführen kann. Die Analyse muss
im schlimmsten Fall alle dieser interprozeduralen Kanten betrachten, aber
nicht alle CF-Kanten der Methoden. Diese CF-Relation innerhalb einer Me-
thode wird ggf. bei Bedarf konstruiert und untersucht.
Im Folgenden werde ich Analyseergebnisse der stagewise-immutable Eigen-
schaft unter zwei verschiedenen Gesichtspunkten repräsentieren. Im ersten
Abschnitt werden nur stagewise-immutable Ergebnisse innerhalb der Anwen-
dungsprogramme dargestellt. Danach werden Ergebnisse für den gesamten
Analysekontext, also die Anwendung und ihre transitive Hülle, präsentiert.
8.2.1. Analyseergebnisse der Anwendungen
Die stagewise-immutable Analyse wurde auf die acht oben vorgestellten An-
wendungsprogramme angewandt. Als Eingabe für die Analyse wurde nicht
nur jeweilige Anwendung, sondern der Vollständigkeit wegen auch ihre ge-
bildete Hülle übergeben. Die hier dargestellten Ergebnisse sind statische Er-
gebnisse und geben die Anzahl der Erzeugungsstellen mit der jeweiligen Ei-
genschaft an, die ebenfalls vor der Laufzeit identifiziert wurden. Die Grafik in
Abbildung 8.2 stellt die Analyseergebnisse visuell dar. Die X-Achse repräsen-
tiert die einzelne Anwendung mit ihren Ergebnissen über die Erzeugungs-
stellen (NEW-Stelle), davon gefundene mutable und stagewise-immutable Stel-
len, die jeweils als Balken von links nach rechts innerhalb einer Anwendung
dargestellt werden. Auf der Y-Achse wird die Anzahl der Stellen aufgetra-
gen. Die unterstrichenen Zahlen präsentieren die gewonnenen Lesezugrif-
fe, die lokal durchgeführt würden, wenn entstehende Objekte aus allen als
stagewise-immutable identifizierten Erzeugungsstellen repliziert würden. Die
Anzahl der gewonnenen Lesezugriffe wurde ebenfalls statisch erkannt.
Die identifizierten NEW-Stellen umfassen alle Erzeugungsstellen innerhalb des
untersuchten Anwendungsprogramms. Die gewonnenen Lesezugriffe kön-
nen aber nicht nur in der Anwendung, sondern auch in den aufgerufenen
Methoden aus verwendeten Bibliotheken vorkommen. Da die Analyse ein
statisches Verfahren ist, wird eine in einer Schleife liegende Erzeugungsstelle
nur einfach gezählt.
Die ganz links stehenden Balken stellen die gesamte Anzahl der identifizier-
ten NEW-Stellen dar, die nur im Anwendungsprogramm vorkommen. Davon
196
8.2. EMPIRISCHE UNTERSUCHUNG DER STAGEWISE-IMMUTABLE
EIGENSCHAFTEN
als mutable bzw. als stagewise-immutable erkannte Erzeugungsstellen werden
als mittlere bzw. rechts daneben stehende Balken dargestellt. Diese stagewise-
immutable Stellen umfassen pure- und postponed-immutable Stellen. Ihre sepa-
raten Anteile werden in Abbildung 8.3 dargestellt.
       














 ! "!#
$ 
 
%&'( ))
Abbildung 8.2.: Analyseergebnisse zur stagewise-immutable Eigenschaft
       



*

+&,&-  & &- & & 
- 
-  
Abbildung 8.3.: Ergebnisse zur Pure- und Postponed-Immutable-
Eigenschaft
Bei mtrt können sehr viele (5721) aus vorhandenen Lesezugriffe lokal durch-
geführt werden, dank der 50 gefundenen stagewise-immutable NEW-Stellen, wo-
von 23 pure-immutable und 27 postponed-immutable sind. Wie die Erzeugungs-
stellen sind die Werte zu den gewonnenen Lesezugriffen sowie die immutable
1Das entspricht etwa 13,44% aller statisch gezählten Lesezugriffe.
197
KAPITEL 8. EVALUATION
Werte statische Zahlen. Das heißt, dass die Lesezugriffe der potentiellen Ver-
wendungsstellen von der Programmanalyse gefunden werden. Sie können
innerhalb einer Schleife auftreten und mehrfach ausgeführt werden oder aber
aufgrund bestimmter Laufzeitbedingungen nicht ausgeführt werden.
       
12
12
*12
12
12
12
-( & ! $ 
- 
-  
Abbildung 8.4.: Anteile der als stagewise- und pure-immutable erkannten
Stellen zu gesamten NEW-Stellen
Zum einfachen Vergleich werden die gefundenen Erzeugungsstellen der Ka-
tegorien pure-immutable,postponed-immutable sowie mutable in Abbildung 8.4
prozentual dargestellt. Im tsp-Programm konnten besonders viele postponed-
immutable Stellen erkannt werden. Das sind meistens StringBuffer-
Objekte (z.B. sb), die innerhalb von System.out.println(sb) verwendet
wurden und die Form sb=s1+s2; besitzen, wobei s1 und s2 selbst auch
String-Objekte sind. Da der Java-Übersetzer einen solchen Ausdruck in new
StringBuffer().append(s1).append(s2).toString() übersetzt,
bedeutet dies für die Analyse, dass das zu erzeugende StringBuffer-
Objekt nach dieser Aufrufkette, wobei das Objekt modifiziert wird, dann
postponed-immutable wird.
Da Objekte von Bibliothek-Klassen (noch) nicht verteilt werden, sondern
nur diejenigen der Anwendungsklassen, sind zurzeit nicht alle stagewise-
immutable Erzeugungen zur Verteilung nutzbar. Von solchen Fällen abgesehen
können aber durch die Analyseergebnisse viele zu erzeugende Objekte repli-
ziert werden, wie z.B. bei mtrt oder raytracer, wodurch viele aufwändige
entfernte Zugriffe lokal werden. Bei der empirischen Untersuchung zur Ver-
198
8.2. EMPIRISCHE UNTERSUCHUNG DER STAGEWISE-IMMUTABLE
EIGENSCHAFTEN
teilung ist zu sehen, dass hierdurch viel Kommunikation eingespart und die
gesamte Laufzeit damit auch verkürzt wurde.
8.2.2. Analyseergebnisse der Anwendungen zusammen mit ihren
transitiven Hüllen
Im obigen Abschnitt wurden nur Analyseergebnisse für NEW-Stellen gezeigt,
die innerhalb des betrachteten Anwendungsprogramms vorkommen. Es gibt
daneben aber auch sehr viele Objekte, die außerhalb der eigentlichen Anwen-
dung, aber innerhalb der verwendeten Methode in der transitiven Hülle er-
zeugt werden. Deshalb lohnt es sich, nach stagewise-immutable Eigenschaft sol-
cher in den Bibliotheks-Klassen erzeugter Objekte zu suchen. In diesem Ab-
schnitt wird bei jeder Anwendung ihr gesamter Analysekontext analysiert.
Die Balkendiagramme in Abbildung 8.5 sowie in Abbildung 8.6 mit gleicher
Bedeutung wie im letzten Abschnitt zeigen solche Analyseergebnisse.
.  .  .  .  .  .  .  . 








 

  
 !&&/0 "!#
$ 
 
%&'( ))
Abbildung 8.5.: Analyseergebnisse zur stagewise- und pure-immutable Ei-
genschaft der Anwendungen mit ihrer transitiven Hülle
Durch Hinzunahme der Erzeugungsstellen außerhalb der Anwendungen
wurden bei den meisten untersuchten Programmen insgesamt erheblich viel
mehr Erzeugungsstellen identifiziert. Der Grund dafür ist, dass die Vielzahl
neu hinzukommender Erzeugungsstellen aus Bibliotheken die wenigen in-
nerhalb der Anwendung komplett dominieren. Somit steigt die Anzahl der
gesamten NEW-Stellen um das 13fache (mtrt) bis 152fache (moldyn) an. Bei
199
KAPITEL 8. EVALUATION
.  .  .  .  .  .  .  . 

*




+&,&-  & &- & & &&/0
- 
-  
Abbildung 8.6.: Anteile der als stagewise- und pure-immutable erkannten
Stellen im Verhältnis zur Gesamtanzahl der NEW-Stellen
der Anwendungen und ihrer transitiver Hülle
der Betrachtung der Anwendung allein konnte man erkennen, dass nicht so-
viel pure-immutable Stellen gefunden werden. Mit der Hülle zusammen sind
die Balken für diese Eigenschaft (in Abbildung 8.6), und somit auch die Bal-
ken für stagewise-immutable (in Abbildung 8.5), bei allen Programmen deut-
lich höher. Auch die statisch gewonnenen Lesezugriffe wachsen bei allen
Analysekontexten gleichmäßig an, etwa um einen Faktor zwischen 22 und
193. So kann man daraus schließen, dass viele Erzeugungsstellen in den ver-
wendeten Bibliotheken unabhängig von der Anwendung pure-immutable sind.
Dies erkennt man am deutlichsten bei den drei Anwendungen aus dem Java-
Grande-Benchmark: Hierbei werden nach wie vor keine bzw. bei raytracer
nur eine NEW-Stelle mit der postponed-immutable Eigenschaft gefunden, denn
nach Definition ist die postponed-immutable Eigenschaft stark anwendungsab-
hängig. Die pure-immutable Kandidaten sind nur abhängig davon, welche ih-
rer Methoden benutzt werden, die potentiell Schreib- oder Lesezugriffe dar-
stellen. Diese Aussage läßt sich durch die prozentuale Darstellung der Ana-
lyseergebnisse bestätigen, die in Abbildung 8.7 zu sehen sind.
Bei der prozentualen Darstellung der Ergebnisse ist deutlich erkennbar, dass
bei allen Anwendungen etwa 40% der NEW-Stellen über den gesamten Analy-
sekontext die pure-immutable Eigenschaft besitzen. Die gefundenen postponed-
immutable Stellen sind im Vergleich dazu nicht viel, machen aber auch et-
wa 4% Anteil aus. Insgesamt können durch etwa 44% der NEW-Stellen bis
zu 15524 Lesezugriffe (etwa 6,97% aller statisch gezählten Lesezugriffe) nach
200
8.3. EMPIRISCHE UNTERSUCHUNGEN ZUR PERFORMANZ DER
VERTEILTEN ANWENDUNGEN
.  .  .  .  .  .  .  . 
12
12
*12
12
12
12
-( & !&&/0 $ 
- 
-  
Abbildung 8.7.: Anteile der stagewise- und pure-immutable Stellen über
den Analysekontext
der Replikation lokal werden, so dass entfernter Kommunikationsaufwand
erheblich reduziert werden kann.
Diese schönen Erkenntnisse für den gesamten Analysekontext können lei-
der noch nicht in vollem Umfang zur automatischen Verteilung genutzt wer-
den. Denn das JScatter-System ist technisch noch nicht so ausgereift, dass die
Anwendung und ihre gesamte transitive Hülle automatisch komplett fehler-
frei transformiert werden. Für die empirische Untersuchung zur Performanz
der verteilten Ausführung wurden nur die Anwendungen allein transfor-
miert und deren Objekte automatisch verteilt. Objekte, die in der Standard-
Bibliothek erzeugt werden, wurden nicht verteilt. Sie werden in ihrer voll-
ständigen Größe über das Netzwerk verschickt. Trotz dieses Nachteils kann
die Immutabilität, selbst wenn sie nur in der Anwendung ausgenutzt wird,
schon sehr viel entfernte Kommunikationskosten einsparen, wie wir im fol-
genden Abschnitt erfahren.
8.3. Empirische Untersuchungen zur Performanz der
verteilten Anwendungen
Zur empirischen Untersuchung meines Konzepts zur automatischen Vertei-
lung mehrsträngiger Java-Anwendungen habe ich drei verschiedene Anwen-
201
KAPITEL 8. EVALUATION
dungsprogramme verwendet, die zu drei unterschiedlichen Kategorien ge-
hören. Das sind die Grocery-Simulation, der multithreaded raytracer (mtrt) aus
dem SPECjvm98-Benchmark und das klassische Programm TSP von IBM. Die
erste Anwendung repräsentiert eine diskrete ereignisbasierte Simulation, wo-
bei die Threads viel zu berechnen haben, jedoch nicht so viel miteinander
kommunizieren. Das mtrt-Programm besitzt eine reguläre Berechnungsstruk-
tur, wobei die Worker-Threads nach der Initialisierung der Daten ihre eigenen
Berechnungen durchführen und das Ergebnis jedesmal aktualisieren. Im Ge-
gensatz dazu stellt TSP ein Muster dar, bei dem wenige Berechnungen aber
viel Kommunikationen erforderlich sind. Außerdem erschwert die verwen-
dete Load-Balancierungstechnik der Problemlösung die statische Analyse, so
dass prinzipiell keine eindeutige Objektzuordnung zu den Worker-Threads
gefunden werden konnte.
Für jede Art der Messungen, z.B. Messung der verteilten Ausführung auf 2
Rechnern, wurden 10 Testläufe hintereinander durchgeführt. Die Messergeb-
nisse der ersten beiden Testläufe wurden ignoriert, da hierbei Unregelmä-
ßigkeiten wie beispielsweise das allererste Laden der Klassen der verteilten
Laufzeitumgebung über NFS oder Netzwerkverkehr auftreten können. Von
den restlichen Ergebnissen wurden dann die Mittelwerte gebildet und ausge-
wertet.
Im Allgemeinen wurden die Anwendungsprogramme mit Blick auf die Ge-
schwindigkeitssteigerung unter Einsatz der Verteilungspläne mit verschie-
denen Platzierungsstrategien evaluiert. So wurde die Laufzeit der verteil-
ten Ausführung auf einer bestimmten Anzahl der beteiligten Rechner ge-
genüber dem sequentiellen Ablauf auf einem Rechner und der Ausführung
auf der Mehrprozessor-Maschine (fireball) verglichen. Die Verteilungsstra-
tegien in einem Verteilungsplan wurden wie folgt angewandt: „Die entste-
henden Threads werden einfach zyklisch den verfügbaren JVMs zugeord-
net. Nicht-Thread-Objekte werden zuerst nach ihren Lokalitätseigenschaften
verteilt, d.h. falls die isPeer,isProxy- oder isThreadLocal-Eigenschaft
erfüllt ist, können sie einfach im lokalen Adressraum erzeugt werden.
Sonst werden sie zu der Aktivität zugeordnet, die die meiste Workload
auf ihnen verrichtet und bei der wenig Kommunikationskosten entstehen
(fTHREADWORKCOMM).“ Zusätzlich wurde der Ansatz zur hybriden Analy-
se, die die dynamische Programmeigenschaft zur Workload-Abschätzung in-
nerhalb der ODFfTHREADWORKCOMM berechnet, für die Anwendung mtrt
erprobt. Um die Auswirkung der Immutabilitätseigenschaft auf die Vertei-
lungsgüte zu untersuchen, habe ich bei der Grocery-Simulation separat Mes-
202
8.3. EMPIRISCHE UNTERSUCHUNGEN ZUR PERFORMANZ DER
VERTEILTEN ANWENDUNGEN
sungen mit und ohne Replikation durchgeführt, bei den beiden anderen aber
mit dieser evaluiert. Bei allen drei Anwendungen wurde der statische Anteil
einer Klasse auf dem Rechner erzeugt, auf dem der allererste Zugriff darauf
stattfand.
Wie im Abschnitt 8.1.3 erläutert, entspricht die Rechenleistung eines mit
900MHz getakteten Sparc-Prozessors (Fireball-Maschine) theoretisch der
Leistung eines Pentiums 4 mit doppelter Taktfrequenz. Praktisch zeigt sich
die erreichte Performanz eines solchen Sparc-Prozessors aus den Messungen
etwa 2,66 fach langsamer als die eines Pentiums 4 mit 1.7GHz. Der Vorteil
einer solchen Mehrprozessor-Maschine liegt nur darin, dass der Kommuni-
kationsaufwand zwischen den einzelnen Prozessoren fast Null ist, während
die verteilte Ausführung auf einem PC-Cluster auch Zeit zur Kommunikation
benötigt. Wenn nur der Rechenaufwand ohne Kommunikation der verteil-
ten Ausführung auf einem Cluster mit dem Rechenaufwand auf der Fireball-
Maschine verglichen wird, dann wird mehr Performanz (auch etwa 2,4 fach)
beim Cluster erreicht. Mit Kommunikation kann eine Anwendung auf der
Mehrprozessor-Maschine schneller ausgeführt werden.
Um zu zeigen, dass das JScatter-System auch einfach auf unterschiedlichen
Cluster-Architekturen eingesetzt werden kann, wurden zusätzliche Messun-
gen für die Grocery-Simulation und das Benchmarkprogramm mtrt auf dem
speziellen Arminius-Cluster des PC2durchgeführt. Als Verteilungsstrategien
wurden dafür diejenigen genommen, die bei den vorherigen Messungen auf
dem normalen PC-Cluster die besten Verteilungsergebnisse lieferten. Der Ar-
nimius verfügt über zwei unterschiedliche Netzwerk-Architekturen: Das sind
die Standard-Ethernet-Verbindung mit 1000MBit und die Hochgeschwindig-
keitsverbindung über das InfiniBand [Ass]. Bei der empirischen Untersu-
chung fand die Kommunikation über die Ethernet-Gbit-Verbindung statt.
Im Folgenden wird die Evaluation nach den drei vorgestellten Anwendungs-
programmen jeweils auch mit zusätzlichen Evaluationsaspekten beschrieben.
8.3.1. Grocery-Simulation
Performanz-Messungen auf einem normalen PC-Cluster
Diese Simulation erzeugt zur Laufzeit insgesamt 16 Threads, davon sind
15 die Kunden und einer der Laden selbst. Um aber zunächst den Unter-
schied des Laufzeit- und Kommunikationsaufwands zwischen der origina-
203
KAPITEL 8. EVALUATION
len, sequentiellen Version und der transformierten, verteilten Version des Pro-
gramms zu untersuchen, wurden die beiden Versionen auf demselben Rech-
ner ausgeführt. An den Messergebnissen sollte erkannt werden, welche zu-
sätzliche Belastungsfaktoren auf die tatsächliche Ausführungszeit des Pro-
gramms entstehen. Das sind der Delegationsmechanismus an Handle- und
an InstanceManager, die Zugriffe auf Klassenanteile eines Objekts, wel-
che auch zu remote geworden sind, und Delegationen an erzeugten Stubs und
Skeleton.
Die Messungen ergeben, dass die verteilte Version auf einem Rechner um et-
wa 9% langsamer als die Originalversion ist. Dies entspricht einem „speed-
down“ von etwa 0,917. Der Kommunikationsaufwand ist etwa um das
11fache erhöht. Diese Verlangsamung ist auch zu erwarten, denn ein norma-
ler Methodenaufruf im sequentiellen Fall wird in der verteilten Version zu ei-
nem remote Aufruf. Bis zum eigentlichen Aufruf der Methode muss zunächst
die ganze Delegationskette des JScatter-Systems verarbeitet werden: als der
Aufruf des Handles, des InstanceManagers, Abfragen an die verteilte Lauf-
zeitumgebung usw. Außerdem kommen noch Aufrufe der von karmi gene-
rierten Stubs und Skeleton hinzu. Für Parameter und Rückgabewerte müssen
die Werte noch serialisiert bzw. deserialisiert werden. Bei der tatsächlichen
verteilten Ausführung auf mehreren Rechnern wird der Kommunikations-
aufwand nochmals durch das eigentliche Versenden der Daten über das Netz
und die Netzlatenz gesteigert.
Zur verteilten Ausführung wurden drei Verteilungspläne zur automatischen
Verteilung eingesetzt: Der erste umfasst eine einfache zyklische Verteilung al-
ler Objekte, der zweite enthält die oben beschriebenen Strategien, aber ohne
Replikation der pure-immutable und postponed-immutable Objekte und der drit-
te mit Replikation. Bei der zyklischen Verteilung wurden die erzeugten Ob-
jekte im Ablauf des Programms einfach zyklisch auf die verfügbaren Rechner
verteilt, ohne dabei Threads und Nicht-Thread-Objekte zu unterscheiden und
auch ohne komplexe Strategien anzuwenden. Alle Objekte wurden dabei als
remote Objekte behandelt. Für die Simulation wurden Messungen der Vertei-
lung auf 2 bis 9 Rechnern durchgeführt. Abbildung 8.8 zeigt die Ergebnis-
se zur Ausführungsdauer der Simulation, die mit verschiedenen Verteilungs-
strategien ausgeführt wurde.
Auf der X-Achse ist die Anzahl der beteiligten Rechner aufgetragen, wobei
auf einem Rechner (sequentiell) und auf der Mehrprozssor-Maschine (fire-
ball) die originale, untransformierte Version des Programms ausgeführt wur-
de. Die Y-Achse präsentiert die gemessenen Laufzeitwerte in Sekunden. Wie
204
8.3. EMPIRISCHE UNTERSUCHUNGEN ZUR PERFORMANZ DER
VERTEILTEN ANWENDUNGEN
3  * 4 )

*




% &&5  (6
7 6&8(69
&5 &85-9
7 6&8:5-9
&5-& & 
7 6&8:5-: 9
+(&&;
' )(&89
Abbildung 8.8.: Verteilungsergebnisse der Grocery-Simulation mit ver-
schiedenen Verteilungsstrategien
bereits erläutert, wurden hier drei verschiedene Verteilungspläne angewandt,
deren Messergebnisse in drei nebeneinander stehenden Balken mit unter-
schiedlichen Farben dargestellt werden. Von links nach rechts innerhalb einer
Gruppe präsentieren die ganz links stehenden Balken Messergebnisse unter
Verwendung des Verteilungsplans mit einfacher zyklischer Verteilungsstra-
tegie, die mittleren Balken repräsentieren die oben beschriebenen Strategien
und die rechten Balken den Plan mit Replikation von stagewise-immutable Ob-
jekten. Die inneren schmalen Balken, die jeweils innerhalb der Laufzeitbalken
vorkommen, repräsentieren die anteiligen Kommunikationskosten.
Durch die zyklische Verteilung werden die Objekte einfach der Reihe des
Programmablaufs nach auf die beteiligten Rechner verteilt. Das verursacht
viel entfernte Kommunikation, so dass die Simulation in bestimmten Fällen,
z.B. bei Verteilung auf 2 oder 3 Rechner, noch langsamer als die sequentielle
ablief. Mit mehr als 7 Rechnern verbesserte sich auch die Laufzeit nicht mehr,
das Programm lief sogar noch etwas langsamer. Dieses Phänomen läßt sich
auch deutlich am verursachten Kommunikationsaufwand erkennen.
Beim Verteilungsplan mit intelligenten Strategien wird die Performanz spür-
bar verbessert. Durch Analyseergebnisse konnte die Lokalität vieler Objekte
erkannt werden, wie z.B. die Proxy-Eigenschaft und Thread-Lokalität. Au-
ßerdem entstand dadurch eine bessere Zuordnung der Objekte zu den Ak-
tivitäten anhand der maximalen Workload und minimaler Kommunikation.
205
KAPITEL 8. EVALUATION
Somit wurden Objekte zusammen mit ihrer Aktivität zusammen platziert,
so dass die durch die Transformation „entfernt“ werdenden Methodenaufru-
fe nur noch innerhalb eines Adressraums stattfinden. Bei manchen Objekten
mit isProxy- und isThreadLocal- Eigenschaft wurden ihre Methodenauf-
rufe sogar direkt durchgeführt. Durch all diese Verbesserungen können viel
entfernte Kommunikationen eingespart werden, was zusätzlich zu einer Ge-
schwindigkeitssteigerung führt.
3  * 4 )
14
1
1
14
*1
 
(6
)
&5 &85-9
&5-& & 
+(&&;
Abbildung 8.9.: Speedup für Grocery-Simulation
Die Performanzsteigerung erkennt man ganz deutlich an den sinkenden in-
neren Balken für Kommunikationskosten im Vergleich zu denjenigen bei der
zyklischen Verteilung. Dieser eingesparte Anteil an entfernter Kommunika-
tion ist auch etwa gleich dem Gewinn an gesamter Ausführungsdauer des
Programms, die an den mitteren Balken zu erkennen ist: Schon bei 2 Rech-
nern lief das Programm schneller als sequentiell und ab 4 Rechnern fällt die
gesamte Laufzeit weiter bis unter 40% des sequentiellen Ablaufs. Durch Re-
plikation der als stagewise-immutable erkannten Objekte, in der Simulation 4
von insgesamt 45 NEW-Stellen, konnte zusätzlich noch bis zu etwa 9.9% an Ge-
schwindigkeitsgewinn (die rechts stehenden Balken) erzielt werden. Durch
die lokalisierten Lesezugriffe auf die replizierten Objekte konnte einiges an
entfernter Kommunikation eingespart werden, was auch zu den sinkenden
(inneren) Balken führte. Somit ist der ersparte Anteil an entfernten Kommu-
nikationen auch der Zusatzgewinnanteil an der gesamten Laufzeit des Pro-
gramms. Wie man auch an der Grafik erkennt, bleiben die Kommunikations-
kosten unter Einsatz des Verteilungsplans mit Replikation ab 4 Rechnern fast
unverändert, nur die gesamte Laufzeit wird mit steigender Anzahl der be-
teiligten Rechner verbessert. Das heißt, dass die Parallelität zur Berechnung
206
8.3. EMPIRISCHE UNTERSUCHUNGEN ZUR PERFORMANZ DER
VERTEILTEN ANWENDUNGEN
der Kunden-Threads bei gleichbleibenden Kommunikationskosten besser ge-
nutzt wird. Abbildung 8.9 zeigt uns die Speedup-Werte der Simulation.
Trotz Einsatz der 9 Rechner erreichte die Performanz leider noch nicht ganz
die der 8-Prozessor-Maschine. Das läßt sich dadurch erklären, dass der Kom-
munikationsaufwand auf dem PC-Cluster höher ist. Denn wenn nur der Re-
chenanteil, also ohne Kommunikation, der gesamten Laufzeit betrachtet wür-
de, würde die Performanz beim Einsatz von 6 Rechnern bereits die Per-
formanz der Fireball-Maschine erreichen. Diese steigt dann mit zunehmen-
der Anzahl der beteiligten Rechner an. Der Vorteil des Einsatzes eines PC-
Clusters ist außerdem natürlich die Skalierbarkeit und die Anschaffungskos-
ten gegenüber einem solchen speziellen Rechner. Außerdem wird bei einem
solchen Cluster nicht nur die Anzahl der Prozessoren, sondern auch die ver-
fügbare Speicherkapazität mitskaliert. Im Gegensatz dazu sind diese Faktoren
bei Mehrprozessor-Maschinen beschränkt.
Neben der Evaluation nach Performanz wurde auch der Aspekt Startup des
verteilten Programms gegenüber der sequentiellen Version sowie auf der
fireball-Maschine untersucht. Die Startup-Differenz ist die benötigte Zeit vom
Programmstart bis zum Zeitpunkt, an dem der erste arbeitende Thread be-
ginnt zu starten. Während dieser Zeit bei der Simulation werden alle In-
itialisierungsschritte durchgeführt, beispielsweise Erzeugung der für die Si-
mulation relevanten Objekte, Initialisierung der statischen Werte, Konstan-
ten usw. Im sequentiellen Fall werden in dieser Phase u.a. Klassen geladen,
<clinit>-Methoden werden zur Initialisierung der statischen Anteile der
Klassen durchgeführt oder Thread- bzw. Datenobjekte werden erzeugt.
Abbildung 8.10 zeigt diesen Aspekt für die Simulation auf verschiedenen
Rechnern unter Einsatz der drei besprochenen Verteilungspläne. Wie man
daran erkennt, wurde zum Startup im sequentiellen Fall sowie auf der
fireball-Maschine fast keine Zeit benötigt. Obwohl diese Startup-Differenz im
verteilten Fall gemessen wurde, nachdem die gesamte verteilte Laufzeitum-
gebung betriebsbereit gewesen war, wurde viel Overhead in dieser Phase ver-
ursacht. Dabei müssen u.a. alle beteiligten JVMs ihre System- und Anwen-
dungsklassen laden, zu verteilende Objekte ggf. auch entfernt instanziieren,
und beim ersten Zugriff auf einen statischen Anteil einer Klasse muss auch
die eindeutige repräsentative Instanz dafür erzeugt werden, inklusive aller
erforderlichen Schritte für entfernte Zugriffe usw.
Dieser Unterschied läßt sich deutlich an der Grafik erkennen. Mit den bei-
den anderen Verteilungsplänen wurden die Messergebnisse zu der Startup-
Differenz wie erwartet geliefert, wobei viele Objekte lokal erzeugt wurden.
207
KAPITEL 8. EVALUATION
Abbildung 8.10.: Startup-Zeit bei der verteilten Grocery-Simulation
Der Overhead kommt höchstens durch entfernte Erzeugung von Klassenan-
teilen, Thread-Objekten und einigen Datenobjekten zustande. Durch den Ein-
satz intelligenter Verteilungsstrategien konnte die Startup-Zeit hierbei recht
gering gehalten werden. Der Vergleich mit der zyklischen Verteilung zeigt
diesen Unterschied ganz deutlich. Bei der zyklischen Verteilung wächst die
Startup-Differenz auch mit zunehmender Anzahl der Rechner, in großem Ma-
ße im Bereich zwischen 2 bis 6 Rechnern, dann mit immer kleiner werdenden
Zeitdifferenzen. Der Grund dafür ist, dass neben der ständig entfernten In-
stanziierung der zu erzeugenden Objekte auch statische Anteile initialisiert
werden, was auch entfernter Erzeugung von solchen für Klassenanteile ein-
deutig präsentierenden Instanzen entspricht.
Um aber die Güte der Verteilung und damit das Konzept zur automatischen
Verteilung fair zu evaluieren, wurden bei allen drei untersuchten Program-
men die Messergebnisse zum Performanzvergleich erst nach Ausführung des
Startups aufgenommen und in den Diagrammen dargestellt.
Performanz-Messungen auf dem Arminius-Cluster (PC2)
Zusätzlich wurden Performanz-Messungen der Grocery-Simulation auf dem
Arminius-Cluster unseres PC2durchgeführt. Die Untersuchung wurde auf
die gleiche Weise durchgeführt: Das originale, untransformierte Programm
wurde auf einem Knoten ausgeführt. Daraus ergaben sich die Messwerte
für die sequentielle Version. Die Simulation wurde dann auf verschiedenen
208
8.3. EMPIRISCHE UNTERSUCHUNGEN ZUR PERFORMANZ DER
VERTEILTEN ANWENDUNGEN
Rechner-Knoten, also zwischen 2 bis 9 Knoten, verteilt. Die Kommunika-
tion fand über eine Standard-Ethernet-Gbit-Verbindung statt. Die Messun-
gen wurden mehrmals (mehr als 10) durchgeführt. Die Messergebnisse bilden
sich aus den jeweiligen Mittelwerten. Als Verteilungsstrategien wurden die
Strategien genommen, die die besten Verteilungsergebnisse bei der Messung
auf dem normalen Pool-Cluster lieferten. Im Folgenden werden die Vertei-
lungsergebnisse miteinander verglichen, die sich aus der Verteilung auf dem
normalen Pool-Cluster und auf dem Arminius-Cluster über Ethernet-Gbit-
Verbindung ergaben.












 !!""
&'(!)
!"#$%
&'($%)
%.,/.(01)
2+%()
Abbildung 8.11.: Verteilungsergebnisse der Simulation auf normalem
Pool-Cluster und auf Arminius-Cluster
Abbildung 8.11 repräsentiert die genannten Ergebnisse: Die linken Balken
stellen die Laufzeitwerte auf dem normalen Pool-Cluster, die rechten Balken
die Werte auf dem Arminius-Cluster dar. Die darin enthaltenen schmaleren
Balken zeigen die jeweiligen anteiligen Kommunikationszeiten. Zu beachten
ist, dass auf der X-Achse die Anzahl der Rechner (Knoten) aufgetragen ist.
Ein Knoten auf dem Arminius-Cluster besitzt aber zwei CPUs, die die Pro-
zesse parallel ausführen können. Wie oben schon erläutert unterstützt die
JVM auf jedem Rechner-Knoten des Arminius-Clusters die Abbildung der
Java-Threads auf die Unix-Prozesse. Die Prozesse werden dann vom Betriebs-
system automatisch auf die CPUs verteilt und parallel ausgeführt. Das heißt,
dass die echte Parallelität bereits auf einem solchen Knoten stattfindet. Dies
ist auch der Grund, warum die Laufzeit der sequentiellen Version auf einem
Arminius-Knoten viel kürzer ist als die auf einem normalen PC, und zwar um
209
KAPITEL 8. EVALUATION
einen Faktor von 2.7. Außerdem besitzt eine CPU eines solchen Dual-System-
Rechners eine andere Architektur als die eines normalen PCs: Ein Xeon-CPU
ist mit mehr 2nd-Level-Cache (1MB) ausgerüstet und viel höher (3,2GHz) ge-
taktet. Ein Rechen-Knoten des PC2besitzt auch mehr und schnelleren Spei-
cher (4Gbyte DDR-2).
Insgesamt kann man mit so einer speziellen Architektur mehr Performanz
erreichen als mit herkömmlichen PC-Clustern. Der beste erreichte Faktor bei
einer verteilten Ausführung im Vergleich zu der sequentiellen auf demsel-
ben Arminius-Cluster beträgt etwa 1.86. Aufgrund des hohen Kommunika-
tionsaufwands ist dieser Gewinn aber nicht so groß. Dies kann man deut-
lich an den anteiligen Kommunikationskosten der gesamten Laufzeiten er-
kennen. Wie in Abbildung 8.11 zu sehen lief das verteilte Programm auf zwei
Arminius-Knoten noch langsamer als die sequentielle Version, ab 3 Knoten ist
es dann schneller. Abgesehen vom Kommunikationsaufwand kann die Paral-
lelität durch die Verteilung auf mehreren Rechnern, der jeweils 2 CPUs be-
sitzt, erzielt werden. Dadurch bekommt man insgesamt bessere Performanz
als beim normalen PC-Cluster. Abbildung 8.12 zeigt die Speedup-Werte. Bei
verteilter Ausführung auf dem Arminius-Cluster konnte ab 6 Knoten nicht
mehr viel Performanz erzielt werden, dafür nahm aber der Kommunikati-
onsaufwand zu.

3
3
3
3
4,4
!!""
!"#$%
%.,/.
Abbildung 8.12.: Speedup-Werte - Verteilung auf normalem Pool-Cluster
und auf Arminius-Cluster
Das Phänomen, ab einer bestimmten Anzahl beteiligter Rechner keinen Per-
formanzgewinn mehr zu erreichen, ist auch zu erwarten. Denn die Grocery-
Simulation ist eine irreguläre Anwendung, die im Allgemeinen willkürli-
che Kommunikationsstrukturen enthalten kann. Durch die wachsende An-
210
8.3. EMPIRISCHE UNTERSUCHUNGEN ZUR PERFORMANZ DER
VERTEILTEN ANWENDUNGEN
zahl der Threads im Programm entsteht auch mehr Kommunikationsauf-
wand. Man erreicht die Geschwindigkeitssteigerung nur bis zu einer be-
stimmten Anzahl der Knoten. Danach kann keine Performanz mehr ge-
wonnen werden, das Programm läuft sogar noch langsamer durch zu-
nehmende Kommunikationskosten. Auf dem Arminius-Cluster nahmen die
Kommunikationskosten ab 6 Knoten (entspricht 12 CPUs) ständig zu: Im Ver-
gleich zu Kommunikationskosten auf 6 Rechner wachsen sie bei 7 Knoten
um 3.03%, bei 8 Knoten um 10.39% und bei 9 Knoten um 7.49%. Trotz zuneh-
mender Kommunikationskosten bleiben die gesamten Laufzeiten ab 7 Knoten
aber fast unverändert. Das liegt daran, dass mehr Rechenleistung zur Verfü-
gung steht und dies den wachsenden Kommunikationsaufwand ausgleicht.
In Abbildung 8.11 ist auch deutlich zu erkennen, dass der Kommunikations-
aufwand beim Arminius-Cluster ab 4 Knoten etwas höher ist als beim PC-
Cluster, obwohl die Knoten dort über eine Ethernet-Gbit-Verbindung kom-
munizieren. Der Grund ist, dass die Latenz zu den Zeitpunkten der Messun-
gen zu hoch war. Die Rechner des Pool-Clusters sind an einen 100Mbit-Switch
angeschlossen und die Verbindung zwischen ihnen findet direkt statt. Wäh-
rend der Messungen auf dem PC-Pool waren auch keine anderen Störfakto-
ren vorhanden. Daher war die Netzlatenz nicht hoch. Beim PC2sind aber alle
200 Knoten an einen einzigen Gbit-Switch angeschlossen. Und dies ist der
Flaschenhals: Intern besitzt der Switch auch mehrere Backplanes, die für das
Verbinden von 200 Knoten zuständig sind. Im günstigen Fall sind die für die
Evaluation reservierten Knoten durch eine Backplane physikalisch direkt ver-
bunden und können direkt miteinander kommunizieren. Hier ist die Latenz
nicht so hoch. Wenn die reservierten Knoten physikalisch an verschiedenen
Backplanes angeschlossen sind, muss der Switch intern den Datenverkehr der
Knoten auch noch über diese Platinen leiten. Zusätzlich ist die Latenz noch da-
von abhängig, wie stark die Nutzung des gesamten Netzes ist, und somit der
Engpass des Switches. Alle dieser Aspekte zusammen führten zu hoher La-
tenz während der Messungen, daher musste die Performanz insgesamt dar-
unter leiden.
Die Evaluation auf dem Arminius-Cluster des PC2zeigte, dass durch mehr
Rechnerleistung mehr Parallelität und somit auch mehr Performanz erzielt
werden kann. Ein wichtiger Schlüssel zur Geschwindigkeitssteigerung durch
die geschickte verteilte Ausführung ist die schnelle Kommunikation, wobei
die Latenz eine sehr wichtige Rolle spielt. Im Allgemeinen besitzen irregu-
läre Anwendungen wie die Grocery-Simulation Charakteristiken, die nicht
typisch für High-Performance-Computing sind wie numerische Berechnun-
211
KAPITEL 8. EVALUATION
gen, Renderer usw. Daher konnte die parallele Ausführung mit geschickten
Verteilungsstrategien keine sehr guten Ergebnisse auf dem Arminius-Cluster
erreichen, der aber für solche HP-Anwendungen viel besser geeignet ist. Im
folgenden Abschnitt werden wir erfahren, dass die verteilte Ausführung des
Raytracer-Programms mtrt mit regulären Strukturen sehr gute Performanz
auf diesem speziellen Cluster erreichte.
8.3.2. Multithreaded Raytracer (mtrt) aus dem SPECjvm98
Der nächste zu untersuchende Kandidat ist das bekannte Raytracer-
Programm aus dem SPECjvm98-Benchmark [Cor] in der mehrsträngigen Ver-
sion. Man kann die Größe des Canvas des zu rendernden Bildes und die An-
zahl der Threads anfangs definieren. Wenn mehr als ein Thread angegeben
wird, z.B. n > 1Threads, besitzt jeder die Daten der gesamten Szene - je-
der erzeugt also ein eigenes Szene-Objekt - aber rendert nur eine Kachel der
Größe 1
n. Zum Start lesen die Threads aus einer Eingabedatei die Spezifikati-
onsdaten des zu rendernden Bildes. Hierin sind die Objekte mit ihren Koordi-
naten, Formen, Farb- und Lichtinformationen usw. definiert. Aus diesen Da-
ten werden jeweils zugehörige Objekte aus entsprechenden Klassen erzeugt,
die die Datenstrukturen repräsentieren, z.B. mtrt.Vector,mtrt.Point,
mtrt.Light . . . Nachdem die erforderlichen initialen Strukturen komplett
aufgebaut wurden, fängt jeder Worker-Thread an, seine Kachel zu rendern.
Hier steckt die eigentliche Arbeit bzw. der Workload. So durchläuft jeder Wor-
ker den gesamten Canvas mittels zweier geschachtelter Schleifen in Breite
und Höhe und rendert jedes seiner Kachel zugehörige Pixel. Dies wurde mit-
tels der Methode Shade() in der Klasse mtrt.Scene realisiert. In der ori-
ginalen Version des Programms wird jedes fertig gerenderte Pixel sofort im
Canvas mittels der mtrt.Canvas.Write(...)-Methode aktualisiert. Seien
height und width die definierte Höhe und Breite des Bildes, dann werden diese
beiden Methoden bei jedem Worker-Thread height×1
n×width-mal ausgeführt.
Zur Evaluation wurden fünf Threads eingesetzt, um ein Canvas der Größe
805 ×805 zu rendern. Der Grund für diese ungewöhnliche Größe ist, dass
unabhängig von der Anzahl der Threads die Überprüfung der Pixels mit-
tels der mtrt.IntersectPt.FindNearestIsect()-Methode an den Ka-
chelgrenzen bei bestimmten Kachelgrößen zu endloser Rekursion führt. Dies
ist ein reproduzierbarer Fehler im originalen Programm. Außerdem habe ich
bei der Untersuchung der Verteilung einige Optimierungen und Erweiterun-
gen der ursprünglichen Version des Programms eingebaut: Im originalen Pro-
212
8.3. EMPIRISCHE UNTERSUCHUNGEN ZUR PERFORMANZ DER
VERTEILTEN ANWENDUNGEN
gramm wird bei jeder Schleifeniteration jeweils ein neues mtrt.Color- und
mtrt.Point-Objekt erzeugt, dies führt zu unnötigem Speicherverbrauch.
Deshalb wurde es so optimiert, dass die beiden Objekte außerhalb der Schlei-
fe erzeugt und bei jeder Iteration erneut initialisiert und wiederverwendet
werden. Neben der Aktualisierung des Canvas nach jeder Berechnung eines
Pixels in der originalen Version, die im Folgenden mit opt0 bezeichnet wird,
wurde das gesamte Rendering um zwei weitere Versionen erweitert: Bei der
einen wird die Aktualisierung des Canvas nach fertigem Rendern einer Spal-
te der Kachel (opt1) und bei der anderen nach dem kompletten Rendern der
Kachel (opt2) durchgeführt. Hierdurch werden die Kommunikationsschrit-
te der Threads mit dem Canvas reduziert, dafür werden pro Kommunika-
tionsschritt aber mehr Daten verschickt. Diese Änderungen der ursprüngli-
chen Version dienen nur dem Zweck, die Güte der angewandten Verteilungs-
strategien zu evaluieren. Man kann vor allem dadurch zeigen, für welche
Berechnungs- sowie Kommunikationsstrukturen sich der Einsatz bestimmter
Verteilungsstrategien eignet.
Performanz-Messungen auf einem normalen PC-Cluster
Eine Laufzeitmessung bei der Evaluation erfolgt ab dem Start des ersten
Threads und endet, nachdem der Canvas komplett gerendert wurde. Wie
bei der Grocery-Simulation werden die Laufzeit und der Kommunikations-
aufwand der sequentiellen, originalen Version gegenüber der transformierten
Version opt0, ausgeführt auf einem Rechner verglichen. Hierbei kann man den
durch die Transformations- und verteilten Laufzeitumgebung verursachten
Overhead erkennen. So lief die transformierte Version auf einem Rechner et-
wa 77% langsamer als die sequentielle, dies entspricht etwa einem speed-down
von 0.566. Diese verursachte etwa 157fach mehr Kommunikationsaufwand
als die Originale. Dieser höhere Kommunikationsaufwand entsteht durch das
ständig Aktualisieren des Canvas bei jeder Iteration plus zusätzlicher Kosten
für entfernte Kommunikation, die bei der verteilten Version trotz der Ausfüh-
rung auf einem Rechner entstehen.
Bei der Evaluation des mtrt-Programms wurde die zyklische Verteilung nicht
eingesetzt. Die hier verwendeten Verteilungsstrategien sind die gleichen wie
bei der Grocery-Simulation. Außerdem wurde das Konzept zur hybriden
Analyse hierbei angewandt: Zur Abschätzung des Workloads und Kommu-
nikationsaufwands mittels der ODF fTHREADWORKCOMM wurde zunächst
bei allen vorkommenden Schleifen die Anzahl der Durchläufe jeweils der
213
KAPITEL 8. EVALUATION
3 
*




*
1
*14
1
$<;<&86&= 9&&5  =
7 6
+(&&;
' )(&89
Abbildung 8.13.: Verteilungsergebnisse der originalen Version des mtrt-
Programms
Wert 10 angenommen. Da die Laufzeitkonstanten zur Höhe und Breite (je-
weils 805) des Canvas zur Laufzeit bekannt waren, bevor die Worker an-
fangen zu starten, konnten diese Werte direkt in die Verteilungsberechnun-
gen eingesetzt werden. Dies führte zur erneuten Berechnung der Platzie-
rungsentscheidung der Objekte zu den Workern, bevor die Objekte tatsäch-
lich erzeugt wurden (alle zum Rendern benötigten Datenobjekte wurden
innerhalb der Szene erzeugt, also nachdem ein Worker gestartet wurde).
Durch die hybride Analyse zeigten die neu berechneten Verteilungshinwei-
se, dass alle innerhalb der geschachtelten Schleife zum Rendern zu erzeu-
genden Objekte zusammen mit dem zugehörigen Worker platziert werden
sollen. Durch die Bekanntgabe der Laufzeitkonstanten verrichtet ein Worker
die meiste Workload und Methodenaufrufe auf diesen Objekten. Zusammen
mit anderen statischen Strategien wie isPeer,isProxy,isThreadLocal
und isStagewiseImmutable ergaben sich die Platzierungsentscheidung-
en: Außer dem Canvas-Objekt, auf das alle Worker zugreifen, wurde die kom-
plette Szene (Scene-Objekt) mit allen dazu gehörigen Datenobjekten zusam-
men mit ihrem zugehörigen Worker-Thread in einen Adressraum platziert.
Die hier als stagewise-immutable erkannten Erzeugungsstellen besitzen auch
die isThreadLocal-Eigenschaft, so dass die entstehenden Objekte ebenfalls
lokal erzeugt werden, anstatt diese zu replizieren. Der einzige entfernte Me-
thodenaufruf ist das Schreiben des Canvas.
Die ersten Messergebnisse des mtrt-Programms werden in Abbildung 8.13
dargestellt. Es handelt sich hierbei um das originale Programm (opt0). Wie
man erkennt, wurde die meiste Zeit des Ablaufs für Kommunikation benö-
tigt, also etwa 77% bzw. 95% der gesamten Laufzeit auf 2 bzw. auf 5 Rech-
214
8.3. EMPIRISCHE UNTERSUCHUNGEN ZUR PERFORMANZ DER
VERTEILTEN ANWENDUNGEN
nern. Die anteiligen Kommunikationskosten werden durch die jeweiligen in-
neren Balken visualisiert. Das wurde durch Aktualisierung des Canvas bei je-
der Iteration verursacht, indem die Write(...)-Methode bei jedem Thread
805×1
5×805 =129605mal entfernt aufgerufen wurde. Trotz der Lokalitätsop-
timierung der Datenobjekte mittels geschickter Verteilungsstrategien wurde
das Programm noch erheblich verlangsamt, auch im Fall mit mehr beteiligten
Rechnern. Daraus kann man schließen, dass die Performanz bei der verteilten
Ausführung insgesamt hauptsächlich durch entfernte Methodenaufrufe be-
einträchtigt wird. Um dies genauer zu untersuchen, wurde deshalb das mtrt-
Programm mit Kommunikationsoptimierungen opt1 und opt2 verteilt ausge-
führt, also mit einmaliger Aktualisierung einer Spalte bzw. der ganzen Ka-
chel. Abbildung 8.14 zeigt die Verteilungsergebnisse.
3  *

*



1
41
1 1 41


  
$<;<&87 6 9&&5  =
7 6
=
7 6
+(&&;
' )(&89
Abbildung 8.14.: Verteilungsergebnisse mit Kommunikationsoptimie-
rung opt1 und opt2 für mtrt
Gegenüber der vorherigen verteilten Ausführung kann hier sehr viel Per-
formanz aufgrund der Kommunikationsoptimierung herausgeholt werden.
Beim Einsatz von 5 Rechnern konnte das Programm mit opt1 um etwa 25fach,
und mit opt2 32fach schneller gegenüber der unoptimierten Version laufen.
Die sequentielle Ausführung wurde dadurch auch etwas schneller, da hier-
durch anscheinend sehr viele Methodenaufrufe eingespart werden. Diese Op-
timierungsschritte basieren auf den Erkenntnissen, dass ein IP-Paket mindes-
tens 15 Bytes fester Verwaltungsdaten (2 Bytes für MAC-Adresse, 2 für IP ...)
und die Nutzdaten enthält. Somit wird ein Paket bis einer Gesamtgröße von
maximal 1,5 KBytes übertragen. Daher besteht nur sehr wenig Unterschied
bei der Übertragungsdauer zwischen den Daten eines Pixels und den Daten
215
KAPITEL 8. EVALUATION
der gesamten Spalte (zweidimensionales Array für die ganze Spalte mit Farb-
information) bzw. denen des gesamten Canvas. (dreidimensionales Array).
Der entstehende Aufwand zum Verschicken von mehr Daten kann in Kauf
genommen werden. Dies wird ganz deutlich durch die inneren Balken für
Kommunikationszeit gezeigt. Bei der opt1-Version kann bis zu etwa 97% an
Kommunikationskosten bei der verteilten Ausführung auf 5 Rechnern ein-
gespart werden. Obwohl bei der Optimierung opt2 mehr Daten aber nur
einmalig nach dem fertigen Rendern verschickt wurden, wurden trotzdem
Kommunikationskosten eingespart und somit die gesamte Laufzeit verkürzt.
Bei der verteilten Ausführung auf 2 Rechnern mit der Optimierung opt2 wur-
den 3 Worker-Threads zusammen mit dem Canvas-Objekt auf demselben
Rechner platziert. Daher fand die Kommunikation dieser 3 Worker mit dem
Canvas dann lokal statt. Dies führt zur Einsparung von etwa 98% Kosten an
entfernten Methodeaufrufen. Mit Ausnahme von diesem sind die anderen in-
neren Balken für die Kommunikationszeit fast gleich hoch. Dies entspricht
auch der Tatsache, dass bei Verteilung auf mehreren Rechnern auch soviel
Daten verschickt werden.
3  * )



*

1
414
*1*
1
*1*
1

   

$<;<&8#6>&7 6 9&&5  =
7 6
=
7 6
+(&&;
' )(&89
Abbildung 8.15.: Verteilungsergebnisse mit Kommunikationsoptimie-
rung opt1 und opt2 und 10fach Workload für mtrt
Diese Ergebnisse zeigen, dass die entfernte Kommunikation durch entfernte
Methodenaufrufe bei der verteilten Ausführung eines mehrsträngigen Pro-
gramms schwergewichtig ist. Wenn man aber nur den eigentlichen Workload
für das Rendern in Abbildung 8.14 betrachtet, also den restlichen Teil ohne
die Kommunikationszeit bei allen Balken, dann ist diese Zeit kürzer als die
bei der sequentiellen Ausführung. Dies kommt dadurch, dass die Parallelität
216
8.3. EMPIRISCHE UNTERSUCHUNGEN ZUR PERFORMANZ DER
VERTEILTEN ANWENDUNGEN
im verteilten Fall besser ausgenutzt wurde als die verzahnte, aber sequentielle
Ausführung auf einem Rechner. Es lohnt sich daher der Fall zu untersuchen,
bei dem der Workload mehr Gewicht als die Kommunikation aufweist. Des-
halb wurde zu diesem Zweck der Workload für das Rendern zusätzlich um
das 10fache erhöht. Dieser Zusatzaufwand entspricht etwa mehr Berechnun-
gen zusätzlicher Informationen für jedes Pixel wie etwa Farben, Lichtreflexi-
on und 10fach Antialiasing usw. Die Verteilungsergebnisse dafür werden in
Abbildung 8.15 dargestellt.
3  * )
1
1
1
1
  =
=
)
+(&&;
Abbildung 8.16.: Speedup bei mtrt mit 10fach Workload und Optimie-
rungen opt1,opt2
Daran erkennt man, dass das Verschicken der gesamten Kachel (opt2) im ver-
teilten Fall gleich bleibt (die inneren Balken bei opt2). Zum Vergleich mit den
Ergebnissen über Kommunikationskosten bei der Optimierung opt1 in Ab-
bildung 8.14 sinken die inneren Balken für Kommunikationskosten hierbei
mit steigender Rechnerzahl. Der Grund dafür ist, dass ohne Erhöhung des
Workloads die Worker-Threads schnell fertig berechnen konnten und ihre
Daten gleichzeitig auf den Canvas aktualisieren. Da das Canvas-Objekt auf
einem Rechner liegt, entsteht dort ein Engpass zur Entgegennahme der ent-
fernten Aufrufe. Denn auf dieser Seite gibt es nur einen einzigen Thread, der
von RMI erzeugt wurde und das Skeleton des Canvas-Objekts repräsentiert.
Er muss alle diese entfernten Aufrufe nach und nach abarbeiten. Durch die
Erhöhung des Workloads haben die Worker viel mehr zu tun als zu kommu-
nizieren. Die einzelnen Messungen (wurde schon in der Grafik zusammen-
gefasst) zeigten, dass je mehr ein Worker Zeit zum Rendern braucht, desto
weniger Zeit benötigt er zur Kommunikation. Je mehr Rechner sich daran be-
teiligen, um so gleichmäßiger wird der Workload auf alle Workern verteilt, so
dass der Kommunikationsaufwand insgesamt reduziert wurde. Bei diesem
217
KAPITEL 8. EVALUATION
Performanzvergleich wurde das Messergebnis auf der fireball-Maschine ein-
bezogen. So kann man deutlich erkennen, dass allein auf 2 Rechnern schon
mehr Performanz erreicht wurde als auf der Mehrprozessor-Maschine. Wenn
man nur den Rechenanteil ohne Kommunikation in Betracht zieht, dann wird
etwa 2,4fache mehr Performanz beim Cluster mit 5 Rechnern erreicht als bei
der Mehrprozessor-Maschine.
Abbildung 8.17.: Die gerenderte Szene
Abbildung 8.16 stellt die Speedup-Daten dar. So kann eine verteilte Ausfüh-
rung mit geschickten Verteilungsstrategien sehr gute Performanz erreichen.
Diese kann aber nur erzielt werden, wenn das Programm sehr viel mehr Zeit
mit Berechnungen verbringt als mit Kommunikation. Sonst können intelli-
gente Strategien zur Verteilung auch nicht zur Geschwindigkeitssteigerung
beitragen. Zusätzlich zur Gültigkeitsprüfung der gerenderten Daten wurde
das Ergebnis des Programms bei der verteilten Ausführung mit dem der se-
quentiellen Ausführung der originalen Version verglichen. Das sind die Pi-
xeldaten, die sich aus dem Rendering ergeben. Das Ergebnis läßt sich in Ab-
bildung 8.17 sehen.
Performanz-Messungen auf dem Arminius-Cluster (PC2)
Für dieses Benchmark-Programm wurden ebenfalls Messungen zur automa-
tischen Verteilung auf dem Arminius-Cluster des PC2durchgeführt. Wie bei
der Grocery-Simulation wurden die Verteilungsstrategien verwendet, die auf
218
8.3. EMPIRISCHE UNTERSUCHUNGEN ZUR PERFORMANZ DER
VERTEILTEN ANWENDUNGEN
dem normalen Pool-Cluster die besten Ergebnisse ermittelten. Abbildung 8.18
zeigt die Messwerte zum Raytracer mtrt, die sich aus der empirischen Unter-
suchung auf dem Pool-Cluster und auf dem Cluster des PC2ergaben. Hierbei
wurden auch 5 Knoten zur Berechnung genommen, die jeweils 2 CPUs als
Dual-System besitzen.











3
3 3 3 3
    
15/5(67',8&'4) !!""
&'(!)
!"#$%
&'($%)
%.,/.
2+%()
Abbildung 8.18.: Verteilungsergebnisse - Messwerte auf normalem PC-
Cluster und auf Arminius-Cluster
Schon beim sequentiellen Ablauf des Raytracers erkennt man den großen
Laufzeitgewinn (etwa 3.6fach schneller) durch mehr Rechenleistung, die ein
Knoten mit 2 CPUs anbietet. Die verteilte Ausführung auf mehr als einem
Knoten des Arminius-Clusters brachte dann wieder etwas mehr Performanz
als die sequentielle auf einem Arminius-Knoten. Hier haben die geschickten
Strategien, die Rechenleistung und die schnellere Verbindung ihren Beitrag.
So konnte die Verteilung des Programms auf 3 Knoten etwa 23,7% mehr Per-
formanz als auf 2 Knoten und 49.9% mehr als auf einem Knoten erzielen. Die
Geschwindigkeitssteigerung durch Teilnahme von 4 und 5 Knoten änderte
sich dann aber nicht mehr. Es ist sogar etwas langsamer. Der Grund ist der,
dass das Raytracer-Programm insgesamt nur 5 Worker-Threads zum Rendern
enthält. Die 3 Knoten des Arminius-Clusters besitzen bereits 6 CPUs, also
optimal für die parallele Berechnung der Worker. Das heißt, dass 2 Worker-
Threads auf einem Knoten parallel ablaufen können und die Kommunikation
zwischen ihnen zum Schluß beim Schreiben der zugeteilten Kachel vom Can-
vas fast Null ist. Es entstand nur entfernter Kommunikationsaufwand dort,
wo die Worker auf anderen Knoten ihre Daten verschicken.
Da die Rechnerarchitektur bei der Verteilungsberechnung (noch) nicht be-
rücksichtigt wurde, wurde die Information nicht ausgenutzt, dass ein Kno-
ten ein Dual-System mit 2 CPUs ist und 2 Worker-Threads auf einen Knoten
219
KAPITEL 8. EVALUATION

3
3
3
3
4,4
!!""
!"#$%
%.,/.
Abbildung 8.19.: Speedup-Werte
platziert werden sollten. Stattdessen wurden die 5 Worker einfach zyklisch
auf die verfügbaren Knoten verteilt, was bei 4 und 5 Knoten zur Verschwen-
dung der Rechnerleistung führte. Damit wurde aber noch mehr entfernter
Kommunikationsaufwand verursacht. Das läßt sich deutlich an den antei-
ligen Kommunikationskosten der inneren Balken erkennen. Abbildung 8.19
zeigt die Speedup-Werte aller drei Untersuchungen.
Im Gegensatz zur Grocery-Simulation besitzt das mtrt-Programm reguläre
Berechnungsstruktur, bei der die Worker ihre eigenen Berechnungsanteile
ausführen und in einer bestimmten Phase miteinander kommunizieren. Die
Parallelität derartiger Anwendungen skaliert sehr gut mit zunehmender An-
zahl der beteiligten CPUs. Durch mehr Rechenleistung und eine schnellere
Verbindung, welche der Arnimius-Cluster zur Verfügung stellt, konnten so-
wohl die Rechen- als auch Kommunikationszeiten bei der verteilten Ausfüh-
rung verkürzt werden. Insgesamt erzielte man erheblich Geschwindigkeits-
steigerung.
Speichernutzung
Bei der Evaluation des mtrt-Programms wurde erkannt, dass sehr viel
Speicher zur Ausführung erforderlich ist. Das liegt auch daran, dass je-
der Worker-Thread ein eigenes Scene-Objekt besitzt. Eine Szene erzeugt
während des Renderns, also innerhalb der mtrt.Scene.RenderScene()-
Methode, sehr viele lokale Objekte, z.B. Vector-Objekte (nicht vom Typ
java.util.Vector, sondern eine Struktur zur Darstellung mathematischer
Vektoren) oder Point-Objekte. Daher habe ich noch einen anderen Aspekt un-
tersucht, und zwar den Speicherverbrauch bei solchen Programmen im Zu-
220
8.3. EMPIRISCHE UNTERSUCHUNGEN ZUR PERFORMANZ DER
VERTEILTEN ANWENDUNGEN
sammenhang mit der Verteilung. So wurde Profiling für das ursprüngliche
mtrt-Programm mit derselben Eingabe durchgeführt. Die Anwendung wur-
de sequentiell auf einem Powerbook G4 mit 1,5 GB Hauptspeicher ausgeführt,
wobei das Programm jprofiler [Gmb] (Version 4.1.2) für Profiling benutzt
wurde.
Abbildung 8.20 zeigt die absteigend sortierte Anzahl der erzeugten Instan-
zen und den Speicherverbrauch dafür. In Abbildung 8.21 finden sich die
Profiling-Daten darüber, in welchen aufgerufenen Methoden wie viele In-
stanzen insgesamt erzeugt werden und wieviel Speicher (in Bytes) jeweils
gelegt wird. Die Methoden werden als Aufrufbaum zusammen mit ihrem
anteiligen Speicherverbrauch und der Anzahl der allokierten Objekten dar-
gestellt. Diese Daten gelten für das ganze Programm, also für alle Worker-
Threads. Die Angabe zu erzeugten Instanzen bezieht sich auf die während
des gesamten Programmablaufs erzeugten Objekte. Das bedeutet, dass man
etwa 2,5GB Speicher für die Ausführung des mtrt-Programms benötigen
würde, wenn der Garbage-Collector nicht aktiv wäre. Mit Garbage-Collector
brauchte jeder Worker-Thread etwa 25MB Speicher ständig während sei-
ner gesamten Ausführung. Mit den hier verwendeten Eingabedaten, also
mit 5 Worker-Threads und Canvas-Größe 805×805, musste das Programm
mit einer initialen Heap-Größe von 250MB starten. An den Ergebnissen in
Abbildung 8.20 erkennt man, dass sehr viele mtrt.Vector-Objekte zum
Rendern erzeugt wurden. Zusammen mit der Information aus Abbildung
8.20 kann man daraus schließen, dass 98.4% alle dieser Objekte innerhalb
der mtrt.Scene.RenderScene()-Methode erzeugt wurden, insbesonde-
re durch die Vielzahl der Aufrufe der mtrt.Scene.Shade()-Methode.
Aus dieser Speicheranalyse, insbesondere aus den Daten in Abbildungen 8.21
und 8.22 kann man erkennen, dass der Einsatz der geschickten Verteilungs-
strategien in diesem Fall sehr nützlich war. Denn ohne die Lokalitätsoptimie-
rung mittels der verwendeten ODFs, die die statischen Programmeigenschaf-
ten isThreadLocal,isProxy,isPeer und isStagewiseImmutable be-
rechnen, müssten sehr viele Objekte entfernt erzeugt werden. Dies würde
dann zu sehr viel zusätzlichen Kommunikationen führen. Durch diese sta-
tischen ODFs, insbesondere die zur isThreadLocal-Eigenschaft, wurden
viele Objekte innerhalb der mtrt.Scene-Klasse lokal zusammen mit dem
zugehörigen Thread erzeugt. Daher konnten alle Methodenaufrufe innerhalb
des erzeugten Scene-Objekts direkt lokal ausgeführt werden. Zusätzlich da-
zu leistete die ODF fTHREADWORKCOMM zur Berechnung von Workload und
Kommunikationskosten zusammen mit der hybriden Analyse einen großen
221
KAPITEL 8. EVALUATION
Beitrag. Durch die Bekanntgabe der Laufzeitkonstanten zur Breite und Hö-
he des Canvas konnten Verteilungsentscheidungen für die in der geschach-
telten Schleife zu Rendern entstehenden Objekte erneut berechnet werden,
so dass diese zusammen mit dem sie erzeugenden Worker-Thread plat-
ziert werden. Das sind die meisten Objekte, die durch viele Aufrufe der
mtrt.Scene.Shade()-Methode in der inneren Schleife entstanden.
In Abbildung 8.22 kann man erkennen, dass 96.8% der gesamten Lauf-
zeit durch die Ausführung der mtrt.Scene.Shade()-Methode verur-
sacht wurde. Diese Zeit ergabt sich aus den Ausführungszeiten verschie-
dener Methoden, die sich in den Unterbaumknoten in der Grafik fin-
den lassen: z.B. 36.3% von mtrt.Scene.FindLightBlock, 29.3% von
mtrt.IntersectPt.FindNearestIsect, 25.2% der Shade()-Methode
selbst mit 18.0% Anteil von mtrt.IntersectPt.FindNearestIsect.
Würden beispielsweise die mtrt.ItersectPt-Objekte vom mtrt.Scene-
Objekt entfernt platziert, hätte dies sehr viel zusätzlichen Kommunikations-
aufwand durch remote Aufrufe verursacht. Diese Profiling-Daten bestätigen
also nochmals die Güte der getroffenen Verteilungsentscheidungen.
Aus der Untersuchung des Speicherbedarfs derartiger Anwendungen ergibt
sich die Erkenntnis, dass durch die verteilte Ausführung nicht nur die Per-
formanz verbessert werden kann, sondern automatisch auch mehr Speicher
zur Verfügung gestellt wird. Die Skalierbarkeit kann hier in zwei Hinsich-
ten genutzt werden. In bestimmten Anwendungsgebieten, wie z.B. Load-
Balancierung bei verteilten Servern, wo die verteilten Programme endlos lau-
fen, spielt der Speicherbedarf eine wichtigere Rolle als die Geschwindigkeits-
steigerung.
222
8.3. EMPIRISCHE UNTERSUCHUNGEN ZUR PERFORMANZ DER
VERTEILTEN ANWENDUNGEN
Name Instance count Size
mtrt.Vector 64,712,257 1,553,094,168
int[ ] 16,544,920 399,673,208
mtrt.Point 5,091,992 122,207,808
float[ ] 3,244,246 77,861,936
<class>[ ] 3,186,876 147,635,128
mtrt.Color 3,033,787 72,810,888
mtrt.IntersectPt 2,847,968 91,134,976
mtrt.Ray 2,435,193 58,444,632
mtrt.OctNode 1,026,169 42,437,936
java.lang.String 180,112 4,322,688
mtrt.ObjNode 152,370 3,656,880
char[ ] 131,187 6,391,520
mtrt.Face 89,790 2,154,960
java.lang.FloatingDecimal 64,295 2,571,800
java.lang.Float 64,270 1,028,320
mtrt.CacheIntersectPt 7,060 282,400
mtrt.TriangleObj 6,975 502,200
java.lang.StringBuffer 1,191 28,584
java.util.HashMap 145 5,800
java.util.HashSet 145 2,320
short[ ] 142 11,160
byte[ ] 84 430,064
mtrt.PolygonObj 60 3,840
java.lang.Class 59 3,776
java.net.URL 58 3,248
java.net.URLClassLoader$1 58 928
java.lang.ClassNotFoundException 58 1,856
mtrt.Material 40 1,600
mtrt.MaterialNode 40 960
java.lang.ref.Finalizer 35 1,120
java.nio.HeapCharBuffer 33 1,584
java.io.FileInputStream 30 480
java.io.FileDescriptor 30 480
java.io.File 30 480
sun.misc.URLClassPath$7 29 696
java.security.CodeSource 29 696
java.security.PrivilegedActionException 29 928
mtrt.SphereObj 25 1,200
java.lang.ThreadLocal$ThreadLocalMap$Entry 10 320
mtrt.LightNode 10 240
mtrt.Light 10 240
java.security.AccessControlContext 8 192
mtrt.Scene 5 360
mtrt.Runner 5 520
mtrt.Camera 5 160
java.io.BufferedInputStream 5 160
java.io.DataInputStream 5 120
java.lang.ThreadLocal$ThreadLocalMap 5 120
mtrt.io.FileInputStream 5 160
java.io.ByteArrayInputStream 4 96
java.lang.reflect.Method 4 224
double[ ] 3 312
java.util.Hashtable$Entry 2 48
java.lang.Thread 2 144
mtrt.Canvas 1 24
java.util.Hashtable 1 40
Abbildung 8.20.: Profilingdaten für mtrt: Anzahl erzeugter Objekte mit
dafür benötigtem Speicher 223
KAPITEL 8. EVALUATION
98.4% - 2,536,019 kB - 101,481,836 alloc. mtrt.Runner.run
98.4% - 2,536,014 kB - 101,481,740 alloc. mtrt.Scene.RenderScene
94.9% - 2,445,097 kB - 98,233,176 alloc. mtrt.Scene.Shade
34.5% - 889,668 kB - 35,825,862 alloc. mtrt.Scene.Shade
20.1% - 518,322 kB - 21,475,714 alloc. mtrt.IntersectPt.FindNearestIsect
12.4% - 320,307 kB - 13,346,145 alloc. mtrt.PolyTypeObj.Intersect
5.4% - 139,790 kB - 5,824,597 alloc. mtrt.TriangleObj.Check
6.5% - 168,558 kB - 7,023,266 alloc. mtrt.OctNode.Intersect
0.3% - 8,716 kB - 363,179 alloc. mtrt.IntersectPt.<init>
0.0% - 402 kB - 16,766 alloc. mtrt.SphereObj.Intersect
8.7% - 223,169 kB - 9,265,642 alloc. mtrt.Scene.GetLightColor
8.6% - 220,788 kB - 9,166,440 alloc. mtrt.Scene.FindLightBlock
6.2% - 160,497 kB - 6,687,385 alloc. mtrt.PolyTypeObj.Intersect
2.5% - 64,389 kB - 2,682,916 alloc. mtrt.TriangleObj.Check
2.0% - 52,086 kB - 2,170,288 alloc. mtrt.OctNode.Intersect
0.1% - 2,380 kB - 99,202 alloc. mtrt.IntersectPt.<init>
0.0% - 267 kB - 11,161 alloc. mtrt.SphereObj.Intersect
0.0% - 1,190 kB - 49,601 alloc. mtrt.Ray.<init>
1.8% - 46,486 kB - 1,089,537 alloc. mtrt.OctNode.<init>
0.7% - 17,432 kB - 726,358 alloc. mtrt.Ray.<init>
0.3% - 8,716 kB - 363,179 alloc. mtrt.IntersectPt.<init>
29.3% - 755,235 kB - 31,226,018 alloc. mtrt.Scene.GetLightColor
28.6% - 737,802 kB - 30,499,660 alloc. mtrt.Scene.FindLightBlock
13.4% - 344,388 kB - 14,349,524 alloc. mtrt.PolyTypeObj.Intersect
5.3% - 135,279 kB - 5,636,646 alloc. mtrt.TriangleObj.Check
12.9% - 333,291 kB - 13,887,150 alloc. mtrt.OctNode.Intersect
0.7% - 17,432 kB - 726,358 alloc. mtrt.IntersectPt.<init>
0.1% - 2,013 kB - 83,912 alloc. mtrt.SphereObj.Intersect
0.3% - 8,716 kB - 363,179 alloc. mtrt.Ray.<init>
20.8% - 535,798 kB - 22,108,946 alloc. mtrt.IntersectPt.FindNearestIsect
9.4% - 242,027 kB - 10,084,469 alloc. mtrt.PolyTypeObj.Intersect
4.2% - 108,984 kB - 4,541,019 alloc. mtrt.TriangleObj.Check
9.3% - 240,205 kB - 10,008,542 alloc. mtrt.OctNode.Intersect
0.6% - 15,552 kB - 648,025 alloc. mtrt.IntersectPt.<init>
0.1% - 1,724 kB - 71,860 alloc. mtrt.SphereObj.Intersect
3.2% - 82,947 kB - 1,944,075 alloc. mtrt.OctNode.<init>
3.2% - 82,947 kB - 1,944,075 alloc. mtrt.OctNode.Initialize
1.2% - 31,105 kB - 1,296,050 alloc. mtrt.Ray.<init>
0.6% - 15,552 kB - 648,025 alloc. mtrt.IntersectPt.<init>
0.0% - 3,720 bytes - 55 alloc. java.lang.StringBuffer.append(double)
0.0% - 2,136 bytes - 35 alloc. java.lang.StringBuffer.append(int)
0.0% - 552 bytes - 6 alloc. java.lang.StringBuffer.append(java.lang.String)
0.0% - 480 bytes - 10 alloc. java.io.PrintStream.println
0.0% - 240 bytes - 5 alloc. java.lang.StringBuffer.<init>
0.0% - 120 bytes - 5 alloc. java.lang.StringBuffer.toString
0.0% - 120 bytes - 5 alloc. mtrt.Ray.<init>
0.0% - 2,320 bytes - 20 alloc. java.lang.StringBuffer.append(java.lang.String)
0.0% - 1,280 bytes - 28 alloc. java.io.PrintStream.println
0.0% - 480 bytes - 10 alloc. java.lang.StringBuffer.<init>
0.0% - 440 bytes - 10 alloc. java.lang.StringBuffer.append(double)
0.0% - 240 bytes - 10 alloc. java.lang.StringBuffer.toString
1.6% - 40,363 kB - 1,339,746 alloc. mtrt.Main.main
Abbildung 8.21.: Profilingdaten für mtrt: Aufrufbaum mit Anzahl er-
zeugter Objekte zusammen mit belegtem Speicher
224
8.3. EMPIRISCHE UNTERSUCHUNGEN ZUR PERFORMANZ DER
VERTEILTEN ANWENDUNGEN
99.9% - 49,450,079 ms - 5 inv. mtrt.Runner.run
99.9% - 49,449,458 ms - 5 inv. mtrt.Scene.RenderScene
96.8% - 47,891,622 ms - 648,025 inv. mtrt.Scene.Shade
37.2% - 18,400,319 ms - 363,179 inv. mtrt.Scene.GetLightColor
36.3% - 17,977,057 ms - 726,358 inv. mtrt.Scene.FindLightBlock
0.2% - 78,307 ms - 363,179 inv. mtrt.Ray.<init>
0.1% - 58,641 ms - 726,358 inv. mtrt.Vector.Normalize
0.1% - 37,580 ms - 726,358 inv. mtrt.Vector.Length
0.1% - 30,209 ms - 726,358 inv. mtrt.Vector.Sub
0.1% - 26,794 ms - 682,194 inv. mtrt.Color.Mix
0.0% - 22,747 ms - 682,228 inv. mtrt.Vector.Dot
0.0% - 9,279 ms - 2,861,302 inv. mtrt.Ray.GetDirection
0.0% - 6,337 ms - 726,358 inv. mtrt.LightNode.Next
0.0% - 3,734 ms - 1,408,552 inv. mtrt.LightNode.GetLight
0.0% - 2,771 ms - 682,228 inv. java.lang.Math.max
0.0% - 2,396 ms - 682,194 inv. mtrt.Light.GetColor
0.0% - 2,367 ms - 726,358 inv. mtrt.Ray.SetOrigin
0.0% - 2,126 ms - 726,358 inv. mtrt.Ray.SetID
0.0% - 2,009 ms - 682,194 inv. mtrt.Material.GetDiffColor
0.0% - 1,972 ms - 682,194 inv. mtrt.ObjectType.GetMaterial
0.0% - 1,720 ms - 726,358 inv. mtrt.Light.GetPosition
29.3% - 14,492,164 ms - 648,025 inv. mtrt.IntersectPt.FindNearestIsect
13.5% - 6,690,068 ms - 5,004,271 inv. mtrt.OctNode.Intersect
9.3% - 4,623,998 ms - 5,543,450 inv. mtrt.PolyTypeObj.Intersect
4.3% - 2,116,907 ms - 6,432,435 inv. mtrt.OctNode.FindTreeNode
0.3% - 157,639 ms - 648,025 inv. mtrt.IntersectPt.<init>
0.1% - 60,992 ms - 5,856,349 inv. mtrt.ObjNode.Next
0.1% - 54,391 ms - 400,810 inv. mtrt.OctNode.Copy
0.1% - 40,667 ms - 11,843,224 inv. mtrt.ObjNode.GetObj
0.1% - 39,200 ms - 71,860 inv. mtrt.SphereObj.Intersect
0.1% - 29,202 ms - 648,025 inv. mtrt.Point.Combine
0.1% - 26,471 ms - 400,810 inv. mtrt.IntersectPt.SetIsectPt
0.1% - 26,441 ms - 648,025 inv. mtrt.Point.<init>
0.1% - 24,872 ms - 6,227,914 inv. mtrt.ObjectType.GetCachePt
0.0% - 20,765 ms - 5,856,349 inv. mtrt.CacheIntersectPt.GetID
0.0% - 18,507 ms - 5,615,310 inv. mtrt.IntersectPt.SetOrigID
0.0% - 18,344 ms - 5,856,349 inv. mtrt.Ray.GetID
0.0% - 17,363 ms - 5,367,450 inv. mtrt.OctNode.GetList
0.0% - 6,955 ms - 1,296,050 inv. mtrt.Ray.GetOrigin
0.0% - 3,242 ms - 1,064,985 inv. mtrt.IntersectPt.GetIntersection
0.0% - 2,440 ms - 648,025 inv. mtrt.Ray.GetDirection
0.0% - 47 ms - 48,198 inv. mtrt.IntersectPt.GetT
25.2% - 12,493,623 ms - 363,179 inv. mtrt.Scene.Shade
18.0% - 8,908,535 ms - 363,179 inv. mtrt.IntersectPt.FindNearestIsect
5.1% - 2,545,375 ms - 49,601 inv. mtrt.Scene.GetLightColor
0.4% - 214,918 ms - 363,179 inv. mtrt.OctNode.<init>
0.3% - 144,568 ms - 726,358 inv. mtrt.Ray.<init>
0.2% - 81,801 ms - 363,179 inv. mtrt.IntersectPt.<init>
0.0% - 10,864 ms - 1,089,537 inv. mtrt.Color.<init>
0.0% - 2,930 ms - 49,601 inv. mtrt.Color.Combine
0.0% - 2,751 ms - 363,179 inv. mtrt.Vector.<init>
0.0% - 1,060 ms - 49,113 inv. mtrt.PolyTypeObj.FindNormal
0.0% - 860 ms - 347,207 inv. mtrt.IntersectPt.GetIntersectObj
0.0% - 469 ms - 248,005 inv. mtrt.ObjectType.GetMaterial
0.0% - 168 ms - 99,202 inv. mtrt.Material.GetKTran
0.0% - 133 ms - 99,202 inv. mtrt.IntersectPt.GetIntersection
0.0% - 123 ms - 49,601 inv. mtrt.Material.GetEmissColor
0.0% - 93 ms - 488 inv. mtrt.SphereObj.FindNormal
0.0% - 75 ms - 49,601 inv. mtrt.Material.GetAmbColor
0.0% - 53 ms - 49,601 inv. mtrt.Material.GetShininess
1.3% - 653,231 ms - 648,025 inv. mtrt.OctNode.<init>
0.6% - 286,239 ms - 1,296,050 inv. mtrt.Ray.<init>
0.3% - 166,760 ms - 648,025 inv. mtrt.IntersectPt.<init>
0.1% - 30,786 ms - 363,179 inv. mtrt.Color.Combine
0.0% - 20,180 ms - 1,944,075 inv. mtrt.Color.<init>
0.0% - 15,297 ms - 3,631,790 inv. mtrt.IntersectPt.GetIntersectObj
0.0% - 14,064 ms - 363,179 inv. mtrt.Vector.Combine
0.0% - 12,493 ms - 363,179 inv. mtrt.Vector.Dot
0.0% - 9,152 ms - 360,493 inv. mtrt.PolyTypeObj.FindNormal
0.0% - 8,757 ms - 363,179 inv. mtrt.Color.Scale
0.0% - 8,563 ms - 2,905,432 inv. mtrt.ObjectType.GetMaterial
0.0% - 6,423 ms - 648,025 inv. mtrt.Vector.<init>
0.0% - 3,775 ms - 1,089,537 inv. mtrt.Ray.GetDirection
0.0% - 3,043 ms - 1,089,537 inv. mtrt.Material.GetKTran
0.0% - 2,582 ms - 1,089,537 inv. mtrt.IntersectPt.GetIntersection
0.0% - 2,077 ms - 726,358 inv. mtrt.Material.GetShininess
0.0% - 1,339 ms - 363,179 inv. mtrt.Ray.SetOrigin
0.0% - 1,335 ms - 363,179 inv. mtrt.Material.GetSpecColor
0.0% - 1,049 ms - 363,179 inv. mtrt.Ray.SetID
Abbildung 8.22.: Profilingdaten für den Aufrufbaum des mtrt-
Programms 225
KAPITEL 8. EVALUATION
8.3.3. Das Traveling Salesperson-Problem (TSP)
Das TSP-Programm von IBM wurde mit dem Branch-and-Bound-Verfahren
zur Berechnung der Wege implementiert. Außerdem wurde ein Load-
Balancierungsmechanismus bei der Abarbeitung der Routen der Worker-
Threads verwendet. Zur Lösung der Aufgabe werden 5 Worker-Threads ver-
wendet. 15 Städte wurden für die Routen-Berechnung aufgenommen. Für das
ganze Programm wurde ein tsp.BestTour-Objekt erzeugt, das die bisher
beste gefundene Lösung präsentiert. Ein Worker bekommt eine Tour von der
WorkQueue, ebenfalls ein Objekt vom Typ tsp.Tour, und fängt damit an
die Route zu berechnen. Der hier verwendete Algorithmus zur Lösung wurde
rekursiv implementiert: Wenn alle Städte einer Tour schon besucht wurden,
wird die berechnete Länge der Tour mit der bisher besten gefundenen Lösung
im BestTour-Objekt verglichen und ggf. die beste Tour aktualisiert. Sonst
werden neue Städte in die Tour aufgenommen und die Berechnung wird für
jeden der nächsten Kandidaten rekursiv durchgeführt.
3  )



*


1 1*
<-&&5  &5-
7 6
+(&&;
' )(&89
Abbildung 8.23.: Verteilungsergebnis für TSP
Aufgrund dieser Implementierungsstruktur wurde die zum eigentlichen Lö-
sen des Problems zuständige Worker.solve(Tour t)-Methode bei jedem
Worker-Thread in der sequentiellen Ausführung durchschnittlich 137786 mal,
auf der fireball-Maschine 121116 mal und im verteilten Fall (auf 2 und 5 Rech-
nern) 254143 mal rekursiv ausgeführt. Dies entspricht also der Rekursionstie-
fe. Für die verteilte Ausführung wurden die gleichen Strategien angewandt,
die bei der Evaluation des mtrt-Programms zum Einsatz kamen. Hiermit wur-
den die in dieser Methode erzeugten Tour-Objekte repliziert. Trotz der An-
wendung der geschickten Strategien, welche eine positive Auswirkung auf
226
8.3. EMPIRISCHE UNTERSUCHUNGEN ZUR PERFORMANZ DER
VERTEILTEN ANWENDUNGEN
die Laufzeit bei anderen Programmen aufzeigen, wirkt die verteilte Ausfüh-
rung in diesem Fall sehr schlecht, wie die Messergebnisse in Abbildung 8.23
zeigen. Zum Vergleich mit der Ausführungsdauer im sequentiellen Fall von
1.02s sowie auf der fireball-Maschine von 0.46s ist die verteilte Ausführung
auf 2 und 5 Rechnern viel zu langsam. Wie man wohl am Balkendiagramm
erkennt, wird diese Geschwindigkeitsverlangsamung durch die entstehende
entfernte Kommunikation verursacht. Das sind entfernte Methodenaufrufe,
die die fünf Worker auf das tsp.BestTour-Objekt durchführten. Diese do-
minieren fast die gesamte Laufzeit des Programms.
3  )






+(&)&+ ) )&8&&5 9 + ) )
+(&&;
+(&&)&$ ) )
Abbildung 8.24.: Anzahl der entfernten Methodenaufrufe jedes Worker-
Threads
Abbildung 8.24 zeigt uns nochmals die Anzahl der entfernten Methodenauf-
rufe bei jedem Worker-Thread. Die Ausführungsdauer der Methodenaufrufe
im sequentiellen Fall sowie in der Mehrprozessor-Maschine kostet fast keine
Zeit, während ein remote-Aufruf im verteilten Fall sehr teuer ist. Daher ist es
sehr ungünstig, dieses Programm mit diesem Konzept verteilt auszuführen.
Mit diesem Beispiel wird nochmals bestätigt, dass viel entfernte Kommuni-
kation die gesamte Laufzeit eines verteilten Programms sehr beeinträchtigt.
Es gibt andere Systeme wie cJVM von IBM [AFT99], [AFT+00] und Hyperion
[A+01], die eine eigene JVM implementieren (siehe Abschnitt 4.1.5) und sehr
schnelle Kommunikation unterstützen (nicht über RMI). Zur Verteilung der
Objekte wird hierbei neben der Technik der Lastbalancierung ebenfalls auch
die Last der Rechner-Knoten zur Laufzeit mit berücksichtigt. So konnte für
das TSP-Problem mit 14 Städten bei 4 Threads unter cJVM ein Speedup von
3 erreicht werden. Der verwendete Cluster ist eine Gruppe von IBM Intelli-
stations, die untereinander via Myrinet verbunden sind.
227
KAPITEL 8. EVALUATION
8.4. Die Erkenntnisse
Durch die empirische Untersuchung von drei Programmen aus drei verschie-
denen Kategorien kann man die Erkenntnisse gewinnen, dass unter dem
Einsatz der geschickten Verteilungsstrategien viel Performanz erzielt werden
kann. Dies wurde durch die Messergebnisse bei der Grocery-Simulation und
dem mtrt-Programm bestätigt. Der Vergleich zwischen der zyklischen Vertei-
lung und einem Verteilungsplan mit intelligenten Strategien zeigt ganz deut-
lich den entscheidenden Beitrag der automatisch eingehenden Analysetech-
niken. Neben den verwendeten statischen Analysetechniken zur Erkennung
von Lokalitätseigenschaften der entstehenden Objekte können mittels der
stagewise-immutable Eigenschaft viele zusätzliche Objekte repliziert werden,
was zur Performanzsteigerung beiträgt. Für diesen Zweck wurde auch die
neuartige statische stagewise-immutable Analyse entworfen. Dies ist ein sehr
aufwändiges Analyseverfahren, wird aber vor der Laufzeit angewandt und
hat deshalb keine negative Wirkung auf die Laufzeit des Programms. Zur Ver-
besserung der ungenauen statischen Abschätzung der Workload sowie der
Kommunikation wurde der hybride Ansatz zur Kombination von Laufzeit-
daten mit statischen Analyseergebnissen in einem Beispiel erfolgreich evalu-
iert.
Außerdem wurde gezeigt, wie wichtig die Reihenfolge zur Anwendung der
verschiedenen Strategien innerhalb eines Verteilungsplans ist. So konnten
Programmeigenschaften berechnet werden, die durch Anwendung vorheri-
ger Strategien nicht entdeckt werden konnten. Die automatische Verteilung
mit geschickten Strategien funktioniert im Allgemeinen nur gut für mehr-
strängige Anwendungsprogramme, die insgesamt sehr viel aufwändige Be-
rechnungen durchführen, aber wenig miteinander kommunizieren. Beispiele
dafür sind diskrete ereignisbasierte Simulationen und Programme mit regulä-
ren Strukturen wie das mtrt-Programm, wobei innerhalb eines Threads auch
viel lokale Datenobjekte zur Eigenberechnung erzeugt werden können. Der
nicht zu vernachlässigende Aspekt dabei ist der Speicherverbrauch: Wenn ein
Rechner sehr wenig Speicher zur Verfügung hat, können Anwendungen mit
sehr hohem Speicherbedarf darauf nicht ausgeführt werden oder sie laufen
wegen des notwendigen Swappings sehr langsam. Dieses Problem kann gut
mit der automatischen Verteilung auf mehreren Rechnern gelöst werden. Ein
weiterer Vorteil der automatischen Verteilung ist auch die Skalierbarkeit, die
bei Mehrprozessor-Maschinen sehr beschränkt ist.
228
8.4. DIE ERKENNTNISSE
Der Einsatz des JScatter-Systems und die verteilte Ausführung der untersuch-
ten Programme auf dem normalen PC-Cluster und auf dem Arminius-Cluster
des PC2zeigten, wie einfach es ist, das System auf unterschiedlichen Cluster-
Architekturen zu benutzen. Die Messergebnisse auf dem Cluster des PC2be-
stätigen, dass nicht nur durch die geschickt angewandten Strategien, son-
dern auch durch hohe Rechenleistung mehr Geschwindigkeit erreicht wer-
den kann. Die Messergebnisse zeigten, dass die verteilte Ausführung irregu-
lärer Anwendungen wie der Grocery-Simulation auf dem Arminius-Cluster
keine sehr gute Performanz erreichte. Das liegt daran, dass die Charakteristi-
ken solcher diskreten ereignisbasierten Simulationen nicht typisch für High-
Performance-Computing sind. Im Gegensatz dazu konnte sehr viel Performanz
beim mtrt-Raytracer auf dem Cluster des PC2erzielt werden: Mit den ge-
schickten Verteilungsstrategien, mehr Rechenleistung und schnellerer Verbin-
dung konnte dieses Programm bis auf einen Faktor 3 schneller verteilt ausge-
führt werden, als die verteilte Ausführung auf einem normalen Pool-Cluster.
Speziell beim TSP-Programm und allgemein bei Programmen mit viel Kom-
munikationen lohnt sich die verteilte Ausführung nur, wenn entfernte
Kommunikationskosten sehr gering sind. Für einen Fall wie bei TSP kann
mein Konzept wie folgt erweitert werden: Zuerst ist mittels Programm-
analyse herauszufinden, ob im Eingabeprogramm die Kommunikationsan-
teile erheblich größer als Rechenanteile sind. Für die Programmanalyse ist
es technisch möglich, solche Abschätzungen vor der Laufzeit durchzuführen,
insbesondere bei rekursiven Strukturen schärfere Schranken zur Abschätzung
zu verwenden. Wenn dies der Fall ist, dann wird entschieden, das Programm
oder Teile davon besser nicht verteilt auf den zur Verfügung stehenden JVMs
auszuführen, sondern nur in einer JVM lokal laufen zu lassen.
Zur Evaluationszeit wurden die Ergebnisse einiger Analysen, wie die
stagewise-immutable Analyse und die hybride Analyse, noch nicht automa-
tisch direkt zur Berechnung der Verteilungsentscheidung integriert, sondern
manuell eingebunden. Dies erfordert noch einiges an Implementierung im
JScatter-System. Trotzdem wurde das Konzept zur automatischen Verteilung,
insbesondere mit der stagewise-immutable Eigenschaft und der hybriden Ana-
lyse, durch die empirische Untersuchung erfolgreich evaluiert.
229
.
9. Zusammenfassung
Inhalt
9.1. Wichtige Aspekte in Kürze . . . . . . . . . . . . . . . . . . . . . 232
9.2. Ausblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
231
KAPITEL 9. ZUSAMMENFASSUNG
In dieser Arbeit habe ich ein Konzept zur automatischen Verteilung mehr-
strängig formulierter Java-Anwendungen repräsentiert. Über einen Vertei-
lungsplan können alle strategischen Entscheidungen über Transformation
und Verteilung der Objekte getroffen werden. Mit automatisch eingehenden
Programmanalysen können die für die Verteilung relevanten Programmei-
genschaften bestimmt werden. Sie dienen als Grundlage der Verteilungsbe-
rechnung vor der Laufzeit und zur Laufzeit. Insbesondere zählen hierzu die
stagewise-immutable Eigenschaft zur Objektreplikation und die Anwendung
der hybriden Analyse. Das ist eine Kombination statischer Analyseergeb-
nisse mit dynamischer Analyse. Sie wird zur Verbesserung der Workload-
Eigenschaft und somit der Verteilungsentscheidung zukünftig zu erzeugen-
der Objekte verwendet. Im Folgenden werden die Kernpunkte des gesamten
Konzepts zusammengefasst.
9.1. Wichtige Aspekte in Kürze
Verteilungsplan-Konzept Ein Verteilungsplan liefert vor der Laufzeit die
Entscheidung über die Transformation der Erzeugungsstellen und berechnet
für als remote gekennzeichnete Objektrepräsentanten initiale Platzierungsent-
scheidungen. Zur Laufzeit wird mittels eines Planes die Entscheidung zur
Verteilung unmittelbar vor der Erzeugung getroffen. Mit diesem Konzept
können verschiedene in einem Plan verwendete Strategien zur geschickten
Verteilung der Objekte unabhängig von der Anwendung formuliert werden.
So werden allgemeine Programmeigenschaften mittels der Programmanaly-
sen ermittelt, die innerhalb spezieller Objektverteilungsfunktionen (ODFs)
zur Verteilungsberechnung verwendet werden. Bestimmte ODFs zusammen
bilden eine Strategie. Mit diesem Modell hat man den Vorteil, neue ODFs for-
mulieren zu können, verschiedene Varianten der Strategien anwenden oder
neue entwickeln zu können und somit unterschiedliche Pläne zur Vertei-
lung eines Programms einsetzen zu können. Durch die Modellierung der Pro-
grammeigenschaften und deren Verwendung in den ODFs wird die eigentli-
che Programmanalyse von der Berechnung der Verteilungsfunktion getrennt.
So können durch Einsatz spezieller Analysetechniken Analyseergebnisse zur
Verteilung verbessert werden.
Die stagewise-immutable Eigenschaft und hybride Analyse Zur Objek-
treplikation habe ich eine neuartige Analyse entwickelt, die die stagewise-
232
9.1. WICHTIGE ASPEKTE IN KÜRZE
immutable Eigenschaft der Objekte vor der Laufzeit erkennt. Pure-immutable
Objekte können direkt nach ihrer Initialisierung repliziert werden. Postponed-
immutable Objekte werden nach der Initialisierungsphase noch geschrieben,
aber ab einem späteren Zeitpunkt zur Laufzeit nur noch gelesen. Sie können
dann auch repliziert werden. Nach der Replikation finden alle solche Zugriffe
dann lokal statt, so dass viel Kommunikationsaufwand gespart werden kann,
wie dies bei der empirischen Untersuchung gezeigt wurde.
Zur Verbesserung der unpräzisen Abschätzung der Workload sowie der
Kommunikationskosten vor der Laufzeit wurde der Ansatz zur hybriden
Analyse erprobt. Hierbei wurden die Laufzeitdaten mittels dynamischer Ana-
lyse mit den statischen Analyseergebnissen kombiniert. Dadurch konnten die
tatsächlichen verwendeten Grenzwerte der äquivalenten Schleifen berechnet
werden, was zur genaueren Workload-Berechnung führt. Somit konnten Ver-
teilungsentscheidungen verbessert werden, die dann zur Platzierung zukünf-
tig zu erzeugender Objekte benutzt werden. Der Vorteil des hybriden An-
satzes gegenüber nur dynamischer Analyse liegt darin, dass vor der Lauf-
zeit die statische Struktur, insbesondere die interprozedurale Information des
Programms zur Verfügung steht. Der Beschaffungsaufwand statischer Ergeb-
nisse hat keinen Einfluss auf die Laufzeit des Programms. Die dynamische
Analyse wird gezielt eingesetzt, um die noch fehlenden Laufzeitdaten zu er-
mitteln.
Ergebnisse Die vorgestellten Konzepte wurden prototypisch im JScatter-
System sowie die Analysen in der Analyseumgebung PAULI realisiert. Sie
wurden dann auf 8 Anwendungsprogramme und Benchmarks angewandt,
um die stagewise-immutable Eigenschaft zu evaluieren. Das Konzept zur auto-
matischen Verteilung wurde auf 3 Programmen aus drei verschiedenen Ka-
tegorien empirisch erprobt. Als komplettes (vollständiges) Programm konn-
ten sehr viele Erzeugungsstellen mit stagewise-immutable, insbesondere der
pure-immutable Eigenschaft, entdeckt werden. So besitzen etwa 44% der NEW-
Stellen bei allen 8 untersuchten Anwendungen die pure-immutable und die
postponed-immutable Eigenschaft. Dadurch können bis zu 15524 (etwa 6,97%
aller statisch gezählten) Lesezugriffe nach der Replikation lokal werden, so
dass erheblich viel entfernter Kommunikationsaufwand reduziert werden
kann.
Bei der Untersuchung der Verteilungsgüte konnte gezeigt werden, dass viel
Performanz unter dem Einsatz der geschickten Verteilungsstrategien erzielt
233
KAPITEL 9. ZUSAMMENFASSUNG
wurde. Bei der verteilten Ausführung auf 9 Rechnern einer diskreten er-
eignisbasierten Simulation mit Replikation der stagewise-immutable Objekte
konnte ein Speedup von etwa 3.8 erreicht werden. Außerdem wurde dabei
auch erkannt, dass die automatische Verteilung mit intelligenten Strategien
im Allgemeinen nur gut für Anwendungen funktioniert, die insgesamt viel
aufwändige Berechnungen durchführen, aber wenig Kommunikation verur-
sachen. Dies wurde deutlich durch Messungen am mtrt-Programm gezeigt:
Durch den künstlich eingefügten Workload sowie Kommunikationsoptimie-
rung konnte ein Geschwindigkeitsgewinn bei der verteilten Ausführung er-
zielt werden. Messergebnisse der TSP-Anwendung, die über 98% der gesam-
ten Laufzeit mit Kommunikation verbringt, zeigten auch deutlich, dass man
durch die verteilte Ausführung derartiger Anwendungsprogramme mit dem
JScatter-System auf PC-Cluster mit Standard-Ethernet-Vernetzung keine gute
Performanz erreichen kann.
Durch viel Rechenleistung und schnellere Kommunikation des Arminius-
Clusters im PC2konnten die Programme mit geschickten Strategien bis auf
einen Faktor 3 (über Ethernet-Gbit-Verbindung) schneller verteilt ausgeführt
werden, als die verteilte Ausführung auf einem normalen Pool-Cluster.
Zusammenfassung Mit diesem Konzept kann ein mehrsträngiges Java-
Programm benutzertransparent automatisch verteilt werden. Alle notwen-
digen und aufwändigen Schritte, die ein Programmierer manuell durchfüh-
ren muss, um ein sequentielles multithreaded Java-Programm in eine verteil-
te Version mit RMI umzuschreiben und diese auf mehreren Rechnern ver-
teilt auszuführen, werden automatisiert. Durch die geschickten Verteilungs-
strategien mit automatisch eingehender Programmanalyse werden die Ob-
jekte automatisch auf die beteiligten JVMs platziert, so dass der Benutzer
sich nicht darum kümmern muss. Hierbei erhält man eine gewünschte Ver-
teilung der Programmobjekte, durch die für bestimmte Anwendungsgebiete
eine Geschwindigkeitssteigerung mittels der verteilten Ausführung auf meh-
reren Rechnern erzielt wird. Da das JScatter-Systems vollständig in Java reali-
siert ist, kann man es plattformunabhängig verwenden. Dies wurde gezeigt,
indem die Evaluation nicht nur auf einem Cluster von Standard-PCs, sondern
auch auf dem Arminius-Cluster des Paderborn Center for Parallel Computing
(PC2) durchgeführt wurde. Außerdem stehen einem durch verteilte Ausfüh-
rung auf einem Cluster auch automatisch mehr Ressourcen wie z.B. Speicher
zur Verfügung.
234
9.2. AUSBLICK
Dieser Ansatz kann allerdings nicht mit denjenigen konkurrieren, bei denen
ein Experte eine spezielle Lösung für den im Programm verwendeten Al-
gorithmus programmiert und danach die Objekte verteilt. Hierbei kann viel
mehr Performanz erreicht werden. Der Vorteil meines Ansatzes ist aber, dass
alles vollständig automatisch durchgeführt wird. Außerdem erhält man au-
tomatisch die Skalierbarkeit, die Mehrprozessor-Maschinen nicht zur Verfü-
gung stellen können. Der Benutzer braucht für die verteilte Ausführung nur
noch die Rechner zu bestimmen, die sich an der parallelen Lösung des Pro-
blems beteiligen. Außerdem kann bei bestimmten Anwendungen (TSP) keine
gute Performanz mit JScatter-System erreicht werden, wie mit anderen Sys-
temen (cJVM von IBM Research Laboratory in Haifa [AFT99], [AFT+00]).
Das liegt daran, dass bei JScatter die verteilte Laufzeitumgebung aus den
Standard-JVMs entsteht und RMI als Kommunikationsplattform verwendet
wird. Alle Komponenten des JScatter-Systems sind also in Java implemen-
tiert. cJVM ist dagegen eine speziell implementierte JVM, die einen Cluster
virtualisiert, die Load-Balancierungstechnik zur Objektverteilung verwendet
und ein anderes Objekt- sowie Speichermodell benutzt. Außerdem kommuni-
zieren die Rechner miteinander über eine sehr schnelle Verbindung mit einer
Latenz im Bereich von Microsekunden.
9.2. Ausblick
Neben den Vorschlägen zur Erweiterung des Ansatzes zur hybriden Analyse,
die im Abschnitt 6.3.3 beschrieben wurden, kann ein weiteres Konzept dazu
untersucht werden:
Verfeinerung statischer Analyseergebnisse durch dynamische Analyse
Mittels der Objektgraph-Analyse [Spi99b] kann die Laufzeitstruktur eines
Programms statisch modelliert werden. Die Knoten im Graphen repräsentie-
ren die zur Laufzeit entstehenden Objekte und die Kanten die Beziehungen
zwischen ihnen. Die interessanten Kanten im Objektgraphen sind diejenigen,
bei denen jeder der Endknoten mehrere Laufzeitobjekte darstellt, wie das in
Abbildung 9.1 gezeigt wird.
Es gibt einen gemeinsamen Knoten für die Instanzen der Klasse MyThread
und einen Knoten für die Vector-Objekte. Diese beiden Knoten werden aber
durch eine einzige Kante miteinander verbunden. Man kann statisch nicht be-
stimmen, wie die Beziehung zwischen den 2n Objekten genau aussieht. Ziel
235
KAPITEL 9. ZUSAMMENFASSUNG
for (int i = 0;i < n;i++)
MyThread t =
new MyThread(new Vector());
n MyThread−Objekte
n Vector−Objekte
Abbildung 9.1.: n:n-Beziehung zwischen Laufzeitobjekten im Objektgra-
phen
ist es, genau die 1:1-Beziehung für diese nPaare von Laufzeitobjekten festzu-
stellen. Dadurch erhält man die Information, dass zur Laufzeit jedes Vector-
Objekt genau einem MyThread-Objekt zugeordnet wird. Somit ist keine Syn-
chronisation beim Zugriff auf die Vector-Objekte nötig.
Als Lösung kann eine statische Analyse entwickelt werden, die auf einem Ob-
jektgraphen die Knotenpaare nach obigem Muster erkennt und die betroffe-
nen Programmstellen markiert. An solchen markierten Stellen kann eine dy-
namische Analyse das Programmverhalten beobachten und eliminiert über-
flüssige Thread-Synchronisation, wenn 1:1-Beziehungen zwischen den Lauf-
zeitobjekten vorliegen.
Beseitigung unnötiger Synchronisation von stagewise-immutable Ob-
jekten Eine weitere Optimierungsanwendung für Objekte mit stagewise-
immutable Eigenschaft ist das Entfernen unnötiger Thread-Synchronisation.
Dies kann man für pure-immutable Objekte direkt nach ihrer Initialisierung an-
wenden. Für postponed-immutable Objekte kann die Beseitigung der Synchro-
nisation an geeigneten Stellen nach einem guten Schnitt durchgeführt wer-
den.
Analyse zur Erkennung anwendungsspezifischer Muster Zu der als
Peer zusammengefassten Programmeigenschaft gehören verschiedene ele-
mentare Eigenschaften, die jeweils ein anwendungsspezifisches Muster dar-
stellen. Einige Beispiele dafür sind Muster zur Benutzerinteraktion mittels
Eingabe und Ausgabe, Muster zur Benutzung von speziellen Hardware-
Ressourcen wie Hardware- oder Grafik-Beschleuniger etc. So können spezi-
elle Analysetechniken dafür gezielt eingesetzt werden, um solche Muster im
Programm vor der Laufzeit zu entdecken. Bei der automatischen Verteilung
können Objekte, die solche Muster enthalten, nur dann dort erzeugt werden,
wo die benötigten Ressourcen benutzt werden.
236
9.2. AUSBLICK
Im Prinzip stellt die hybride Analyse eine ideale Ausgewogenheit der stati-
schen und dynamischen Analyse dar. Damit können die Vorteile der beiden
Welten genutzt werden, um die Eigenschaften des Programms so genau wie
möglich zu berechnen. Außerdem kann durch Hinzunahme externer Infor-
mation wie z.B. der aktuellen CPU-Load in die Verteilungsentscheidung mehr
Performanz durch eine verteilte Ausführung erzielt werden.
237
.
Anhang
239
A. Verzeichnisse
241
.
Literaturverzeichnis
[A+01] G. Antoniu et al. The Hyperion System: Compiling Multi-
threaded Java Bytecode for Distributed Execution. Parallel Com-
puting, 27(10):1279–1297, September 2001.
[AB] G. Antoniu and L. Bougé. Implementing Multithreaded Proto-
cols for Release Consistency on Top of the Generic DSM-PM Plat-
form. Lecture Notes in Computer Science, 2326.
[AB01] G. Antoniu and L. Bouge. DSM-PM2: A Portable Implementa-
tion Platform for Multithreaded DSM Consistency Protocols. In
Proceedings of the 15th International Parallel & Distributed Proces-
sing Symposium (IPDPS-01), pages 104–104, Los Alamitos, CA,
April 23–27 2001. IEEE Computer Society.
[ABH+a] G. Antoniu, L. Bougé, P. Hatcher, M. MacBeth, K. McGuigan,
and R. Namyst. Compiling Multithreaded Java Bytecode for
Distributed Execution (Distinguished Paper). Lecture Notes in
Computer Science, 1900.
[ABH+b] G. Antoniu, L. Bougé, P. Hatcher, M. MacBeth, K. McGuigan, and
R. Namyst. Implementing Java Consistency Using a Generic,
Multithreaded DSM Runtime System. Lecture Notes in Computer
Science, 1800.
[ACG00] I. Attali, D. Caromel, and R. Guider. A Step Toward Automatic
Distribution of Java Programs, 2000. FMOODS 2000: 141-162.
[AFT99] Y. Aridor, M. Factor, and A. Teperman. cJVM: A Single System
Image of a JVM on a Cluster. In International Conference on Parallel
Processing, pages 4–11, 1999.
243
LITERATURVERZEICHNIS
[AFT+00] Y. Aridor, M. Factor, A. Teperman, T. Eilam, and A. Schuster. A
High Performance Cluster JVM Presenting a pure Single System
Image. In Java Grande, pages 168–177, 2000.
[AH01] G. Antoniu and P. Hatcher. Remote Object Detection in Cluster-
Based Java. In Proceedings of the 15th International Parallel and
Distributed Processing Symposium (IPDPS-01), pages 108–108, Los
Alamitos, CA, April 23–27 2001. IEEE Computer Society.
[Ant] INRIA Sophia Antipolis. ProActive PDC - Parallel, Distributed
and Concurrent Computing in Java with Mobility. http://
www-sop.inria.fr/sloop/javall.
[Ass] InfiniBand Trade Association. InfiniBand HCA. http://www.
infinibandta.org/home.
[B. 00] B. Joy and G. Steele and J. Gosling and G. Bracha. The Java Lan-
guage Specification. Addison-Wesley, 2nd edition edition, 2000.
[BBH+96] Henri E. Bal, R. Bhoedjang, R. Hofman, C. Jacobs, K. Langen-
doen, T. Rühl, and M. F. Kaashoek. Orca: a Portable User-Level
Shared Object System. Technical Report IR-408, Vrije University,
Amsterdam, June 1996.
[BBH+98] H. E. Bal, R. Bhoedjang, R. Hofman, C. Jacobs, K. Langendoen,
T. Rühl, and M. F. Kaashoek. Performance Evaluation of the Orca
Shared-Object System. ACM Transactions on Computer Systems,
16(1):1–40, February 1998.
[BCCH95] M. Burke, P. Carini, J.-D. Choi, and M. Hind. Flow-Insensitive
Interprocedural Alias Analysis in the Pressence of Pointers. In
David Gelertner, Alexandru Nicolau, and David Padua, editors,
Lecture Notes in Computer Science, 892. Springer-Verlag, 1995.
[BCFK] M. Baker, B. Carpenter, G. Fox, and S. H. Koo. mpiJava: An
Object-Oriented Java Interface to MPI. Lecture Notes in Computer
Science, 1586.
[BCP96] B. Blount, S. Chatterjee, and M. Philippsen. Irregular Parallel
Algorithms in Java. Technical Report IR-408, Vrije University,
Amsterdam, June 1996.
244
LITERATURVERZEICHNIS
[BG97] A. J. C. Bik and D. B. Gannon. Automatically Exploiting implicit
Parallelism in Java. j-CPE, 9(6):579–619, June 1997.
[BG98] A. J. C. Bik and D. B. Gannon. A Prototype Bytecode Paralleliza-
tion Tool. Concurrency: Practice and Experience, 10(11–13):879–885,
September 1998. Special Issue: Java for High-performance Net-
work Computing.
[BH99] J. Bogda and U. Hölzle. Removing unnecessary Synchronization
in Java. ACM SIGPLAN Notices, 34(10):35–46, 1999.
[BK93] H. E. Bal and M. F. Kaashoek. Object Distribution in Orca using
Compile-time and Run-time Techniques. In Andreas Paepcke,
editor, Proceedings of the Conference on Object-Oriented Program-
ming Systems, Languages, and Applications (OOPSLA), volume 28,
pages 162–177, New York, NY, 1993. ACM Press.
[BLS97] G. Brose, K. P. Löhr, and A. Spiegel. Java Does not Distribute.
Technical Report B-97-07, Freie Universität Berlin, October 1997.
[Bor02] J. Borrmann. Replikation statischer Konstanten in JavaParty.
Technical report, Institut für Programmstrukturen und Datenor-
ganisation, Universität Karlsruhe. Studienarbeit, February 2002.
[Cen] The Edinburgh Parallel Computing Centre. The Java Grande Fo-
rum Multi-threaded Benchmarks. http://www.epcc.ed.ac.
uk/computing/research_activities/java_grande/.
[CGS+99] J.-D. Choi, M. Gupta, M. J. Serrano, V. C. Sreedhar, and S. P. Mid-
kiff. Escape Analysis for Java. In Proceedings of the Conference on
Object-Oriented Programming Systems, Languages, and Applications
(OOPSLA), pages 1–19, 1999.
[CJR92] R. Clark, D.E. Jensen, and F.D. Reynolds. An Architectural Over-
view of the Alpha Real-Time Distributed Kernel. In Proceedings
of the USENIX Workshop on Microkernel and Other Kernel Architec-
tures, April 1992.
[CKV98] D. Caromel, W. Klauser, and J. Vayssière. Towards Seamless
Computing and Metacomputing in Java. Concurrency: Practice
and Experience, 10(11–13):1043–1061, September 1998. Special Is-
sue: Java for High-performance Network Computing.
245
LITERATURVERZEICHNIS
[Cor] The Standard Performance Evaluation Corporation. SPEC
JVM98. http://www.spec.org/osg/jvm98.
[CZF+98] B. Carpenter, G. Zhang, G. Fox, X. Li, and Y. Wen. HPJava: Data
Parallel Extensions to Java. Concurrency: Practice and Experience,
10(11–13):873–877, September 1998. Special Issue: Java for High-
performance Network Computing.
[Daha] M. Dahm. BCEL: The Byte Code Engineering Library. http:
//bcel.sourceforge.net.
[Dahb] M. Dahm. Doorastha A Step towards Distribution Transparen-
cy. http://citeseer.nj.nec.com/dahm00doorastha.
html.
[Dah00] M. Dahm. The Doorastha System. Technical report B-1-2000,
Freie Universität Berlin, 2000.
[Dow98] T. B. Downing. Java RMI: Remote Method Invocation. IDG Books,
San Mateo, CA, USA, January 1998.
[DPW01] T. Dowling, J. Power, and J. Waldron. Relating Static and Dyna-
mic Measurements for the Java Virtual Machine Instruction Set.
In Symposium on Mathematical Methods and Computational Techni-
ques in Electronic Engineering, Athens, Greece, December 29-31
2001.
[DS84] L. P. Deutsch and A. M. Schiffman. Efficient Implementation
of the Smalltalk-80 System. In Conference Record of the Eleventh
Annual ACM Symposium on Principles of Programming Languages,
pages 297–302, Salt Lake City, Utah, 1984.
[FFF+02] M. Faust, M. Freund, J. Friedrich, M. Stiller, M. Weißenborn,
and H. Wessels. Abschlussbericht der Projektgruppe Verteilung
von parallelen Java-Anwendungen. Abschlussbericht, Novem-
ber 2002.
[FHA99] E. F., S. Hupfer, and K. Arnold. JavaSpaces Principles, Patterns, and
Practice. Addison-Wesley, June 1999.
[Gmb] EJ-Technologies GmbH. JProfiler. http://www.
ej-technologies.com/products/jprofiler/
overview.html.
246
LITERATURVERZEICHNIS
[Hau98] B. Haumacher. Lokalitätsoptimierung durch statische Typanaly-
se in JavaParty. Master’s thesis, Institut für Programmstrukturen
und Datenorganisation, Universität Karlsruhe, 1998.
[HBCC99] M. Hind, M. Burke, P. Carini, and J.-D. Choi. Interprocedural
Pointer Alias Analysis. ACM Transactions on Programming Lan-
guages and Systems, 21(4):848–894, 1999.
[HCU91] U. Hölzle, C. Chambers, and D. Ungar. Optimizing
Dynamically-Typed Object-Oriented Languages with Polymor-
phic Inline Caches, 1991.
[HD04] C. Herder and J. J. Dujmovi´c. Workload Characterization Using
Metrics Based on Instruction Grouping. International Journal of
Computer and Information Science (IJCIS), Vol. 5, No. 1, Mar 2004.
[HL98] D. Hagimont and D. Louvegnies. Javanaise: Distributed Shared
Objects for Internet Cooperative Applications. In Middleware’98,
The Lake District, England, 1998.
[HMP] B. Haumacher, T. Moschny, and M. Philippsen. KaRMI: Effi-
cient RMI for Java. http://www.ipd.uka.de/JavaParty/
KaRMI/index.html.
[HMRT03] B. Haumacher, T. Moschny, J. Reuter, and W. F. Tichy. Transpa-
rent Distributed Threads for Java. In Proceedings of 5th Interna-
tional Workshop on Java For Parallel and Distributed Computing (in
conjunction with JPDPS 2003), April 2003.
[Hoe] J. Hoenicke. JODE - Java Optimize and Decompile Environment.
http://jode.sourceforge.net/.
[HP99] B. Haumacher and M. Philippsen. More Efficient Object Seriali-
zation. Parallel and Distributed Processing, (number 1586 in Lec-
ture Notes in Computer Science):718–732, Puerto Rico, April 12
1999.
[HP01] B. Haumacher and M. Philippsen. Exploiting Object Locality in
JavaParty, a Distributed Computing Environment for Worksta-
tion Clusters. In 9th Workshop on Compiler for Parallel Computers
(CPC, Edingburgh, Scotland,UK 2001), 27-29 Juni 2001.
247
LITERATURVERZEICHNIS
[HRP] B. Haumacher, J. Reuter, and M. Philippsen. JavaParty: A dis-
tributed Companion to Java. http://wwwipd.ira.uka.de/
JavaParty.
[Höl94] U. Hölzle. Adaptive Optimization for Self: Reconciling High
Performance with Exploratory Programming, 1994.
[KLK02] K. Klohs, D. K. Le, and U. Kastens. Automatic Distribution of
Multithreaded Java Programs using Distribution Plans. Techni-
scher Bericht, Reihe Informatik tr-ri-02-234, Universität Pader-
born - Institut für Informatik, 2002.
[Klo02] K. Klohs. Analyse nebenläufiger Java-Programme zur Berech-
nung von Verteilungsstrategien. Master’s thesis, Fachbereich 17,
Mathematik-Informatik, Universität Paderborn, 2002.
[Le00] D. K. Le. Referenzziel-Analyse für Java-Bibliotheken und Wie-
derverwendung ihrer Ergebnisse. Master’s thesis, Universität
Paderborn, 2000.
[LP97] P. Launay and J.-L. Pazat. A Framework for Parallel Program-
ming in Java. Technical Report 1154, IRISA, Intitut de Recherche
en Informatique et Systèmes Aléatoires, Dezember 1997.
[LP98] P. Launay and J.-L Pazat. Generation of Distributed Parallel Java
Programs. Lecture Notes in Computer Science, 1470:729–32, Sep-
tember 1998.
[Mic] Sun Microsystems. Java RMI. http://java.sun.com/
products/jdk/rmi/.
[Mic98] Sun Microsystems. Java Remote Method Invocation Distribu-
ted Computing for Java. White Paper, March 1998.
[MKB00] J. Maassen, T. Kielmann, and H. E. Bal. Efficient Replicated Me-
thod Invocation in Java. In Java Grande, pages 88–96, 2000.
[MKB01] J. Maassen, T. Kielmann, and H. E. Bal. Parallel Application
Experience with Replicated Method Invocation. Concurrency and
Computation: Practice and Experience, 13(8–9):681–712, 2001.
[MPA99] Sun Microsystems and CA P. Alto. Java Remote Method Invocation:
Specification. December 1999.
248
LITERATURVERZEICHNIS
[Muc97] S. S. Muchnick. Compiler Design and Implementation. Morgan
Kaufmann Publishers, Inc., 1997.
[MWL00] M. J. M. Ma, C.-L. Wang, and F. C. M. Lau. JESSICA: Java-
Enabled Single-System-Image Computing Architecture. Journal
of Parallel and Distributed Computing, 60(10):1194–1222, 2000.
[MZ96] T. Mowbray and R. Zahavi. The Essential CORBA: Systems Inter-
gration Using Distributed Objects. Wiley, 1996.
[NS96] N. Nagaratnam and A. Srinivasan. Remote objects in Java, 1996.
In IASTED International Conference on Networks, 1996.
[OW99] S. Oaks and H. Wong. Java Threads. Oreilly, 2nd edition edition,
1999.
[P. 97] P. Orbek. Trust and Dependence Analysis. PhD thesis, 1997. Uni-
versity of Aarhus, Denmark.
[P. 01] P. W. Sathyanathan. Interprocedural Dataflow Analysis - Alias Ana-
lysis. PhD thesis, 2001. Stanford University’s Computer Science
Department, USA.
[Pad] Universität Paderborn. The Paderborn Center for Parallel Com-
puting, PC2.http://wwwcs.uni-paderborn.de/pc2/.
[PH00] M. Philippsen and B. Haumacher. Locality Optimization in Java-
Party by means of static Type Analysis. Concurrency: Practice and
Experience, 2000.
[Phi98] M. Philippsen. Data Parallelism in Java. In J. Schaefer, editor,
High Performance Computing Systems and Applications, pages 85–
99. Kluwer Academic Publishers, Boston, Dordrecht, London,
1998.
[PS91] J. Palsberg and M. I. Schwartzbach. Object-Oriented Type In-
ference. In Proceedings of the OOPSLA ’91 Conference on Object-
oriented Programming Systems, Languages and Applications, pages
146–161, November 1991. Published as ACM SIGPLAN Notices,
volume 26, number 11.
249
LITERATURVERZEICHNIS
[PZ97] M. Philippsen and M. Zenger. JavaParty Transparent Remote
Objects in Java. Concurrency: Practice and Experience, 9(11):1225–
1242, November 1997. Special Issue: Java for computational
science and engineering simulation and modeling II.
[Rin01a] M. C. Rinard. Analysis of Multithreaded Programs. In Procee-
dings of the 8th Static Analysis Symposium (Paris, France), July 2001.
[Rin01b] M. C. Rinard. Analysis of Multithreaded Programs. In Static
Analysis Symposium, pages 1–19, 2001.
[RR99] R. Rugina and M. Rinard. Pointer Analysis for Multithreaded
Programs. In Proceedings of the ACM SIGPLAN ’99 Conference on
Programming Language Design and Implementation, pages 77–90,
Atlanta, Georgia, May 1–4, 1999.
[Ryd03] B. Ryder. Dimensions of Precision in Reference Analysis of
Object-oriented Languages, 2003. In CC 2003, pages 126–137.
[SH97] M. Shapiro and S. Horwitz. Fast and Accurate Flow-insensitive
Points-to Analysis. In Proceedings of the ACM SIGPLAN/SIGACT
Symposium on Principles of Programming Languages, pages 1–14,
1997.
[SH98] M. Schroeder and F. Hauck. Juggle: Eine verteilte virtuelle Ma-
schine fuer Java. Techn. Report TR-I4-98-03, Univ. of Erlangen-
Nuernberg, 1998. german.
[Spi99a] A. Spiegel. Pangaea: An Automatic Distribution Front-End for
Java. Lecture Notes in Computer Science, 1586:93–99, 1999.
[Spi99b] A. Spiegel. Object Graph Analysis. Technical report, Technical
Report B-99-11, FU Berlin, FB Mathematik und Informatik, July
1999.
[Spi99c] A. Spiegel. Automatische Verteilung in Pangaea. In Java
Informations-Tage (JIT ’99), Düsseldorf, September 1999.
[SRW98] M. Sagiv, T. Reps, and R. Wilhelm. Solving Shape-Analysis Pro-
blems in Languages with Destructive Updating. In ACM TO-
PLAS, pages 20(1): 1–50, January 1998.
250
LITERATURVERZEICHNIS
[Ste96] B. Steensgaard. Points-to Analysis by Type Inference of Pro-
grams with Structures and Unions. In Proceedings of the 6th In-
ternational Conference on Compliler Construction, pages 136–150,
April 1996.
[StJOG01] Y. Smaragdakis and the J-Orchestra Group. Apllication Parti-
tioning without Programming (a White-Paper and Future Work
Proposal, 2001. Colege of Computing, Georgia Institute of Tech-
nology.
[T. 99] T. Lindholm and F. Yellin. The Java Virtual Machine Specification.
Addison-Wesley, 2nd edition edition, 1999.
[TAS98] B. Topol, M. Ahamad, and J. T. Stasko. Robust State Sharing for
Wide Area Distributed Applications. In International Conference
on Distributed Computing Systems, pages 554–561, 1998.
[Thi01] M. Thies. Combining Static Analysis of Java Libraries with Dynamic
Optimization. PhD thesis, 2001. Universität Paderborn, Germany.
[TP00] F. Tip and J. Palsberg. Scalable Propagation-Based Call Graph
Construction Algorithms. In Proceedings of Conference on
Object-Oriented Programming Systems, Languages and Applications
(OOPSLA ’00), October 2000.
[TRV+00] E. Truyen, B. Robben, B. Vanhaute, T. Coninx, W. Joosen, and
P. Verbaeten. Portable Support for Transparent Thread Migrati-
on in Java. In Proceedings of International Symposium on Agent Sys-
tems and Applications/Mobile Agents (ASA/MA’2000), Zurich, Suis-
se, 2000.
[TS01] E. Tilevich and Y. Smaragdakis. J-Orchestra: Automatic Java
Apllication Partitioning, 2001. Colege of Computing, Georgia
Institute of Technology.
[TS02] E. Tilevich and Y. Smaragdakis. J-Orchestra: Automatic Java
Application Partitioning, June 2002. European Conference on
Object-Oriented Programming (ECOOP 2002), Malaga.
[VHBB01] R. Veldema, R. F. H. Hofman, R. Bhoedjang, and H. E. Bal. Runti-
me Optimizations for a Java DSM Implementation. In Java Gran-
de, pages 153–162, 2001.
251
LITERATURVERZEICHNIS
[vNMB+99] R. v. Nieuwpoort, J. Maassen, H. E. Bal, T. Kielmann, and R. Vel-
dema. Wide-area Parallel Computing in Java. In Proceedings of
the ACM Java Grande Conference, New York, NY, June 1999. ACM
Press.
[Wei03] M. Weißenborn. Strategien zur automatischen Objektmigration
auf Grundlage statischer und dynamischer Analyse. Master’s
thesis, Universität Paderborn, 2003.
[Wes04] H. Wessels. Optimierung dynamischer Methodenbindung zum
Entfernen ungenutzter Programelemente durch Kombination
statischer Analyseverfahren. Master’s thesis, Universität Pader-
born, 2004.
[WR99] J. Whaley and M. Rinard. Compositional Pointer and Escape
Analysis for Multithreaded Java Programs. Technical report,
Massachusetts Institute of Technology Cambrige, MA 02139, No-
vember 1999.
[WTV02a] D. Weyns, E. Truyen, and P. Verbaeten. Distributed Threads in
Java. In In Proceedings of the International Symposium on Parallel
and Distributed Computing (ISPDC’2002), 2002.
[WTV02b] D. Weyns, E. Truyen, and P. Verbaeten. Serialization of a Distri-
buted Execution-state in Java. In accepted for Net.ObjectDays 2002,
2002.
[YC97] W. Yu and A. L. Cox. Java/DSM: A Platform for Heterogeneous
Computing. Concurrency - Practice and Experience, 9(11):1213–
1224, 1997.
[YSP+98] K. Yelick, L. Semenzato, G. Pike, C. Miyamoto, B. Liblit, A. Kris-
hnamurthy, P. Hilfinger, S. Graham, D. Gay, P. Colella, and A. Ai-
ken. Titanium: a High-Performance Java Dialect. Concurrency:
Practice and Experience, 10(11–13):825–836, September 1998. Spe-
cial Issue: Java for High-performance Network Computing.
252
Abbildungsverzeichnis
2.1. Prozesszustandsdiagramm . . . . . . . . . . . . . . . . . . . . . . 11
2.2. Das Multithreading-Konzept in Java . . . . . . . . . . . . . . . . . 13
2.3. Das RMI-Konzept . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.4. Semantik des Methodenaufrufs bei RMI . . . . . . . . . . . . . . 21
2.5. Verzahnte und verteilte Ausführung . . . . . . . . . . . . . . . . 25
2.6. Verteilte Ausführung . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.7. Aktivität und verteiltes Ausführungsmodell . . . . . . . . . . . 28
3.1. Programmanalysen und verteilte Ausführung . . . . . . . . . . 38
3.2. Verwendung der Analyseergebnisse in Verteilungsstrategien . 41
5.1. Modell zur automatischen Verteilung - Von Programmanalyse
zu Verteilungsplänen . . . . . . . . . . . . . . . . . . . . . . . . . . 70
5.2. Analysetechniken zur Bestimmung der Programmeigenschaften 72
5.3. Das Zusammenwirken aller Komponenten bei Verteilungsplänen 78
6.1. TSP-Beispiel zur hybriden Analyse für Schleifen . . . . . . . . . 131
6.2. Ergebnis der PreciseCallGraph-Analyse . . . . . . . . . . . . . . 135
6.3. Ergebnis der erweiterten PreciseCallGraph-Analyse . . . . . . . 137
6.4. Beispiel für interprozedurale Definitions-/Verwendungsketten 138
6.5. Verfahren zum Aufbau der interprozeduralen Definitions-
/Verwendungsketten . . . . . . . . . . . . . . . . . . . . . . . . . . 140
6.6. Anwendung der pure-immutable und postponed-immutable Ei-
genschaft zur Replikation . . . . . . . . . . . . . . . . . . . . . . . 147
6.7. Aufteilung des interprozeduralen Ablaufgraphen in drei Phasen 149
6.8. Beispielprogramm zur stagewise-immutable Eigenschaft . . . . . 150
6.9. Gefundene Replikationspunkte mit Must-Post-Dominator-
Bedingung ................................ 155
6.10. MAY- und MUST-POSTDOM . . . . . . . . . . . . . . . . . . . . . 159
7.1. Das JScatter-System mit seinen Komponenten . . . . . . . . . . . 168
253
ABBILDUNGSVERZEICHNIS
7.2. Die Infrastruktur der Analyseumgebung [Thi01] . . . . . . . . . 170
7.3. Objektmodell für die zu analysierende Software [Thi01] . . . . 171
7.4. Klassenaufteilung einer Klasse . . . . . . . . . . . . . . . . . . . . 173
7.5. Hierarchie der aufgeteilten Klassen . . . . . . . . . . . . . . . . . 174
7.6. Transformationsschritte mit verwendeter Software . . . . . . . . 175
7.7. Infrastruktur der verwendeten ODFs, Strategien und Vertei-
lungspläne ................................ 177
7.8. Abstraktion der verteilten Laufzeitumgebung . . . . . . . . . . . 180
7.9. Komponenten der verteilten Laufzeitumgebung . . . . . . . . . 181
7.10. remote NEW . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
8.1. Klassifikation verschiedener Arten von Simulationen . . . . . . 188
8.2. Analyseergebnisse zur stagewise-immutable Eigenschaft . . . . . 197
8.3. Ergebnisse zur Pure- und Postponed-Immutable-Eigenschaft . 197
8.4. Anteile der als stagewise- und pure-immutable erkannten Stellen
zu gesamten NEW-Stellen . . . . . . . . . . . . . . . . . . . . . . . . 198
8.5. Analyseergebnisse zur stagewise- und pure-immutable Eigen-
schaft der Anwendungen mit ihrer transitiven Hülle . . . . . . 199
8.6. Anteile der als stagewise- und pure-immutable erkannten Stellen
im Verhältnis zur Gesamtanzahl der NEW-Stellen der Anwen-
dungen und ihrer transitiver Hülle . . . . . . . . . . . . . . . . . 200
8.7. Anteile der stagewise- und pure-immutable Stellen über den Ana-
lysekontext . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
8.8. Verteilungsergebnisse der Grocery-Simulation mit verschie-
denen Verteilungsstrategien . . . . . . . . . . . . . . . . . . . . . . 205
8.9. Speedup für Grocery-Simulation . . . . . . . . . . . . . . . . . . . 206
8.10. Startup-Zeit bei der verteilten Grocery-Simulation . . . . . . . . 208
8.11. Verteilungsergebnisse der Simulation auf normalem Pool-
Cluster und auf Arminius-Cluster . . . . . . . . . . . . . . . . . . 209
8.12. Speedup-Werte - Verteilung auf normalem Pool-Cluster und
auf Arminius-Cluster . . . . . . . . . . . . . . . . . . . . . . . . . . 210
8.13. Verteilungsergebnisse der originalen Version des mtrt-
Programms................................ 214
8.14. Verteilungsergebnisse mit Kommunikationsoptimierung opt1
und opt2 fürmtrt ............................ 215
8.15. Verteilungsergebnisse mit Kommunikationsoptimierung opt1
und opt2 und 10fach Workload für mtrt . . . . . . . . . . . . . . . 216
8.16. Speedup bei mtrt mit 10fach Workload und Optimierungen
opt1,opt2 ................................. 217
254
ABBILDUNGSVERZEICHNIS
8.17. Die gerenderte Szene . . . . . . . . . . . . . . . . . . . . . . . . . . 218
8.18. Verteilungsergebnisse - Messwerte auf normalem PC-Cluster
und auf Arminius-Cluster . . . . . . . . . . . . . . . . . . . . . . . 219
8.19. Speedup-Werte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
8.20. Profilingdaten für mtrt: Anzahl erzeugter Objekte mit dafür
benötigtem Speicher . . . . . . . . . . . . . . . . . . . . . . . . . . 223
8.21. Profilingdaten für mtrt: Aufrufbaum mit Anzahl erzeugter Ob-
jekte zusammen mit belegtem Speicher . . . . . . . . . . . . . . . 224
8.22. Profilingdaten für den Aufrufbaum des mtrt-Programms . . . . 225
8.23. Verteilungsergebnis für TSP . . . . . . . . . . . . . . . . . . . . . . 226
8.24. Anzahl der entfernten Methodenaufrufe jedes Worker-Threads 227
9.1. n:n-Beziehung zwischen Laufzeitobjekten im Objektgraphen . 236
255
.
Tabellenverzeichnis
4.1. Alle Systeme und Bibliotheken - Zusammenfassung . . . . . . . 65
8.1. Kenndaten der Anwendungsprogramme . . . . . . . . . . . . . 191
8.2. Einige Kenndaten über Ergebnisse der stagewise-immutable
Analyse.................................. 195
257