08.03.2014 Aufrufe

1.2 Garbage-Collection

1.2 Garbage-Collection

1.2 Garbage-Collection

MEHR ANZEIGEN
WENIGER ANZEIGEN

Sie wollen auch ein ePaper? Erhöhen Sie die Reichweite Ihrer Titel.

YUMPU macht aus Druck-PDFs automatisch weboptimierte ePaper, die Google liebt.

Fachbereich Informatik<br />

Integrierte Schaltungen und Systeme<br />

Prof. Dr.-Ing. Sorin Huss<br />

Diplomarbeit<br />

Entwicklung eines Gargbage-Collectors für<br />

rekonfigurierbare Hardwarestrukturen<br />

Felix Madlener<br />

madlener@iss.tu-darmstadt.de<br />

Matr.-Nr.: 948463<br />

Betreuer : Dipl.-Inform. Andreas Kühn<br />

Abgabe : 8.6.2005


Zusicherungen zur Diplomarbeit gemäß §19 Abs. 6 der<br />

Diplomprüfungsordung<br />

Hiermit versichere ich, die vorliegende Diplomarbeit ohne Hilfe Dritter nur mit den<br />

angegebenen Quellen und Hilfsmitteln angefertigt zu haben. Alle Stellen, die aus Quellen<br />

entnommen wurden, sind als solche kenntlich gemacht worden. Diese Arbeit hat in<br />

gleicher oder ähnlicher Form noch keiner Prüfungsbehörde vorgelegen.<br />

Darmstadt, den 8.6.2005<br />

Felix Madlener


Inhaltsverzeichnis<br />

Abbildungsverzeichnis<br />

v<br />

1 Einleitung 1<br />

1.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1<br />

<strong>1.2</strong> <strong>Garbage</strong>-<strong>Collection</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3<br />

1.3 Überblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3<br />

2 Begriffe und Strukturen 5<br />

2.1 Konfigurationsmöglichkeiten eines FPGA . . . . . . . . . . . . . . . . 5<br />

2.2 Objektorientierte Konzepte . . . . . . . . . . . . . . . . . . . . . . . . 6<br />

2.3 Strukturierung der Hardware . . . . . . . . . . . . . . . . . . . . . . . 8<br />

2.3.1 Layer 1: Logik-Ebene . . . . . . . . . . . . . . . . . . . . . . 8<br />

2.3.2 Layer 2: Struktur-Ebene . . . . . . . . . . . . . . . . . . . . . 9<br />

2.3.3 Layer 3: Protokolle . . . . . . . . . . . . . . . . . . . . . . . . 11<br />

2.3.4 Layer 4: Middleware . . . . . . . . . . . . . . . . . . . . . . . 11<br />

2.3.5 Layer 5: Applikation . . . . . . . . . . . . . . . . . . . . . . . 12<br />

2.3.6 Layer 6: SW-Engineering . . . . . . . . . . . . . . . . . . . . 12<br />

2.4 Beispiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13<br />

2.5 Vereinfachte Repräsentation eines Programms . . . . . . . . . . . . . . 13<br />

3 <strong>Garbage</strong>-<strong>Collection</strong> in Software 17<br />

3.1 Statische Reservierung . . . . . . . . . . . . . . . . . . . . . . . . . . 18<br />

3.2 Explizite Reservierung . . . . . . . . . . . . . . . . . . . . . . . . . . 19<br />

3.3 Referenzzählung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19<br />

3.4 Mark-Sweep <strong>Collection</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . 20<br />

3.5 Erweiterungen der Mark-Sweep-<strong>Collection</strong> . . . . . . . . . . . . . . . 21<br />

3.5.1 Fragmentierung . . . . . . . . . . . . . . . . . . . . . . . . . . 21<br />

3.5.2 Nebenläufigkeit . . . . . . . . . . . . . . . . . . . . . . . . . . 22<br />

3.5.3 Generational <strong>Collection</strong> . . . . . . . . . . . . . . . . . . . . . 23


iv<br />

INHALTSVERZEICHNIS<br />

4 <strong>Garbage</strong>-<strong>Collection</strong> in Hardware 25<br />

4.1 Einordnung der <strong>Garbage</strong>-<strong>Collection</strong> . . . . . . . . . . . . . . . . . . . 26<br />

4.2 Traversierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27<br />

4.3 Fragmentierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31<br />

4.4 Nebenläufigkeit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32<br />

4.5 Generational <strong>Collection</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . 34<br />

4.6 Der Algorithmus der Hardware-<strong>Garbage</strong>-<strong>Collection</strong> (HW-GC) . . . . . 34<br />

5 Analyse des HW-GC-Algorithmus 39<br />

5.1 Objektstruktur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40<br />

5.2 Softwarebasiertes Simulationsmodell . . . . . . . . . . . . . . . . . . . 41<br />

5.3 Qualitative Betrachtung . . . . . . . . . . . . . . . . . . . . . . . . . . 45<br />

5.3.1 Warten auf den nächsten <strong>Garbage</strong>-<strong>Collection</strong>-Zyklus . . . . . . 46<br />

5.3.2 Die Traversierung im Mark-Prozess . . . . . . . . . . . . . . . 47<br />

5.3.3 Der Sweep-Prozess . . . . . . . . . . . . . . . . . . . . . . . . 48<br />

5.3.4 Funktionalität des HW-GC-Algorithmus . . . . . . . . . . . . . 49<br />

5.4 Quantitative Betrachtung . . . . . . . . . . . . . . . . . . . . . . . . . 49<br />

5.4.1 Laufzeit der Traversierung . . . . . . . . . . . . . . . . . . . . 50<br />

5.4.2 PageMiss . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52<br />

5.5 Häufigkeit der GC-Zyklen . . . . . . . . . . . . . . . . . . . . . . . . 55<br />

6 Zusammenfassung 57<br />

6.1 Ergebnisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57<br />

6.2 Ausblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58<br />

Literaturverzeichnis 59


Abbildungsverzeichnis<br />

1.1 Flexibilität und Effizienz bei verschiedenen Architekturen . . . . . . . . 2<br />

2.1 Abstraktionsebenen der Entwurfsmethodik . . . . . . . . . . . . . . . . 9<br />

2.2 Struktur eines Virtex2 Pro FPGA . . . . . . . . . . . . . . . . . . . . . 10<br />

2.3 Schematischer Aufbau eines Objekts und der Computing Page . . . . . 12<br />

2.4 Programmablauf und Objektgraph . . . . . . . . . . . . . . . . . . . . 15<br />

3.1 Inkorrekter Objektgraph bei nebenläufiger <strong>Garbage</strong>-<strong>Collection</strong> . . . . . 23<br />

4.1 Traversierungsreihenfolge eines Objektgraphen mit sequenzieller Traversierung<br />

mit Tiefensuche (a) und nebenläufiger Traversierung (b) . . . 28<br />

4.2 Lokalisierter Bus zur <strong>Garbage</strong>-<strong>Collection</strong> . . . . . . . . . . . . . . . . 29<br />

4.3 Ablauf einer Traversierung im HW-GC-Algortihmus . . . . . . . . . . 30<br />

4.4 Algorithmus zur Hardware-<strong>Garbage</strong>-<strong>Collection</strong> in VHDL-Pseudocode . 37<br />

5.1 Objektgraphen mit verschiedener Anzahl an ausgehenden Kanten . . . 43<br />

5.2 Minimaler aufspannender Graph . . . . . . . . . . . . . . . . . . . . . 44<br />

5.3 Erreichbare Knoten in Zufallsgraphen . . . . . . . . . . . . . . . . . . 45<br />

5.4 Verschiedene Traversierungen . . . . . . . . . . . . . . . . . . . . . . 52<br />

5.5 Neuer Objektgraph nach löschen der Kante a) zum Zeitpunkt 5 . . . . . 53<br />

5.6 Neuer Objektgraph nach löschen der Kante b) zum Zeitpunkt 4 . . . . . 53


Kapitel 1<br />

Einleitung<br />

1.1 Motivation<br />

Ziel dieser Arbeit ist der Entwurf eines Verfahrens zur automatischen Speicherverwaltung<br />

(<strong>Garbage</strong>-<strong>Collection</strong>). Diese Speicherverwaltung ist Teil eines neuartigen, objektorientierten<br />

Entwicklungsansatzes für rekonfigurierbare Hardwarestrukturen.<br />

Herkömmliche Hardware ist statisch aufgebaut. Das Logikdesign und damit auch die<br />

Funktionalität müssen bereits bei der Produktion des entsprechenden Chips vollständig<br />

definiert sein. Die Funktionalität einer solchen Architektur kann dann nicht mehr<br />

geändert werden. Dynamische Strukturen und Programme, wie sie im Bereich der Softwareentwicklung<br />

üblich sind, lassen sich somit nicht direkt in einer effizienten Hardwarestruktur<br />

abbilden.<br />

Üblicherweise können vor allem datenflussintensive, aber in ihrer Funktionalität statische<br />

Programme, von den Vorteilen einer Hardwareimplementierung profitieren. Solche<br />

Vorteile sind beispielsweise eine höhere Ausführungsgeschwindigkeit und ein geringerer<br />

Stromverbrauch. Kontrollflussintensive Algorithmen erfordern hingegen die Flexibilität<br />

eines generischen Prozessors mit darauf ablaufenden Programmen.<br />

Mit der Entwicklung von dynamisch rekonfigurierbaren Hardwarestrukturen, wie zum<br />

Beispiel dem Field Programmable Gate Array (FPGA), ist es erstmals möglich auch<br />

in Hardwareimplementierungen dynamische Strukturen umzusetzen. Die Funktionalität<br />

eines FPGAs wird durch seine Konfiguration festgelegt. Sie kann beliebig modifiziert<br />

werden, indem man das FPGA mit einem vorher generierten Datenstrom rekonfiguriert.<br />

Darüberhinaus bieten FPGAs die Möglichkeit zur partiellen Rekonfiguration. Während<br />

bei einer normalen Rekonfiguration immer der gesamte Chip beeinflusst wird, erlaubt<br />

es die partielle Rekonfiguration, nur einzelne Teile des Logikdesigns im laufenden<br />

Betrieb auszutauschen. Der übrige Chip kann dabei ohne Unterbrechung weiterlaufen.<br />

Wie in Abbildung 1.1 veranschaulicht, sind diese dynamisch rekonfigurierbaren Strukturen<br />

in Bezug auf Flexibilität und Effizienz zwischen den flexiblen, aber ineffizienten


2 KAPITEL 1. EINLEITUNG<br />

Software<br />

Flexibilität<br />

Rekonfigurierbare Hardware<br />

Effizienz<br />

Hardware<br />

Abbildung 1.1: Flexibilität und Effizienz bei verschiedenen Architekturen<br />

Softwareprogrammen und den statischen, aber effizienten Hardwareimplementierungen<br />

angeordnet.<br />

Um diese Architektur sinnvoll einzusetzen, ist neben der Verfügbarkeit der Hardware<br />

auch eine Entwurfsmethodik notwendig, die es ermöglicht, die Vorteile der Plattform<br />

voll auszunutzen und umzusetzen.<br />

Die bisherigen Entwurfsmethodiken für dynamisch rekonfigurierbare Strukturen basieren<br />

auf einer Erweiterung der Werkzeuge und Konzepte, wie sie für klassische Hardware<br />

zum Einsatz kommen. Ihre Ausgangsbasis ist also ein effizienter Entwurfsprozess,<br />

der in Bezug auf die Flexibilität jedoch stark eingeschränkt ist. Mit diesen Werkzeugen<br />

können, aufgrund ihrer völlig anderen konzeptionellen Ausrichtung, die Möglichkeiten<br />

zur Flexibilität nicht zufriedenstellend umgesetzt werden.<br />

Diese Arbeit ist Teil eines Projekts zur Entwicklung einer Entwurfsmethodik, die versucht<br />

Konzepte zur Softwareentwicklung auf dynamisch rekonfigurierbare Strukturen<br />

abzubilden. Der schematischen Einordnung aus Abbildung 1.1 folgend, wird also nicht<br />

mehr ein hardwarenaher Entwurfsansatz um Rekonfigurationsmöglichkeiten erweitert,<br />

sondern Ansätze zur flexiblen Entwicklung von Software werden auf dynamisch und<br />

partiell rekonfigurierbare Strukturen übertragen.<br />

Als besonders geeignet hat sich hierfür der objektorientierte Entwicklungsansatz [1]<br />

erwiesen.


<strong>1.2</strong>. GARBAGE-COLLECTION 3<br />

<strong>1.2</strong> <strong>Garbage</strong>-<strong>Collection</strong><br />

Im Rahmen dieser Diplomarbeit wird untersucht, wie gut sich die <strong>Garbage</strong>-<strong>Collection</strong> als<br />

wichtiges Element einer objektorientierten Entwurfsmethodik in dynamisch rekonfigurierbare<br />

Hardwarestrukturen übertragen lässt. Unter <strong>Garbage</strong>-<strong>Collection</strong> versteht man<br />

eine automatische Verwaltung von verfügbaren Hardwareressourcen, die durch Rekonfiguration<br />

die vom Benutzer gewünschte Funktionalität annehmen können. Die <strong>Garbage</strong>-<br />

<strong>Collection</strong> soll auf bewährten Konzepten und Algorithmen aus dem Softwareentwurf<br />

basieren und sich in das Gesamtprojekt zur Entwicklung einer objektorientierten Entwurfsmethodik<br />

für Hardware integrieren.<br />

Neben der Analyse allgemeiner Eigenschaften einer <strong>Garbage</strong>-<strong>Collection</strong> für Hardware<br />

soll eine konkrete Implementierung auf FPGAs der Firma Xilinx zur Evaluation dienen.<br />

Außerdem wird untersucht, welche allgemeinen Richtlinien und Eigenschaften sich für<br />

die Konvertierung einer Applikation aus Software in eine hardwarenahe Applikation<br />

finden lassen.<br />

1.3 Überblick<br />

In Kapitel 2 wird vorgestellt, wie sich objektorientierte Konzepte in einen hardwarebasierten<br />

Entwurfsansatz übertragen lassen. Es wird auf die Rahmenbedingungen der<br />

Hardware eingegangen und ein Abstraktionsmodell vorgestellt, das in den Entwurfsmethodiken<br />

verwendet wird.<br />

In Kapitel 3 werden die bestehenden Konzepte und Ansätze zur Speicherverwaltung<br />

analysiert. Ein Schwerpunkt dieser Analyse ist der Mark-Sweep-Algorithmus, der sich<br />

als günstigste Ausgangsbasis für eine <strong>Garbage</strong>-<strong>Collection</strong> in Hardware erwiesen hat.<br />

In Kapitel 4 wird beschrieben, wie sich die einzelnen Aspekte der <strong>Garbage</strong>-<strong>Collection</strong><br />

in Hardware abbilden lassen. Es wird erläutert, welche Konzepte übernommen werden<br />

können und inwieweit sie abgeändert werden müssen, um eine effiziente und kompakte<br />

Hardwareimplementierung zu gestatten. Die dabei entwickelten Algorithmen werden<br />

vorgestellt und erläutert.<br />

Kapitel 5 analysiert die gewonnen Algorithmen. Es wird ein informeller Beweis für die<br />

Korrektheit angegeben und versucht allgemeine Aussagen und Metriken für die Leistungsfähigkeit<br />

einer <strong>Garbage</strong>-<strong>Collection</strong> anzugeben.<br />

Schließlich liefert Kapitel 6 eine kurze Zusammenfassung der Ergebnisse dieser Arbeit<br />

und weist auf offene Fragestellungen und Optimierungspotenziale hin.


4 KAPITEL 1. EINLEITUNG


Kapitel 2<br />

Begriffe und Strukturen<br />

In diesem Kapitel wird die allgemeine Struktur der Hardware vorgestellt, in welcher<br />

die <strong>Garbage</strong>-<strong>Collection</strong> eingebettet ist. Es wird erläutert, wie sich die objektorientierten<br />

Konzepte auf diese Plattform abbilden lassen.<br />

Die existierenden, rekonfigurierbaren Strukturen sind nicht auf die speziellen Eigenschaften<br />

solcher Entwicklungsmethodiken ausgelegt. Um ein System sinnvoller Größe<br />

zu realisieren, ist es deshalb nötig, die Gegebenheiten der vorhandenen Hardware optimal<br />

auszunutzen. Die wesentlichen Teile dieser Konzepte beruhen auf den Arbeiten von<br />

Kühn [2].<br />

2.1 Konfigurationsmöglichkeiten eines FPGA<br />

Als dynamisch rekonfigurierbare Plattform kommt das FPGA Virtex2 Pro V2P30 der<br />

Firma Xilinx zum Einsatz. Die Konfiguration dieser FPGAs basiert auf den Methoden<br />

der klassischen Hardwarewicklung, wie sie auch beim Entwurf von integrierten Schaltungen<br />

zum Einsatz kommen. Zunächst erfolgt ein Logikentwurf in einer abstrakten<br />

Hardwarebeschreibungssprache wie VHDL oder Verilog. Durch Simulation lassen sich<br />

diese Beschreibungen auf hoher Abstraktionsebene evaluieren. Mit Hilfe eines Synthesewerkzeugs<br />

wird aus der Hardwarebeschreibung eine logische Netzliste generiert.<br />

Hierzu kam das Programm Synplify Pro 8.0 der Firma Synplicity [7] zum Einsatz. Diese<br />

Netzliste wird mit herstellerspezifischen Entwicklungstools (Xilinx ISE 6.3 [5]) auf<br />

dem FPGA platziert und verdrahtet. Dabei entsteht ein Bitstream-File. Dieser Bitstream<br />

wird auf das FPGA geladen, das somit konfiguriert ist.<br />

Um nur Teile des FPGAs partiell zu konfigurieren, existieren zwei verschiedene Entwurfswege.<br />

Beide basieren auf dem Entwurfsprozess zur Konfiguration des gesamten<br />

FPGAs. Der “Small Bit Manipulation Design Flow” ermöglicht es, einzelne Logikgatter<br />

neu zu konfigurieren. Großflächige Rekonfigurationen oder Änderungen in der<br />

Verdrahtung einzelner Logikblöcke sind damit nicht möglich. Komplexere Änderun-


6 KAPITEL 2. BEGRIFFE UND STRUKTUREN<br />

gen erlaubt der “Module Based Design Flow”. Dabei werden definierbare Bereiche des<br />

FPGAs zu Modulen zusammengefasst, die sich unabhängig voneinander ersetzen lassen.<br />

Da dieser Design Flow es zulässt, die Verdrahtung zu ändern, muss sichergestellt<br />

werden, dass bei der Rekonfiguration nicht Signalkonflikte an der Schnittstelle mit anderen<br />

Modulen auftreten. Ein Beispiel hierfür wäre eine Signalsenke, die bei der Rekonfiguration<br />

durch ein schreibendes Signal ersetzt wird. Es würde zu einem Konflikt<br />

zweier Treiber auf der Leitung kommen, der einen Hardwaredefekt auslösen kann. Um<br />

in jedem Fall die Signalintegrität zu gewährleisten, werden zur Kommunikation zwischen<br />

den Modulen Bus Macros verwendet. Diese Kommunikationsblöcke bilden die<br />

Schnittstelle zwischen zwei Modulen. Sie nutzen Tri-State-Logikressourcen, um die Signalintegrität<br />

zu gewährleisten. Bus Macros sind während der gesamten Laufzeit des<br />

FPGAs konstant. Eine Kommunikation zweier Module ist ausschließlich über Bus Macros<br />

möglich.<br />

Da der “Small Bit Manipulation Design Flow” kein dynamisches Ändern der Verdrahtung<br />

ermöglicht, ist er für eine Entwurfsmethodik mit hoher Flexibilität als Ziel ungeeignet.<br />

Deshalb wurde für diese Arbeit der “Module Based Design Flow” als Ausgangsbasis<br />

gewählt. Darüber hinaus bietet die eingeschränkte und konstante Schnittstelle eine<br />

direkte Analogie zu den Schnittstellen eines Softwareobjektes, wie in Abschnitt 2.2 beschrieben.<br />

Neben dem FPGA kommt außerdem ein Host-System zum Einsatz, das die erforderlichen<br />

Rekonfigurationen durchführt, das FPGA mit Daten beschickt, sowie die Ergebnisse<br />

ausliest. Die Kommunikation mit dem Host-System erfolgt über eine RS-232<br />

Schnittstelle. Die verwendete FPGA-Plattform AlphaData ADM-XPL [6] würde auch<br />

eine Kommunikation über den schnelleren PCI-Bus gestatten. Auf diese Möglichkeit<br />

wurde verzichtet, da die Pins der entsprechenden Datenleitungen physikalisch weit über<br />

das FPGA verteilt liegen. Eine Schnittstellenerweiterung sämtlicher Module wäre notwendig,<br />

um die Leitungen in einem zentralen Kommunikationsmodul zusammenzuführen.<br />

Um auf diese zusätzliche Komplexität innerhalb der rekonfigurierbaren Module zu<br />

verzichten, wird die RS-232 Schnittstelle bevorzugt, die mit zwei Leitungen (Senden<br />

und Empfangen) zur Kommunikation mit dem Host-System auskommt.<br />

2.2 Objektorientierte Konzepte<br />

Ziel des Projektes, in das diese Arbeit eingebunden ist, ist es zu untersuchen, welche<br />

objektorientierten Konzepte sich auf Hardware abbilden lassen und welche Modifikationen<br />

dabei vorzunehmen sind. Betrand Meyer hat insgesamt 15 Regeln und Prinzipien<br />

aufgestellt, die eine objektorientierte Entwicklung kennzeichnen [1]. Die meisten dieser<br />

Regeln kommen erst bei der Entwicklung eines konkreten Programms zum Einsatz. So<br />

sagt etwa die “Few Interfaces”-Regel aus, dass Objekte so wenig wie möglich miteinander<br />

kommunizieren.


2.2. OBJEKTORIENTIERTE KONZEPTE 7<br />

Einige dieser Prinzipien haben auch Einfluss auf die Konzeption der Laufzeitumgebung,<br />

hier also des FPGAs. Insbesondere gilt:<br />

1. Ein objektorientiertes System besteht aus unabhängigen Objekten.<br />

2. Ein Objekt fasst eine Menge von konzeptionell zusammenhängenden Methoden<br />

zusammen. Diese können über definierte Schnittstellen von anderen Objekten aus<br />

aufgerufen werden<br />

3. Die Schnittstellen eines Objektes werden durch eine Klasse beschrieben. Ein Objekt<br />

ist eine konkrete Instanz dieser Klasse.<br />

4. Die Schnittstellen sollen so kompakt und einfach wie möglich sein.<br />

5. Die Modifikation eines Objektes durch externe Prozesse ist ausschließlich über<br />

seine Schnittstellen möglich.<br />

6. Ein Objekt hat keine Kenntnis über den internen Zustand eines anderen Objekts<br />

(Geheimnisprinzip).<br />

7. Die Objekte werden zur Laufzeit bei Bedarf erzeugt. Zu diesem Zeitpunkt wird<br />

auch der Speicher reserviert, den dieses Objekt benötigt.<br />

8. Wenn ein Objekt von keinem anderen Objekt aus mehr erreichbar ist, sind seine<br />

Daten wegen des Geheimnisprinzips nicht mehr zugänglich. Das Objekt kann<br />

gelöscht, sein Speicherplatz wieder freigegeben werden.<br />

Aus diesen Punkten ergibt sich, weshalb der Ansatz der Objektorientierung sich besonders<br />

gut auf rekonfigurierbare Hardwarestrukturen abbilden lässt: Betrachtet man<br />

die rekonfigurierbaren Module als Objekte eines objektorientierten Systems, so lassen<br />

sich diese Punkte weitgehend direkt abbilden. Der modulare Entwurfsprozess fordert<br />

bereits, dass die Kommunikation zwischen den Modulen ausschließlich über definierte<br />

und kompakte Schnittstellen stattfindet, womit Punkt 4 und 5 erfüllt sind. Kommunikation<br />

außerhalb dieser Schnittstellen ist auf dem FPGA aus physikalischen Gründen nicht<br />

möglich, womit das Geheimnisprinzip aus Punkt 6 erfüllt wird. Das Erzeugen neuer<br />

Objekte und das zugehörige Allozieren von Speicher in Software ist identisch mit dem<br />

dynamischen Rekonfigurieren einzelner Module des FPGAs.<br />

Wie in Punkt 8 der Aufzählung beschrieben, kommt es vor, dass Objekte nicht mehr<br />

benötigt werden und der von ihnen verwendete Speicher freigegeben werden kann. Die<br />

Analogie in dem hardwarebasierten Ansatz ist das Freigeben der Logikressourcen eines<br />

Moduls. In der Praxis wird die Freigabe realisiert, indem solche Module in eine<br />

Liste übernommen werden. Die Liste enthält sämtliche Positionen, die bei der nächsten<br />

entsprechenden Anforderung rekonfiguriert werden können.


8 KAPITEL 2. BEGRIFFE UND STRUKTUREN<br />

Um zu erkennen, welche Objekte nicht mehr benötigt und bei Bedarf gelöscht werden<br />

können, ist es notwendig zu überprüfen, welches Objekt welche anderen Objekte erreichen<br />

kann. Diese Informationen unterliegen als Bestandteil eines Objekts auch dem<br />

Geheimnisprinzip, sind also eigentlich nicht frei von außen abzufragen.<br />

Um trotzdem eine Aussage bezüglich freizugebender Objekte treffen zu können, gibt<br />

es zwei verschiedene Ansätze. Die eine Möglichkeit ist es, das Geheimnisprinzip etwas<br />

abzuschwächen. Jedes Objekt erlaubt dann eine Abfrage, welche anderen Objekte<br />

es noch erreichen kann. Dieser Ansatz ist beispielsweise für Systeme üblich, in denen<br />

Objekte manuell gelöscht werden. Die andere Möglichkeit ist es, die <strong>Garbage</strong>-<br />

<strong>Collection</strong> auf Systemebene zu implementieren. Als Teil der Laufzeitumgebung, die<br />

erst solche Mechanismen wie das Geheimnisprinzip bereitstellt, lassen sich die nötigen<br />

Daten ermitteln, ohne dass eine Implementierung das Geheimnisprinzip verletzen kann.<br />

Es ist üblich, das Erkennen von Objekten, die nicht mehr benötigt werden, zu automatisieren.<br />

Diese Mechanismen zur automatischen Speicherverwaltung werden als<br />

<strong>Garbage</strong>-<strong>Collection</strong> bezeichnet. Diese Arbeit konzentriert sich auf die Entwicklung einer<br />

<strong>Garbage</strong>-<strong>Collection</strong> auf Systemebene. Die Vorteile gegenüber einer Implementierung<br />

auf Anwendungsebene sind zum einen die bessere Effizienz und zum anderen eine<br />

einfache Umgebung für den Entwickler, für den dieser Prozess transparent abläuft.<br />

2.3 Strukturierung der Hardware<br />

Um eine elegante Übertragung der Softwarekonzepte zu gestatten, bietet es sich an,<br />

auf dem FPGA mehrere aufeinander aufbauende Abstraktionsebenen zu definieren, wie<br />

sie in Abbildung 2.1 aufgeführt sind. In den unteren hardwarenahen Ebenen werden die<br />

Mechanismen implementiert, die es erlauben auf den oberen Ebenen vollständig von der<br />

Hardware zu abstrahieren. Die Aufgaben und Bestandteile der einzelnen Layer werden<br />

im Folgenden kurz erläutert.<br />

2.3.1 Layer 1: Logik-Ebene<br />

Die Logik-Ebene stellt die Hardwareressourcen zur Verfügung. Wie erläutert, ist hier<br />

eine Architektur erforderlich, die eine partielle, dynamische Rekonfiguration der Ressourcen<br />

ermöglicht.<br />

In dieser Arbeit kommt ein FPGA der Firma Xilinx zum Einsatz (V2P30 [3]). Die<br />

Virtex2 Reihe bietet als leistungsfähige Baureihe der Firma Xilinx die nötigen Ressourcen,<br />

um verschiedene Ansätze zu evaluieren. Basiseinheiten der Logikressourcen<br />

auf diesem FPGA sind frei konfigurierbare CLBs (Configurable Logic Block), die aus<br />

vier Eingangssignalen ein Ausgangssignal mit Hilfe einer Boole’schen Wahrheitstabelle<br />

bilden. Für taktsynchrone Designs befindet sich hinter jedem CLB ein FlipFlop, das


2.3. STRUKTURIERUNG DER HARDWARE 9<br />

Layer 6<br />

Layer 5<br />

Layer 4<br />

Layer 3<br />

Layer 2<br />

Layer 1<br />

Engineering<br />

Application<br />

Middleware<br />

Protokolle<br />

Structured Hardware<br />

Unstructured Hardware<br />

Abstraktionsgrad<br />

Abbildung 2.1: Abstraktionsebenen der Entwurfsmethodik<br />

optional verwendet werden kann. Die Verbindung der CLBs ist über Verdrahtungsressourcen<br />

realisiert, die sich ebenfalls konfigurieren lassen. Zusätzlich bietet die Virtex-2<br />

Plattform komplexere logische Einheiten an. Dies sind zum Beispiel dedizierte Multiplizierer,<br />

Frequenzvervielfacher und sogar PowerPC-Cores. Auf den Einsatz der komplexeren<br />

Einheiten wird in dieser Arbeit verzichtet, um den vorgestellten Entwurfsansatz so<br />

flexibel wie möglich zu halten. Partielle Rekonfiguration ist erst seit kurzem verfügbar<br />

und nur innerhalb gewisser Randbedingungen möglich [4], auf die im nächsten Layer<br />

eingegangen wird.<br />

2.3.2 Layer 2: Struktur-Ebene<br />

In der Struktur-Ebene werden die Logikressourcen von Layer 1 in passende rekonfigurierbare<br />

Module unterteilt und die Busstruktur zur Kommunikation festgelegt. Die<br />

Randbedingungen der verwendeten Hardwarearchitektur bestimmen dieses Design maßgeblich.<br />

Xilinx-FPGAs besitzen eine rechteckige Struktur von ungefähr 92*160 Elementen<br />

(die genauen Maße unterscheiden sich je nach verwendetem Typ). Zur Orientierung<br />

verwendet Xilinx ein X-Y-Koordinaten-System wie in Abbildung 2.2 dargestellt.<br />

Rekonfigurierbare Module müssen die vollständige Höhe des FPGAs abdecken und besitzen<br />

eine horizontale Breite, die ein Vielfaches von Vier darstellen muss. Ein FPGA<br />

mit 92*160 Elementen besitzt damit maximal 40 rekonfigurierbare Elemente der Größe<br />

92*4 Zellen.<br />

Wie bereits erwähnt, muss die Kommunikation zwischen den rekonfigurierbaren Blöcken<br />

über dedizierte Routingressourcen erfolgen. Um die Signalintegrität an der Schnittstelle<br />

auch während der Rekonfiguration eines Moduls sicherzustellen, müssen zur Kom-


10 KAPITEL 2. BEGRIFFE UND STRUKTUREN<br />

Y<br />

n<br />

rekonf.<br />

Modul<br />

3<br />

rekonf.<br />

Modul<br />

2<br />

rekonf.<br />

Modul<br />

1<br />

rekonf.<br />

Modul<br />

0<br />

Arbiter<br />

X<br />

Abbildung 2.2: Struktur eines Virtex2 Pro FPGA<br />

munikation Bussysteme mit Tri-State-Treibern verwendet werden. Die Anzahl der Tri-<br />

State-Treiber ist eng begrenzt und limitiert die maximal mögliche Schnittstelle zwischen<br />

jeweils zwei nebeneinanderliegenden Modulen.<br />

Deshalb ist zur Kommunikation der Module untereinander ein Bussystem realisiert,<br />

welches gegenüber einer direkten Punkt-zu-Punkt Verbindung aller Module auf Kosten<br />

der Geschwindigkeit mit weniger Verdrahtungsressourcen auskommt. Insgesamt verwendet<br />

die Implementierung einen 40 Bit breiten Datenbus.<br />

Neben den frei konfigurierbaren Logikblöcken ist mit dem Arbiter ein statisches Modul<br />

implementiert. Der Arbiter bildet die Laufzeitumgebung für ein Programm, das in<br />

den Modulen realisiert ist. Er fungiert als Busmaster und bildet das Interface zum Host-<br />

System.<br />

Die aktuelle Implementierung besitzt zur Evaluation 15 frei konfigurierbare Module.


2.3. STRUKTURIERUNG DER HARDWARE 11<br />

2.3.3 Layer 3: Protokolle<br />

Die komplexen Busstrukturen machen fortgeschrittene Protokolle erforderlich, die als<br />

eigene Abstraktionsebene aufgefasst werden. Im Rahmen dieser Protokolle wird jedem<br />

rekonfigurierbaren Modul eine Adresse zugeteilt, die mit ihrer geometrischen Position<br />

auf dem Chip identisch ist. Das Modul 3 liegt also beispielsweise zwischen den Modulen<br />

4 und 5. Module sind von rechts nach links durchnummeriert, der Arbiter besitzt die<br />

Adresse 0. In Abbildung 2.2 sind die Adressen über den einzelnen Spalten angegeben.<br />

Aktuell besitzen die Adressen eine Breite von vier Bit, die ausreicht, bis zu 15 Module<br />

anzusprechen.<br />

Die Kommunikation über den Bus enthält damit immer die Adresse des Zielmoduls.<br />

Dazu wird optional eine Absenderadresse mitgeschickt, um beispielsweise einen Rückgabewert<br />

wieder zurück zu senden. Im Normalfall besitzt ein Objekt mehrere unterschiedliche<br />

Methoden, die von außen zugänglich sind. Alle Methoden eines Objekts<br />

besitzen eine eindeutige Identifikationsnummer (ID), die beim Aufruf ebenfalls übertragen<br />

wird. Schließlich müssen auch noch die Argumente des Prozeduraufrufs übertragen<br />

werden.<br />

Die Adresse des Ziels, sowie die Absenderadresse sind jeweils vier Bit breit. Ebenso<br />

das Signal zur Auswählen der Methode, womit ein Objekt maximal 16 verschiedene<br />

Methoden besitzen kann. Die Argumente sind als 16 Bit breiter Bus realisiert.<br />

Zusammengefasst gilt also, dass bei jeder Kommunikation die Adresse des Zielblocks,<br />

optional eine Absenderadresse, Daten für einen Prozeduraufruf, und die ID der aufgerufenen<br />

Prozedur versendet werden. Zusätzliche Kommunikationsstrukturen für Reset-<br />

Signale und ähnliches sind ebenfalls erforderlich.<br />

Außerdem sind verschiedene weitere Kommunikationsstrukturen implementiert, die<br />

beispielsweise ein globales Reset-Signal zu allen Komponenten leiten. Die Möglichkeit,<br />

eine zusätzliche Leitung für die <strong>Garbage</strong>-<strong>Collection</strong> zu verwenden ist gegeben. In<br />

dieser Arbeit werden zwei weitere globale Signale eingeführt, die in Kapitel 4 vorgestellt<br />

werden.<br />

2.3.4 Layer 4: Middleware<br />

Die Kombination aus den rekonfigurierbaren Modulen und den Mechanismen zur Protokollverwaltung<br />

bildet den Middleware-Layer.<br />

Wichtigstes Element der Middleware sind die ComputingPages, die einen Containertyp<br />

für die darin eingebetteten eigentlichen Objekte bilden. Abbildung 2.3 gibt einen<br />

schematischen Überblick über die Einbettung der Objekte. Die ComputingPages bieten<br />

ein abstraktes und vereinfachtes Interface zu den Objekten an. Sie implementieren erforderliche<br />

Verwaltungsmechanismen wie Adressdekodierung für den Bus, und Prozedurdekodierung.<br />

Auch Teile der <strong>Garbage</strong>-<strong>Collection</strong>, die als Teil jedes Objekts realisiert<br />

sind, sind auf diesem Layer angesiedelt.


12 KAPITEL 2. BEGRIFFE UND STRUKTUREN<br />

Computing Page<br />

RightIn<br />

LeftOut<br />

Protokoll Decoder<br />

RightOut<br />

LeftIn<br />

Objekt<br />

Adresse : 4 Bit<br />

Funktion 0(Arg:16 Bit)<br />

Funktion 1(Arg:16 Bit)<br />

Funktion 15(Arg:16 Bit)<br />

Abbildung 2.3: Schematischer Aufbau eines Objekts und der Computing Page<br />

Darüber hinaus ist der Arbiter ein Teil der Middleware. Er steuert als Busmaster die<br />

Kommunikation zwischen den verschiedenen ComputingPages. Der Arbiter verwaltet<br />

die freien ComputingPages und deaktiviert das Bussystem für die Rekonfiguration einer<br />

ComputingPage. Die systemweiten Komponenten der <strong>Garbage</strong>-<strong>Collection</strong> sind ebenfalls<br />

als Teil des Arbiters implementiert. Im Sinne der Kommunikationsprotokolle aus<br />

dem Protokoll-Layer (Layer 3) besitzt der Arbiter die Adresse 0. Geometrisch ist er auf<br />

dem Chip am rechten Rand angeordnet. Eine Rekonfiguration zur Laufzeit ist für den<br />

Arbiter nicht vorgesehen.<br />

2.3.5 Layer 5: Applikation<br />

Die Applikations-Ebene kann auch als Anwendungsebene bezeichnet werden. Sie ist<br />

die Basis zur Entwicklung von Programmen, während die darunterliegenden Layer für<br />

einen Anwender weitgehend transparent und unzugänglich sind. Der Applikations-Layer<br />

belegt den rekonfigurierbaren Block einer ComputingPages mit individuellen Funktionsblöcken.<br />

Jede aktive ComputingPage bildet ein Objekt einer Klasse.<br />

2.3.6 Layer 6: SW-Engineering<br />

Durch Verknüpfung der einzelnen Objekte untereinander entstehen vollständige Threads<br />

und Programme. Die Verknüpfung der Objekte untereinander folgt dabei dem objektorientierten<br />

Paradigma der Modularisierung. Das heißt, die Objekte können einzig über<br />

Prozeduraufrufe auf andere Objekte zugreifen. Dazu muss das aufrufende Objekt die<br />

Adresse des Zielobjektes kennen. Ein Objekt kann durch Aufrufen eines Konstruktors


2.4. BEISPIEL 13<br />

die Anzahl der Objekte im Gesamtsystem erhöhen. Zum Start eines Programms wird ein<br />

initiales Objekt vom Host-System aus erstellt. Diese Objekte werden als Wurzelknoten<br />

bezeichnet und leben während der gesamten Programmausführung. Es ist ohne weiteres<br />

möglich mehrere Wurzelknoten und damit mehrere Threads oder Programme parallel<br />

auf dem FPGA ablaufen zu lassen.<br />

2.4 Beispiel<br />

Die Struktur des Anwendungessystems soll an einem Beispiel verdeutlicht werden. Abbildung<br />

2.4 zeigt das Programmablaufdiagramm für folgendes Codesegment:<br />

c l a s s a : Root {<br />

. . .<br />

Math m = new Math ( ) ;<br />

m. run ( ) ;<br />

P r i n t p = new P r i n t ( ) ;<br />

p . p r i n t (m) ;<br />

}<br />

c l a s s S q r t {<br />

i n t run ( i n t Op1 ) {<br />

return s q r t ( Op1 ) ;<br />

}<br />

c l a s s Math {<br />

void run ( ) {<br />

S q r t s = new S q r t ( ) ;<br />

r e s = s . g e t S q r t ( 3 . 1 4 1 ) ;<br />

s=NULL;<br />

return ( ) ;<br />

}<br />

}<br />

c l a s s P r i n t {<br />

p r i n t ( C l a s s a ) {<br />

. . .<br />

}<br />

}<br />

Das Beispiel beginnt während der Ausführung von Objekt “a” der Klasse “Root”.<br />

Man beachte, dass die Freigabe des Objektes “s” der Klasse “Sqrt” zu keinem vorher<br />

festgelegten Zeitpunkt erfolgt. Der Zeitpunkt wird durch die nächste, der Zuweisung<br />

“s = NULL” folgende, Aktivierung der <strong>Garbage</strong>-<strong>Collection</strong> bestimmt. Sobald mit<br />

“s = NULL” die Referenz auf das Objekt der Klasse “Sqrt” überschrieben wurde,<br />

ist dieses nicht mehr erreichbar und kann freigegeben werden.<br />

2.5 Vereinfachte Repräsentation eines Programms<br />

Für die <strong>Garbage</strong>-<strong>Collection</strong> ist die Verknüpfung zwischen den Objekten von besonderer<br />

Bedeutung, weil sie angibt, welche Objekte nicht mehr erreichbar sind und somit freigegeben<br />

werden können. Zwei Objekte sind dann miteinander verknüpft, wenn ein Objekt<br />

auf die Methoden eines anderen Objektes zugreifen kann. Hinreichende Bedingung für<br />

die Verknüpfung ist es, wenn ein Objekt A die Adresse eines anderen Objektes B kennt.<br />

Für eine Untersuchung der Eigenschaften eines <strong>Garbage</strong>-<strong>Collection</strong>-Algorithmus bietet<br />

es sich an, vom gegebenen Programm zu einem gerichteten Objektgraphen zu abstrahieren.<br />

Jeder Knoten des Graphen repräsentiert ein Objekt und die ausgehenden gerichteten<br />

Kanten geben an, welche anderen Objekte von ihm aus über ihre Adressen


14 KAPITEL 2. BEGRIFFE UND STRUKTUREN<br />

erreichbar sind. Der Graph kann über mehrere ausgezeichnete Wurzelknoten verfügen,<br />

die jeweils die Wurzel eines Threads repräsentieren.<br />

Der Objektgraph für das Beispiel aus Abschnitt 2.4 ist in Abbildung 2.4 dargestellt.<br />

Man sieht, dass verschiedene Operationen, wie Konstruktoraufrufe und lokale Variablenzuweisungen<br />

(“s = NULL”) den Objektgraphen verändern.<br />

Der Graph abstrahiert von der Funktionalität der Objekte, die für eine <strong>Garbage</strong>-<strong>Collection</strong><br />

unerheblich ist. Er beschränkt sich auf die Darstellung der Erreichbarkeit der<br />

Objekte. Die Wurzelknoten eines Programms, die im FPGA von extern erstellt werden,<br />

bilden auch die Wurzelknoten des Graphen.<br />

Im allgemeinen Fall können keine weiteren Aussagen bezüglich der Graphenstruktur<br />

gemacht werden. Trotzdem lassen sich einige zusätzliche heuristische Annahmen<br />

treffen, die darauf beruhen, dass Programme üblicherweise nach gewissen Regeln implementiert<br />

werden. In den meisten Systemen existieren langlebige Objekte, in denen<br />

für kurze Zeit temporäre Objekte angelegt werden. Diese temporären Objekte werden<br />

nur im Rahmen einer Operation benötigt und danach wieder gelöscht. Außerdem hat<br />

sich herausgestellt, dass ein Objekt meist mindestens ebensolang existiert, wie weitere<br />

von ihm erstellte Objekte (Kindobjekte).<br />

Dadurch entsteht eine hierarchische Objektstruktur, die einem Baum der Graphentheorie<br />

ähnelt. Die Tiefe eines Knotens, die als die Anzahl der Kanten zwischen einer<br />

Wurzel und dem Knoten definiert ist, korrespondiert mit seiner Lebensdauer. Die Blätter<br />

des Baums besitzen die höchste Tiefe und sind die kurzlebigsten Objekte. Sie werden<br />

als erstes wieder gelöscht.<br />

Trotzdem bleibt die Baumstruktur nur eine näherungsweise Beschreibung für die meisten<br />

Objektgraphen. Referenzen, die diese Baumstruktur zerstören, kommen ebenfalls<br />

vor, sind jedoch gegenüber den regulären Kanten, eher selten. Für die Entwicklung einer<br />

<strong>Garbage</strong>-<strong>Collection</strong> ist diese vereinfachte Sicht damit zumeist unzulänglich. Sie eignet<br />

sich jedoch gut für statistische Analysen der <strong>Garbage</strong>-<strong>Collection</strong>. Hierzu zählen etwa<br />

Aussagen über die Effizienz oder der Versuch, einen günstigen Zeitpunkt zum Start der<br />

<strong>Garbage</strong>-<strong>Collection</strong> zu finden.<br />

Es gibt verschiedene Untersuchungen, die versuchen, zur Optimierung der <strong>Garbage</strong>-<br />

<strong>Collection</strong> weitere Aussagen über die Struktur des Graphen zu treffen und von ihnen<br />

zu profitieren. Einige für diese Arbeit relevante Untersuchungen werden in Kapitel 5<br />

vorgestellt.


2.5. VEREINFACHTE REPRÄSENTATION EINES PROGRAMMS 15<br />

Instanzierung<br />

Procedure(Source,Target [,Operands,...])<br />

Dispatcher / Arbiter a : Root m : Math s : Sqrt p : Print<br />

Create(Math)<br />

Constructor(0,m)<br />

a<br />

Return(a,m)<br />

run(a,m)<br />

run(a,m)<br />

m<br />

a<br />

Create(Sqrt)<br />

Constructor(0,s)<br />

Return(m,s)<br />

m<br />

a<br />

getSqrt(m,s,"3.141")<br />

getSqrt(m,s,"3.141")<br />

Return(m,"1.77")<br />

Return(m,"1.77")<br />

Return(a)<br />

Return(a)<br />

m<br />

s<br />

a<br />

Create(Print)<br />

Constructor(0,p)<br />

Return(a,p)<br />

m<br />

s<br />

a<br />

print(a,p,"1.77")<br />

print(a,p,"1.77")<br />

m<br />

a<br />

p<br />

Abbildung 2.4: Programmablauf und Objektgraph


16 KAPITEL 2. BEGRIFFE UND STRUKTUREN


Kapitel 3<br />

<strong>Garbage</strong>-<strong>Collection</strong> in Software<br />

Mit dem Begriff <strong>Garbage</strong>-<strong>Collection</strong> (GC) bezeichnet man die automatische Speicherverwaltung<br />

eines objektorientierten Systems. Aufgabe der GC ist es, Objekte, die nicht<br />

mehr verwendet werden können, zu löschen und ihren Speicherbereich zur Wiederverwendung<br />

freizugeben. Ein Objekt kann dann nicht mehr verwendet werden, wenn es<br />

in anderen Objekte keine Referenzen auf dieses Objekt mehr gibt, es also nicht mehr<br />

erreichbar ist. Meyer [1] bietet einen kurzen Überblick über die Entwicklung der GC<br />

und ihre Verwendung in objektorientierten Systemen.<br />

Man unterscheidet zwischen zwei Prozessen: Der Mutator verändert den Inhalt des<br />

Speichers. Er enthält das eigentliche Programm und sollte idealerweise ohne Unterbrechung<br />

laufen. Der Collector implementiert die <strong>Garbage</strong>-<strong>Collection</strong>. Seine Aufgabe ist<br />

es zu gewährleisten, dass der Mutator immer in der Lage ist, ausreichend Speicher zu allozieren.<br />

Der Collector sollte den Mutator so wenig wie möglich behindern, ohne dabei<br />

seine Funktionalität einzubüßen.<br />

Die Funktionalität einer GC lässt sich in zwei Anforderung aufteilen:<br />

Definition 3.0.1 (Korrektheit)<br />

Jedes Objekt, das von der <strong>Garbage</strong>-<strong>Collection</strong> gelöscht wird, ist zuvor von keinem anderen<br />

Objekt aus mehr erreichbar.<br />

Definition 3.0.2 (Vollständigkeit)<br />

Alle nicht mehr erreichbaren Objekte werden erkannt und freigegeben.<br />

Die Bedingung der Vollständigkeit wird oft zugunsten einer effizienteren GC-Implementierung<br />

abgeschwächt. Mögliche Neuformulierungen der Vollständigkeit fordern<br />

dann, dass jedes nicht erreichbare Objekt irgendwann einmal gelöscht wird. Es wird<br />

nicht mehr gefordert, dass dies bereits beim nächsten GC-Lauf stattfindet. Eine andere<br />

Möglichkeit zur Abschwächung ist die Forderung, dass die GC in jedem Schritt ein<br />

garantiertes Minimum an nicht mehr erreichbaren Objekten löscht. Wichtig ist es, die


18 KAPITEL 3. GARBAGE-COLLECTION IN SOFTWARE<br />

Anforderungen des Mutators zum Allozieren neuer Objekte erfüllen zu können, solange<br />

Speicherbereiche existieren, die nicht mit aktiven Objekten belegt sind.<br />

Wie in Kapitel 2 erläutert, lassen sich die ComputingPages als Objekte einer objektorientierten<br />

Entwurfsmethodik interpretieren.<br />

Der Analogie folgend bildet das rekonfigurierbare FPGA den Speicher, in dem diese<br />

Objekte abgelegt sind. Auch die Konzepte der <strong>Garbage</strong>-<strong>Collection</strong> lassen sich mit dieser<br />

Analogie auf FPGAs übertragen. Ihre Aufgabe ist es, die verfügbaren rekonfigurierbaren<br />

Ressourcen zu verwalten.<br />

Neben der Korrektheit und Vollständigkeit ist das wesentliche Merkmal eines guten<br />

Algorithmus zur GC, dass das Programm bei seiner Ausführung so kurz wie möglich unterbrochen<br />

wird. Das Instantiieren eines neuen Objektes (die Anforderung eines freien<br />

Speicherbereiches) soll möglichst unterbrechungsfrei stattfinden. Dazu sind verschiedene<br />

Ansätze zur <strong>Garbage</strong>-<strong>Collection</strong> entwickelt worden, die im Folgenden kurz vorgestellt<br />

werden. Zusätzlich wird die Relevanz der Ansätze für rekonfigurierbare Hardware<br />

diskutiert, bevor in Kapitel 4 ein Ansatz für eine effiziente <strong>Garbage</strong>-<strong>Collection</strong> in rekonfigurierbarer<br />

Hardware entwickelt und in Kapitel 5 genauer untersucht wird.<br />

3.1 Statische Reservierung<br />

Bei der statischen Speicherreservierung gibt es keine eigentliche <strong>Garbage</strong>-<strong>Collection</strong>.<br />

Trotzdem soll dieses Prinzip hier der Vollständigkeit halber erwähnt werden. Bei dieser<br />

Art der Speicherverwaltung wird bereits beim Programmstart der gesamte benötigte<br />

Speicher reserviert. Dynamische Programme, bei denen der Speicherbedarf zur Laufzeit<br />

wachsen kann, sind damit nicht möglich. Vorteil dieser Art von Speicherverwaltung<br />

ist ihre Einfachheit: Man benötigt keine <strong>Garbage</strong>-<strong>Collection</strong> und kann daher harten<br />

Echtzeit-Anforderungen genügen, die man durch externe Speicherverwaltung nicht<br />

mehr erfüllen kann. Diese Art der Speicherverwaltung entspricht am ehesten dem klassischen<br />

Ansatz zur Entwicklung von Hardwaredesigns und ist häufig in “Embedded<br />

Systems” anzutreffen .<br />

Charakteristika einer statischen Reservierung<br />

• Keine dynamische Speicherreservierung und Freigabe<br />

• Sehr einfach<br />

• Erfüllt harte Echtzeit-Anforderung<br />

• Keine dynamischen Programme möglich<br />

• Analogie zur Klassischer Hardwareentwicklung


3.2. EXPLIZITE RESERVIERUNG 19<br />

3.2 Explizite Reservierung<br />

Bei der expliziten Speicherreservierung werden die Aufgaben der GC vom Programmierer<br />

übernommen. Dieser kann durch Kenntnis des Programmablaufs bestimmen, wann<br />

ein Objekt nicht mehr verwendet wird. Daraufhin gibt er den vorher vom Objekt belegten<br />

Speicher mit einem entsprechenden Befehl explizit frei. Bekanntestes Beispiel für<br />

dieses Konzept ist die Sprache C [8] mit den Befehlen malloc() und free(). In der Hardwareentwicklung<br />

entspricht es am ehesten den bestehenden Ansätzen zur Ausnutzung<br />

von dynamisch rekonfigurierbarer Hardware, bei denen ebenfalls der Entwickler die<br />

Rekonfiguration steuert. Vorteil dieser Variante ist die gute Kontrolle über das Zeitverhalten<br />

der Speicherverwaltung. Ein wesentlicher Nachteil ist die Komplexität der Softwareentwicklung.<br />

In großen Projekten ist es oft sehr schwer, den Überblick über die<br />

instantiierten Objekte zu behalten. Vergisst der Entwickler Objekte freizugeben, kann<br />

dies zu schwer nachvollziehbaren Fehlern (Memory Leaks) führen, die ein typisches<br />

Problem vieler Anwendungen bilden, die diese Art der Speicherverwaltung nutzen.<br />

Charakteristika einer expliziten Reservierung<br />

• Beispiel: “free” und “malloc” aus C<br />

• Einfach zu implementieren<br />

• Teilweise deterministisches Timing (in kritischen Teilen kein “free” verwenden)<br />

• Analogie zur normalen Hardware-Rekonfiguration<br />

3.3 Referenzzählung<br />

Die Referenzzählung [9] war der erste automatische Ansatz zur <strong>Garbage</strong>-<strong>Collection</strong>. Zu<br />

jedem Objekt wird ein Zähler mitgeführt, der angibt, wieviele Referenzen noch auf dieses<br />

Objekt verweisen. Wenn der Zähler auf Null geht, gibt es keine Referenzen mehr<br />

und das Objekt kann gelöscht werden. Um den Referenzzähler aktuell zu halten, ist<br />

es erforderlich, bei jeder Zuweisung einer Referenz (oder auch deren Löschen) den<br />

Referenzzähler zu inkrementieren und dekrementieren. Damit ist dieser Ansatz relativ<br />

zeitaufwändig. Vorteil des Ansatzes ist, dass sich der Mehraufwand gleichmäßig<br />

über das Programm verteilt und die Latenz einer Operation gut einschätzbar ist. Der<br />

Ansatz ermöglicht also die Einhaltung von weichen Echtzeitbedingungen. Als wesentlicher<br />

Nachteil bleibt zu erwähnen, dass die Referenzzählung nicht in der Lage ist eine<br />

zyklisch verkettete Liste korrekt als <strong>Garbage</strong> zu erkennen, weil alle diese Objekte noch<br />

einen Referenzähler > 0 besitzen.


20 KAPITEL 3. GARBAGE-COLLECTION IN SOFTWARE<br />

Charakteristika der Referenzzählung<br />

• Einfacher Ansatz<br />

• Kann keine Zyklen erkennen<br />

• Bei jeder Zuweisung ändert sich die Referenz (Häufiger Aufruf)<br />

• Weiche Echtzeitbedingungen<br />

3.4 Mark-Sweep <strong>Collection</strong><br />

Das Verfahren der Mark-Sweep-<strong>Collection</strong> ist heute das am häufigsten verwendete Verfahren<br />

zur automatisierten <strong>Garbage</strong>-<strong>Collection</strong>. Der Begriff Mark-Sweep-<strong>Collection</strong> wird<br />

in viele Arbeiten synonym mit dem Begriff <strong>Garbage</strong>-<strong>Collection</strong> verwendet.<br />

Bei diesem Verfahren arbeitet der Collector-Prozess in zwei Phasen: In der ersten Phase<br />

(Mark) werden ausgehend von der Wurzel alle erreichbaren Knoten (die Objekte)<br />

markiert. In der zweiten Phase wird der gesamte Speicher einmal linear durchlaufen<br />

(Sweep). Objekte, die nach diesen beiden Phasen nicht als erreichbar markiert sind,<br />

werden wieder freigegeben.<br />

Gegenüber der Referenzzählung gibt es bei diesem Verfahren keine Probleme mit isolierten<br />

Zyklen, weil sie ausgehend von der Wurzel aus nicht erreichbar sind und somit<br />

während der Mark-Phase auch nicht markiert werden.<br />

Das Verfahren ermöglicht es, ausgehend von einem beliebigen Knoten, kleinere Teilbäume<br />

zu traversieren. Damit kann man ohne den Aufwand einer vollständigen Traversierung<br />

nicht mehr erreichbare Objekte finden und freigeben. Diese Idee ist unter dem<br />

Begriff Generational <strong>Collection</strong> bekannt und wird in Kapitel 3.5.3 näher erläutert.<br />

Hauptnachteil der Mark-Sweep-<strong>Collection</strong> ist ihr schlechtes Echtzeitverhalten. Sie erfordert<br />

eine vollständige Traversierung des Objekt-Graphen im Mark-Prozess und eine<br />

lineare Traversierung des kompletten Speichers im Sweep-Prozess für einen Collector-<br />

Aufruf. Dadurch kann es zu starken Verzögerungen im Ablauf des Mutators kommen.<br />

Ein weiterer Nachteil dieser Art von <strong>Garbage</strong>-<strong>Collection</strong>ist die Fragmentierung des<br />

Speichers. Durch das Löschen von Objekten inmitten des linear angeordneten Speichers<br />

entstehen Lücken. Fordert die nächste Allozierung mehr Speicher an, als in einer<br />

einzelnen Lücke vorhanden ist, muss die Allozierung verhindert werden, obwohl insgesamt<br />

ausreichend freier Speicher vorhanden wäre. Um die Fragmentierung zu beseitigen<br />

muss der belegte Speicher so weit umsortiert werden, dass ausreichend zusammenhängender<br />

freier Speicher vorhanden ist. Eine effiziente Strategie für die Problematik der<br />

Fragmentierung zu finden, ist eine weitere Aufgabe eines GC-Algorithmus.<br />

In der Praxis hat sich die Mark-Sweep-<strong>Collection</strong> durchgesetzt. Grund dafür ist, neben<br />

der einfachen Implementierung, die gute Erweiterbarkeit um die Nachteile des Verfahrens<br />

zu vermindern oder zu beseitigen.


3.5. ERWEITERUNGEN DER MARK-SWEEP-COLLECTION 21<br />

Auch für Hardware bietet sich die Mark-Sweep-<strong>Collection</strong> an. Sie ermöglicht Erweiterungen<br />

und Mechanismen zur Nebenläufigkeit, die sich besonders gut für eine direkte<br />

Abbildung auf die inhärent nebenläufige Hardwareumgebung eignen. Das Problem der<br />

Fragmentierung ist aufgrund der homogenen Struktur der ComputingPages vernachlässigbar<br />

für unsere Implementierung vernachlässigbar.<br />

Charakteristika der Mark-Sweep-<strong>Collection</strong><br />

• Auch bekannt als <strong>Garbage</strong>-<strong>Collection</strong><br />

• Idee: Objekte bilden einen Baum, der traversiert wird<br />

• Findet auch isolierte Zyklen<br />

• Fragmentierung des Speichers<br />

• Ungünstiges Echtzeit-Verhalten<br />

• Normalerweise suspendiert der Prozess während dem Mark-Schritt<br />

• Zum nebenläufigen Algorithmus erweiterbar<br />

3.5 Erweiterungen der Mark-Sweep-<strong>Collection</strong><br />

Im Folgenden werden die Konzepte der wichtigsten Erweiterungen für die Mark-Sweep-<br />

<strong>Collection</strong> vorgestellt. Es wird zusätzlich jeweils kurz erwähnt, inwiefern sie für eine<br />

Hardwareimplementierung von Interesse sein könnten.<br />

3.5.1 Fragmentierung<br />

Die Copying <strong>Collection</strong> [10] ist ein Ansatz, das Problem der Fragmentierung des Speichers<br />

zu beseitigen. Bei dieser Art der <strong>Garbage</strong>-<strong>Collection</strong> werden zwei disjunkte, gleich<br />

große Speicherbereiche für das Programm (den Mutator) reserviert. Alle Objekte liegen<br />

gemeinsam in einem der beiden Speicherbereiche, der im Lauf der Programmlaufzeit<br />

fragmentiert. Während des GC-Zyklus werden alle aktiven Objekte in den anderen Bereich<br />

verschoben und dort wieder hintereinander im Speicher abgelegt. Danach wechseln<br />

die beiden Speicherbereiche ihre Funktion und der Speicher wird durch den Mutator<br />

bis zum nächste GC-Lauf wieder fragmentiert.<br />

Für die Hardwareimplementierung ist das Problem der Fragmentierung vernachlässigbar,<br />

da die technischen Rahmenbedingungen nur eine kleine Zahl gleichförmig großer<br />

ComputingPages zulassen.


22 KAPITEL 3. GARBAGE-COLLECTION IN SOFTWARE<br />

3.5.2 Nebenläufigkeit<br />

Eine wichtige Methode, die schlechten Echtzeitverhältnisse der Mark-Sweep-<strong>Collection</strong><br />

zu verbessern ist, Mutator und Collector als nebenläufige Prozesse zu realisieren. Im<br />

Idealfall kann der Mutator so ohne Unterbrechungen ablaufen, während der Collector<br />

parallel nicht mehr erreichbare Objekte findet und freigibt. Der Mutator muss nur unterbrochen<br />

werden, wenn der Collector nicht ausreichend schnell freie Objekte finden<br />

kann.<br />

Für eine dedizierte Hardwareimplementierung bietet sich ein solcher Ansatz an, da<br />

man die nebenläufigen Strukturen der Plattform optimal nutzen kann.<br />

Die Implementierung eines nebenläufigen Algorithmus ist sehr komplex. Insbesondere<br />

der Beweis der Korrektheit eines solchen Algorithmus erfordert aufwändige formale<br />

Verifikationsmethoden, wie sie zum Beispiel von Dijkstra [11] angewandt werden.<br />

Es lassen sich jedoch einige gemeinsame Aspekte für eine nebenläufige <strong>Garbage</strong>-<br />

<strong>Collection</strong> finden, die ein korrekter Algorithmus aufweisen muss. Huelsbergen [12],<br />

Ben-Ari [13], Doligez [14] und Domani [15] geben einige Beispiele für solche Algorithmen<br />

zusammen mit einem formalen oder semi-formalen Beweis ihrer Korrektheit.<br />

Für einen solchen Beweis muss man über atomare Operationen verfügen, die im Rahmen<br />

des GC-Algorithmus zum Einsatz kommen. Ohne solche Operationen lassen sich<br />

keine Aussagen und Invarianten über die Funktionalität eines nebenläufigen Algorithmus<br />

bilden. Für eine Hardwareimplementierung bietet das synchrone Systemdesign und<br />

das Kommunikationsprotokoll über den Arbiter eine passende Ausgangsbasis für atomare<br />

Operationen der <strong>Garbage</strong>-<strong>Collection</strong>.<br />

Neben atomaren Operationen muss man darüber hinaus die Funktionalität des Mutators<br />

während eines aktiven Collector-Zyklus in gewissem Rahmen einschränken. Normalerweise<br />

schränkt man entweder die Möglichkeiten zum Schreiben in den Speicher<br />

(write-barrier) oder das Lesen aus dem Speicher (read-barrier) ein. Ohne diese Einschränkungen<br />

könnte der Mutator durch Änderungen an der Objektstruktur während<br />

des aktiven Mark-Prozess die Korrektheit des Algorithmus verletzen. Abbildung 3.1<br />

illustriert dieses Problem an einem Beispiel. Die Zahlen geben die Reihenfolge der Traversierung<br />

während des Mark-Schrittes an, wobei von a) nach b) im Mutator eine Kante<br />

gelöscht und von b) nach c) eine neue Kante hinzugefügt wird. Der Algorithmus erkennt<br />

den Knoten “X” nicht als aktives Objekt, obwohl es offensichtlich erreichbar ist.<br />

Darüber hinaus bilden diese Barrieren die Basis für formelle Beweise zur Korrektheit<br />

einer nebenläufigen <strong>Garbage</strong>-<strong>Collection</strong>.<br />

Ein weiterer Nachteil der nebenläufigen Algorithmen bleibt neben ihrer höheren Komplexität<br />

gegenüber sequenziellen Verfahren, dass man bei ihnen die Vollständigkeit der<br />

<strong>Garbage</strong>-<strong>Collection</strong> aufgeben muss. Da der Mutator während der Mark-Traversierung<br />

weiter auf die Objektstruktur Zugriff hat, kann es dazu kommen, dass ein Objekte noch<br />

als erreichbar gekennzeichnet ist, obwohl es noch vor dem Ende der Traversierung durch<br />

den Mutator gelöscht wird. Solche Objekte werden im nächsten GC-Zyklus erkannt.


3.5. ERWEITERUNGEN DER MARK-SWEEP-COLLECTION 23<br />

1<br />

1<br />

1<br />

2 2 5<br />

2<br />

5<br />

3 4<br />

3 4 6<br />

X<br />

a) b) c)<br />

Abbildung 3.1: Inkorrekter Objektgraph bei nebenläufiger <strong>Garbage</strong>-<strong>Collection</strong><br />

Eine Erweiterung der Barrieren, um auch diese Problem-Konstellationen zu vermeiden,<br />

würde wieder direkt zu einem sequenziellen Algorithmus zurückführen.<br />

Üblicherweise nimmt man den Nachteil der nicht mehr erfüllbaren Vollständigkeit in<br />

Kauf, da man in den meisten Szenarien nicht in einem GC-Zyklus sämtlichen freizugebenden<br />

Speicher benötigt, sondern nur ausreichend viel freien Speicher, um bis zum<br />

übernächsten GC-Zyklus neue Objekte allozieren zu können.<br />

3.5.3 Generational <strong>Collection</strong><br />

Ein weiterer Ansatz, das schlechte Echtzeit-Verhalten der Mark-Sweep-<strong>Collection</strong> zu<br />

verbessern, ist die Generational <strong>Collection</strong>, die zuerst von Lieberman [18] vorgeschlagen<br />

wurde und auch unter dem Begriff Generation Scaveging bekannt ist. Dabei wird<br />

der Objektgraph in verschiedene Objekt-Klassen partitioniert. Die Klassen sind dabei<br />

über die Lebensdauer ihrer Objekte definiert.<br />

Der Mark-Sweep Prozess findet nun innerhalb der einzelnen Partitionen statt, wobei<br />

die Partition mit den kurzlebigeren Objekten entsprechend häufiger aufgeräumt wird<br />

als die der langlebigen Objekte. Dadurch wird erreicht, dass man eine kürzere Zeit für<br />

einen GC-Zyklus benötigt, da die einzelnen Partitionen weniger Objekte als das gesamte<br />

Programm enthalten. Zugleich wird die Wahrscheinlichkeit, ausreichend viele Objekte<br />

freizugeben, um ausreichend freien Speicher zur Verfügung zu haben, nicht zu sehr<br />

verringert, weil man in den häufig traversierten kurzlebigen Objekten auch die meisten<br />

wieder freizugebenden Kandidaten finden wird.<br />

Die Generational <strong>Collection</strong> erfordert in zwei Punkten einen Mehraufwand des GC-<br />

Algorithmus. Kanten, die im ursprünglichen Objektgraphen zwischen Objekten zweier


24 KAPITEL 3. GARBAGE-COLLECTION IN SOFTWARE<br />

verschiedener Partitionen liegen, müssen während der Traversierung zusätzlich überwacht<br />

werden, um zu verhindern, dass ein Objekt, welches nur aus einer anderen Partition<br />

heraus erreichbar ist, nicht versehentlich freigegeben wird.<br />

Ein zweites und grundlegenderes Problem ist die Vorhersage der Lebensdauer eines<br />

Objektes und damit seine Einordnung in die korrekte Partition von Objekten. In einem<br />

beliebigen Programm ist die Lebensdauer nicht vorhersehbar, weshalb hier Heuristiken<br />

zum Tragen kommen müssen.<br />

Der einfachste Ansatz, die Lebensdauer vorherzusagen, ist die so genannte weak generational<br />

hypothesis [18]. Sie besagt, dass: “Junge Objekte kürzer leben”. Das ermöglicht<br />

einfache Implementierungen, die auch in der Praxis zu guten Ergebnissen führen<br />

[16], [17]. Die Annahme beruht auf der intuitiven Beobachtung, dass in einem typischen<br />

Programm meist eine Gruppe von langlebigen Objekte existiert, die viele kurzlebige<br />

temporäre Objekte erstellen, die nach der Verwendung nicht mehr benötigt werden. Zur<br />

Implementierung werden neue Objekte immer in der kurzlebigen Klasse eingeordnet<br />

und zu den langlebigen Objekten verschoben, sobald sie einen GC-Zyklus überstanden<br />

haben.<br />

Komplexere Heuristiken verwenden noch weitere Informationen, wie zum Beispiel<br />

die Art der Referenz eines Objektes auf ein anderes [19]. Dabei wird ausgenutzt, dass<br />

Referenzen, die auf dem Stack des Programms liegen, meist zu kurzlebigeren Objekten<br />

führen als Referenzen im globalen Speicher.<br />

Charakteristika der Generational-<strong>Collection</strong><br />

• Bekannt als “Generation Scaveging”<br />

• Verbessert Echtzeit-Verhalten<br />

• Basiert auf der zukünftigen Lebensdauer der Objekte<br />

• Aufwändige Heuristiken


Kapitel 4<br />

<strong>Garbage</strong>-<strong>Collection</strong> in Hardware<br />

In diesem Kapitel wird ein <strong>Garbage</strong>-<strong>Collection</strong>-Algorithmus für Hardware (HW-GC)<br />

entworfen und entwickelt. Er eignet sich für eine hardwarebasierte Laufzeitumgebung,<br />

wie sie zum Beispiel in Embedded Systems vorkommen kann. Der Algorithmus basiert<br />

auf den Erkenntnissen, die in softwarebasierten Ansätzen entwickelt und in Kapitel 3<br />

vorgestellt worden sind. Er soll dabei besondere Rücksicht auf die Rahmenbedingungen<br />

der verwendeten Evaluationsplattform auf Basis eines FPGAs der Firma Xilinx nehmen.<br />

Diese Einschränkung durch äußere Rahmenbedingungen ist für die Entwicklung hardwarenaher<br />

Mechanismen typisch und stellt keinen Nachteil für die vorgestellten Methodiken<br />

dar, mit denen die bestehenden Algorithmen auf die Hardware adaptiert werden.<br />

In Kapitel 2 wurde bereits detailliert auf die Rahmenbedingungen der verwendeten<br />

Plattform eingegangen. Wesentlichen Einfluss auf die Entwicklung des Algorithmus haben<br />

die folgenden Punkte:<br />

• Die rekonfigurierbaren Module sind nebeneinander angeordnet.<br />

• Kommunikation zwischen den Modulen erfolgt ausschließlich über ein festgelegtes<br />

Interface, das möglichst kompakt gehalten wird.<br />

• Die Module verwenden ein gemeinsames Bussystem zum Datenaustausch. Das<br />

Kommunikationsprotokoll sieht vor, dass sämtliche Kommunikation über den zentralen<br />

Arbiter läuft.<br />

Diese Arbeit konzentriert sich auf die Mark-Sweep Ansätze (siehe Kapitel 3.4). Hauptgrund<br />

für diese Entscheidung ist die Tauglichkeit der Mark-Sweep <strong>Garbage</strong>-<strong>Collection</strong><br />

für eine Erweiterung zur Nebenläufigkeit. Selbst entworfene Hardware bietet ohnehin<br />

Nebenläufigkeit, so dass dieser Ansatz eine einfache und effiziente Implementierung<br />

eines nebenläufigen <strong>Garbage</strong>-<strong>Collection</strong>-Algorithmus verspricht. Des weiteren ist<br />

die Kommunikation zwischen einzelnen Objekten verhältnismäßig teuer. Ein Ansatz,<br />

der auf Reference-Counting (Abschnitt 3.3) basiert, erfordert eine Aktualisierung der


26 KAPITEL 4. GARBAGE-COLLECTION IN HARDWARE<br />

Referenz-Zähler bei jeder Zuweisung. Er wird durch die Kommunikation deutlich mehr<br />

beeinflusst als ein Mark-Sweep Ansatz, der nur zu wenigen Zeitpunkten aktiviert werden<br />

muss, um wieder Objekte freizugeben.<br />

4.1 Einordnung der <strong>Garbage</strong>-<strong>Collection</strong><br />

Eine weitere grundlegende Entscheidung ist die Einordnung der <strong>Garbage</strong> <strong>Collection</strong> in<br />

das Schichtenmodell aus Kapitel 2. Wie dort erläutert, lässt sich ein GC-Mechanismus<br />

entweder als Teil der Laufzeitumgebung in der Middleware, oder aber auf Anwendungsebene<br />

im Applikations-Layer implementieren.<br />

Wenn die <strong>Garbage</strong>-<strong>Collection</strong> auf Anwendungsebene realisiert wird, lässt sie sich innerhalb<br />

einer ComputingPage als normales Objekt realisieren. Als Mark-Sweep-Algorithmus<br />

benötigt die <strong>Garbage</strong>-<strong>Collection</strong> für jedes Objekt Zugriff auf deren Liste aller<br />

erreichbaren Objekte. Auf Anwendungsebene erfolgt das Auslesen dieser Liste über<br />

öffentlichen Methoden, die sämtliche Objekte implementieren müssen. Um den dabei<br />

entstehenden, erheblichen Kommunikationsaufwand zu vermeiden, ist die <strong>Garbage</strong>-<br />

<strong>Collection</strong> als Teil der Laufzeitumgebung realisiert. Sie verfügt über zusätzliche, eigene<br />

Kommunikationsmöglichkeiten (Drähte).<br />

Die Mechanismen zur Speicherverwaltung werden als Teil des Arbiters implementiert,<br />

der wiederum als Teil der Middleware parallel zu den frei konfigurierbaren ComputingPages<br />

existiert. Diese Lösung erlaubt es, spezielle Kommunikations- und Traversierungsstrukturen<br />

zu implementieren, welche die <strong>Garbage</strong>-<strong>Collection</strong> wesentlich beschleunigen.<br />

Eine zweite Entscheidung betrifft Systeme, die aus mehreren FPGAs bestehen und<br />

damit auch über mehrere Arbiter verfügen. Hier gibt es ebenfalls zwei Möglichkeiten:<br />

• Separate <strong>Garbage</strong>-<strong>Collection</strong> pro Chip: Sind die Kommunikationskanäle für die<br />

GC völlig transparent, dann reicht ein GC-Prozess aus. Es müssen Mechanismen<br />

entwickelt werden, ineffiziente Graphen-Traversierungen zu vermeiden. Diese<br />

treten auf, wenn häufiger als nötig Kanten über die Chip-Grenzen hinweg traversiert<br />

werden. Weil diese Kommunikationsart teurer als Traversierungen innerhalb<br />

der Objekte eines Chips ist, sollte sie vermieden werden.<br />

• Alternativ gäbe es die Möglichkeit, eine gemeinsame <strong>Garbage</strong>-<strong>Collection</strong> pro<br />

Chip zu realisieren, die mit Hilfe zusätzlicher Logik entsprechend synchronisiert<br />

werden muss.<br />

Mechanismen zur Unterstützung mehrerer Chips werden in dieser Arbeit nicht weiter<br />

betrachtet.


4.2. TRAVERSIERUNG 27<br />

4.2 Traversierung<br />

Kernstück und Hauptunterschied zur <strong>Garbage</strong>-<strong>Collection</strong> in Software ist die Traversierung<br />

im Mark-Prozess. Im Gegensatz zu einer klassischen Software Umgebung existieren<br />

in dem FPGA-basierten System weder ein gemeinsamer Speicher noch eine zentrale<br />

Memory-Mangement-Unit (MMU). Dies erfordert die Entwicklung völlig neuer Mechanismen,<br />

um die Referenzen, die in den ComputingPages abgelegt sind, auszulesen<br />

und zur nächsten ComputingPage weiterzuverfolgen.<br />

Ausgangspunkt der <strong>Garbage</strong>-<strong>Collection</strong> ist eine zentrale Komponente, die sich in der<br />

hierarchischen Struktur aus Kapitel 2 ähnlich wie der Arbiter einordnen lässt. Aufgabe<br />

dieser Komponente ist es, eine Liste der Wurzelknoten zu halten. Die Wurzelknoten<br />

entstehen, wenn über das Host-Interface ein neuer Prozess gestartet wird. An Ende der<br />

Verarbeitung wird das Ergebnis des Prozesses an das Host-System gesendet und der<br />

Wurzelknoten wieder gelöscht. Darüber hinaus muss die zentrale GC-Komponente entscheiden,<br />

wann ein neuer GC-Zyklus erforderlich ist, weil nicht mehr ausreichend viele<br />

freie ComputingPages zur Verfügung stehen. Um einen neuen GC-Zyklus zu starten,<br />

wird der normale Programmablauf gestoppt, der Mark- und der Sweep-Prozess werden<br />

durchgeführt und die nicht mehr erreichbaren Objekte werden der Liste der verfügbaren<br />

ComputingPages zugeteilt.<br />

Ein erster trivialer Ansatz zur Implementierung der Mark-Sweep-<strong>Collection</strong> wäre es,<br />

ausgehend von der zentralen Komponente sämtliche Referenzen des ersten Wurzelknotens<br />

auszulesen, lokal abzuspeichern und mit diesen Referenzen den Objektgraph weiter<br />

zu traversieren. Je nach verwendeter Speicherstruktur für die ausgelesenen Referenzen<br />

(Queue oder Stack) entsteht dadurch eine Breiten- oder Tiefensuche [20], die alle von<br />

der Wurzel aus erreichbaren Knoten besucht. In Abbildung 4.1 (linke Seite) wird solch<br />

eine Traversierung für einen einfachen Objektgraphen gezeigt. Für die Funktionalität<br />

der <strong>Garbage</strong>-<strong>Collection</strong> ist es zunächst unerheblich, ob eine Breiten- oder Tiefensuche<br />

implementiert wird.<br />

Der grundlegende Nachteil des beschriebenen Ansatzes ist die aufwändige Kommunikation.<br />

Es ist erforderlich, jede Referenz, die in einer aktiven ComputingPage vorkommt,<br />

zur zentralen <strong>Garbage</strong>-<strong>Collection</strong> -Komponente zu schicken. Sollte die gerade<br />

gelesene Referenz noch nicht als aktiv markiert sein, so muss in einem weiteren Kommunikationsschritt<br />

das referenzierte Objekt als aktiv markiert, und seine Referenzliste<br />

ausgelesen werden. Bei durchschnittlich n Referenzen pro ComputingPage und m aktiven<br />

ComputingPages auf dem FPGA ist es somit erforderlich, n ∗ m Referenzen zur<br />

zentralen Komponente hin und m Adressen zum Auslesen der nächsten zu traversierenden<br />

Komponente vom Arbiter zu den jeweiligen Objekten zurück zu schicken. Es sind<br />

also insgesamt n ∗ m + m Kommunikationsoperationen nötig.<br />

Unter Berücksichtigung der Randbedingungen, die eine Kommunikation mit dem Arbiter<br />

zu einer teuren Operation machen, ist ein Traversierungsschema wünschenswert,<br />

das mit wesentlich weniger Traversierungen auskommt. Eine effizientere Traversierung


28 KAPITEL 4. GARBAGE-COLLECTION IN HARDWARE<br />

wird durch verschiedene Ansätze ermöglicht. Durch eine nebenläufige Traversierung<br />

lassen sich mehrere Traversierungsschritte auf einmal durchführen. Im Idealfall lassen<br />

sich sämtliche Referenzen einer ComputingPage in einem einzigen Taktzyklus abarbeiten.<br />

Eine mögliche Traversierung hierfür ist in Abbildung 4.1 (rechte Seite) zu sehen.<br />

Anstelle von sieben Schritten bei der klassischen Tiefensuche (linke Seite) werden hier<br />

nur noch vier Schritte benötigt. Um diese Art der Traversierung zu gewährleisten, ist<br />

ein System notwendig, das es gestattet mit mehreren Modulen parallel zu kommunizieren.<br />

So ist in Schritt drei die zeitgleiche Aktivierung dreier verschiedener Knoten<br />

notwendig. Wegen den beschränkten Kommunikationsressourcen ist diese Forderung<br />

nicht erfüllbar.<br />

1<br />

1<br />

2<br />

6<br />

2<br />

2<br />

3<br />

5<br />

7<br />

3<br />

3 3<br />

4<br />

4<br />

a)<br />

b)<br />

Abbildung 4.1: Traversierungsreihenfolge eines Objektgraphen mit sequenzieller Traversierung<br />

mit Tiefensuche (a) und nebenläufiger Traversierung (b)<br />

In dieser Arbeit ist daher eine Struktur entwickelt worden, die durch Lokalisierung der<br />

Kommunikation eine mehrfache Nutzung des Bussystems innerhalb eines Zeitschritts<br />

erlaubt. Zudem wurde der Algorithmus dezentralisiert. Die Aufgabe der zentralen GC-<br />

Komponente ist es nur noch, die Traversierung in den Wurzelknoten anzustoßen, die<br />

ihrerseits die Traversierung in ihren erreichbaren Knoten selber anstoßen. Ein möglicher<br />

Flaschenhals der Kommunikation im Arbiter ist damit beseitigt.<br />

Die Idee dieses lokalisierten Ansatzes ist es, den Bus innerhalb jeder ComputingPage<br />

mit Hilfe von Registern in beide Richtungen zu isolieren. Anstelle eines einzigen<br />

Bussystems über alle Module hinweg, existieren damit mehrere kleinere Systeme. Jedes<br />

dieser kleineren Systeme kann somit zeitgleich genutzt werden. Eine bessere Auslastung<br />

der Systemressourcen ist nun möglich. Abbildung 4.2 verdeutlicht den Aufbau<br />

der Kommunikationsstrukturen zwischen beliebigen ComputingPages.<br />

Konkret enthält die Implementierung in jeder ComputingPage eine Liste aller Referenzen,<br />

die sie erreichen kann. Der GC besitzt nur die Liste der Wurzel-Knoten. Wenn ein<br />

GC-Zyklus startet, sendet er ein Signal zur Aktivierung an alle Wurzel-Knoten. Diese<br />

werden dadurch aktiviert, sie setzen ein Flag, das sie als besucht markiert und aktivieren<br />

ihrerseits alle ComputingPages aus der lokalen Referenzliste. Zur besseren Ausnutzung


4.2. TRAVERSIERUNG 29<br />

Computing Page (Adresse 3)<br />

Computing Page (Adresse 2)<br />

Computing Page (Adresse 1)<br />

LeftReferences RightReferences LeftReferences RightReferences<br />

LeftReferences<br />

RightReferences<br />

LeftOut<br />

RightIn<br />

LeftOut<br />

RightIn<br />

LeftOut<br />

RightIn<br />

LeftIn<br />

SweepOut<br />

Register<br />

RightOut LeftIn<br />

Register<br />

RightOut<br />

LeftIn<br />

Flag Flag Flag<br />

RightOut<br />

SweepIn SweepOut<br />

SweepIn SweepOut<br />

SweepIn<br />

Register<br />

Abbildung 4.2: Lokalisierter Bus zur <strong>Garbage</strong>-<strong>Collection</strong><br />

der Systemressourcen ist die Referenzliste aufgeteilt in Referenzen, die links, und solche,<br />

die rechts vom aktuellen Knoten liegen. Referenzen, die auf Objekte links von der<br />

aktuellen Position verweisen, brauchen nur über die lokalisierten Datenleitungen zu den<br />

linken Nachbarn gesendet werden. Der Bus in die rechte Richtung ist damit für andere<br />

Signale frei. Trotz einer besseren Ausnutzung der Ressourcen kann es zu Konflikten<br />

kommen, wenn ein Modul gleichzeitig ein von links kommendes Aktivierungssignal<br />

weiterleiten und selber ein Aktivierungssignal nach rechts senden will. Um hierbei Kollisionen<br />

zu vermeiden, dürfen Objekte den Bus nur dann selber verwenden, wenn sie<br />

nicht gerade ein anderes Signal weitersenden.<br />

Abbildung 4.3 zeigt beispielhaft eine Traversierung in der implementierten Datenstruktur.<br />

Jede der fünf nebeneinander angeordneten ComputingPages enthält ein Register<br />

mit Referenzen, die nach links und ein zweites Register für Referenzen, die nach<br />

rechts verweisen. Die Zahlen auf den Kanten entsprechend den Signalen, die zu einem<br />

festgelegten Zeitschritt auf den lokalisierten Bussystemen gesendet werden, die Zeilen<br />

entsprechen den einzelnen Zeitschritten der Traversierung. Zum Zeitschritt t = 4 tritt<br />

in der ComputingPage 3 einer der beschriebenen Buskonflikte auf. Das Modul muss<br />

das von rechts kommende Signal weiterleiten, und kann die eigene Referenz erst im<br />

nächsten Schritt versenden.<br />

Nach abgeschlossener Traversierung ist es im Sweep-Schritt erforderlich, der zentralen<br />

<strong>Garbage</strong>-<strong>Collection</strong>-Komponente im Arbiter mitzuteilen, welche ComputingPages bei<br />

der Traversierung aktiviert worden sind. Hierzu dient ein ein Bit breites Schieberegister,<br />

das als Sweep-Leitung bezeichnet wird. Bei der Traversierung wurde in jedem aktivierten<br />

Modul ein entsprechendes Flag gesetzt. Diese Flags sind in dem Schieberegister<br />

hintereinandergeschaltet und werden schrittweise zum Arbiter verschoben. Dieser setzt<br />

daraufhin für jedes nicht gesetzte Flag die korrespondierende ComputingPage auf die<br />

Liste der verfügbaren freien Logikressourcen.<br />

Ein weiteres Problem ist, dass bei der dezentralen Traversierung nicht fest steht, wann


30 KAPITEL 4. GARBAGE-COLLECTION IN HARDWARE<br />

Zeit<br />

ID 5 ID 4<br />

ID 3<br />

ID 2<br />

ID 1<br />

t=1<br />

1<br />

4<br />

5<br />

1<br />

3<br />

4<br />

5<br />

2<br />

FromArbiter<br />

ID 5<br />

ID 4<br />

ID 3<br />

ID 2<br />

ID 1<br />

t=2<br />

1<br />

4<br />

5<br />

1<br />

3<br />

4<br />

2<br />

5<br />

FromArbiter<br />

ID 5<br />

ID 4<br />

ID 3<br />

ID 2<br />

ID 1<br />

t=3<br />

1<br />

4<br />

5<br />

1<br />

3<br />

4<br />

FromArbiter<br />

activated<br />

ID 5<br />

ID 4<br />

ID 3 ID 2<br />

ID 1<br />

t=4<br />

1<br />

4<br />

5<br />

1<br />

4<br />

FromArbiter<br />

activated<br />

activated<br />

ID 5<br />

ID 4<br />

ID 3 ID 2<br />

ID 1<br />

t=5<br />

1<br />

4<br />

5<br />

1<br />

FromArbiter<br />

activated<br />

activated<br />

activated<br />

ID 5<br />

ID 4<br />

ID 3 ID 2<br />

ID 1<br />

5<br />

t=6<br />

1<br />

FromArbiter<br />

activated<br />

activated<br />

activated<br />

activated<br />

ID 5<br />

ID 4<br />

ID 3 ID 2<br />

ID 1<br />

5<br />

t=7<br />

1<br />

FromArbiter<br />

activated<br />

activated<br />

activated<br />

activated<br />

ID 5<br />

ID 4<br />

ID 3 ID 2<br />

ID 1<br />

t=8<br />

FromArbiter<br />

activated<br />

activated<br />

activated<br />

activated<br />

activated<br />

Abbildung 4.3: Ablauf einer Traversierung im HW-GC-Algortihmus


4.3. FRAGMENTIERUNG 31<br />

sämtliche, lokal ablaufenden Aktivierungen zu Ende sind. Diese Problem wird mit Hilfe<br />

eines "Heartbeat”-Signals gelöst, das zum ressourcensparen ebenfalls auf der Sweep-<br />

Leitung realisiert ist. Eine ComputingPage, die aktiviert wurde und noch nicht zu allen<br />

Einträge aus ihrer Referenzliste ein Aktivierungssignal gesendet hat, schickt auf der<br />

Sweep-Leitung eine ’1’ in Richtung des Arbiters. Weil die Sweepleitung als Schieberegister<br />

realisiert ist, die in jeder ComputingPage ein Register besitzt, erhält man somit<br />

bei n Objekten nach maximal 2 ∗ n Takten eine ’1’ in der zentralen GC-Einheit. Dieser<br />

maximale Fall tritt dann ein, wenn das entfernteste Objekt aktiviert wird. Das Aktivierungssignal<br />

für das Objekt benötigt n Takte um die Register der dazwischenliegenden<br />

Module zu durchlaufen. Noch einmal n Takte sind notwendig, damit das “Heartbeat”-<br />

Signal durch das Schieberegister wieder zurück gelangt. Trifft also innerhalb von 2 ∗ n<br />

Takten keine “Heartbeat”-Signal in der zentralen GC-Komponente ein, so ist die Traversierung<br />

des Mark-Prozess abgeschlossen. Mit Hilfe eines globalen Enable-Signals<br />

wird daraufhin der Sweep-Modus aktiviert, um die Liste der erreichbaren Objekte zu<br />

generieren.<br />

Mögliche Lösungen, die in der Lage wären, in weniger als 2 ∗ n Takten das Ende der<br />

Traversierung zu bestimmen, benötigen weitere Routingressourcen. Aus diesem Grund<br />

wurde auf sie verzichtet.<br />

4.3 Fragmentierung<br />

In einem softwarebasierten, objektorientierten System gibt es zwei Arten, auf die sich<br />

eine Fragmentierung des Speichers auswirken kann (siehe Abschnitt 3.5.1). Wird der<br />

Speicher in zu viele kleine freie Bereiche fragmentiert, so besteht die Möglichkeit, das<br />

eine Anforderung für eine zusammenhängende Menge freien Speicher, wie sie etwa<br />

beim Erstellen eines Objekts vorkommt, von der Speicherverwaltung nicht erfüllt werden<br />

kann. In diesem Fall ist zuerst eine zeitaufwändige Defragmentierung des Speichers<br />

notwendig. Die meisten <strong>Garbage</strong>-<strong>Collection</strong>-Algorithmen für Softwaresysteme beinhalten<br />

Mechanismen, um die Fragmentierung des Speichers gering zu halten.<br />

Für den vorgestellten hardwarebasierten Entwicklungsansatz existiert dieser Fall von<br />

Fragmentierung nicht. Ihre Ursache liegt in der Anforderung von Speicherbereichen<br />

verschiedener Größe. Da sämtliche ComputingPages eine konstante Größe besitzen und<br />

entweder vollständig belegt oder vollständig verfügbar sind, kann keine Fragmentierung<br />

auftreten.<br />

Eine zweite Art der Fragmentierung tritt bei einer Softwareimplementierung durch<br />

den Verlust von Lokalität auf. Die Caching-Mechanismen eines modernen Prozessors<br />

erlauben es, auf nah beieinander liegende Speicherbereiche schneller zu zugreifen, als<br />

auf beliebige, weiter entfernt liegende Speicherbereiche. Ist ein Speicher stark fragmentiert,<br />

so liegen auch die Speicherbereiche der Objekte nicht mehr nah beieinander.<br />

Überraschenderweise existiert in der Hardwareimplementierung ein ähnliches Phäno-


32 KAPITEL 4. GARBAGE-COLLECTION IN HARDWARE<br />

men. Durch die Registerstruktur auf den Signalleitungen benötigt ein Signal länger, um<br />

weiter entfernt liegenden ComputingPages zu erreichen. So ist nur ein Taktzyklus erforderlich,<br />

um ein benachbartes Objekt zu aktivieren. Liegt das zu aktivierende Objekt<br />

hingegen am anderen Rand des FPGAs, so braucht das Signal mehrere Takte, um sämtliche<br />

Register zu durchlaufen. Es existiert also eine bestimmte Zuordnung der Objekte<br />

eines Programms zu den ComputingPages, die eine minimale Traversierungsdauer gewährleistet.<br />

Nutzt man die Möglichkeiten eines FPGAs, ein Modul samt seiner aktuellen<br />

Registerbelegung auszulesen und das Modul an anderer Position wieder auf den Chip<br />

zu laden, so ist man in der Lage die Lokalitätsstruktur zu modifizieren. Bisher existiert<br />

weder ein Algorithmus noch eine Heuristik, um zu bestimmen, wie eine geometrische<br />

Anordnung der ComputingPages aussehen muss, damit die Dauer der Traversierung<br />

minimiert wird. Darüber hinaus benötigt das Verschieben von rekonfigurierbaren Modulen<br />

zu viel Zeit, um praktikabel zu sein.<br />

4.4 Nebenläufigkeit<br />

Zur Implementierung einer nebenläufigen <strong>Garbage</strong>-<strong>Collection</strong> gibt es verschiedene Möglichkeiten.<br />

Kernelement aller solcher Algorithmen sind Barrieren, die in Abschnitt 3.5.2<br />

bereits vorgestellt wurden. Zweck dieser Barrieren ist es, gewisse Operationen einzuschränken<br />

um invariante Bedingungen aufzustellen. Diese Invarianzen erlauben eine<br />

nebenläufige <strong>Garbage</strong>-<strong>Collection</strong>.<br />

Herkömmliche Ansätze aus Softwareimplementierung bilden zumeist entweder eine<br />

read-barrier, die das Lesen von Daten einschränkt, oder aber eine write-barrier, um die<br />

Aktualisierung von Referenzen währenden einem GC-Lauf einzuschränken.<br />

Zum Entwurf eines sinnvollen Algorithmus für dynamisch rekonfigurierbare Systeme<br />

ist es sinnvoll, eine Liste möglicher Operationen aufzustellen, durch die sich der Objektgraph<br />

ändern kann. Ein Objekt A kann drei verschiedene Arten von Operationen<br />

durchführen, die Einfluss auf den Objektgraph haben:<br />

1. Der Aufruf eines Konstruktors lässt den Arbiter ein weiteres Objekt B instanziieren.<br />

Die Adresse dieses neuen Objekts wird dem aufrufenden Objekt zurückgeliefert.<br />

Der Objektgraph wird um eine gerichtete Kante vom Knoten A zum Knoten<br />

(B) erweitert.<br />

2. Beim Aufrufen einer Methode eines anderen Objekts kann dieses als Rückgabewert<br />

eine Referenz auf ein Objekt C liefern. Auch hier wird der Objektgraph um<br />

eine gerichtete Kante vom Knoten A zum Knoten C erweitert.<br />

3. Durch Operationen innerhalb eines Objektes können Referenzen gelöscht werden<br />

(s = NULL). Hierbei wird die Kante vom Objekt A zum Objekt s entfernt.


4.4. NEBENLÄUFIGKEIT 33<br />

Neue Kanten können durch diesen Fall nicht entstehen, da für das Objekt A keine<br />

Möglichkeit besteht, an weitere neue Referenzen zu kommen.<br />

Das Hinzufügen von Kanten, während in einem nebenläufigen Prozess die <strong>Garbage</strong>-<br />

<strong>Collection</strong> aktiv ist, erzeugt eine andere Art von Fehlern, als das Entfernen von Kanten.<br />

Durch das Hinzufügen von Kanten kann es zu Situationen kommen, wie sie in<br />

Abschnitt 3.5.2 in der Abbildung 3.1 dargestellt sind. Diese Fehler sind unbedingt zu<br />

vermeiden.<br />

Für die Entfernung von Kanten kann man den Objektgraphen in zwei Mengen unterteilen;<br />

Knoten die bereits traversiert worden sind und Knoten die noch traversiert werden<br />

müssen. Eine getrennte Betrachtung für den Knoten, der aktuell traversiert wird, ist hier<br />

nicht notwendig. In einem taktsynchronen Design lassen sich der Besuch eines Knotens<br />

und das Löschen einer Referenz (einer Kante im Objektgraph) als atomare Operation<br />

betrachten. Wird eine Kante aus der zweiten Menge gelöscht, so stellt dies kein Problem<br />

da. Die Traversierung wird korrekt erkennen, dass die Kante nicht mehr existiert,<br />

sobald die entsprechenden Postion im Graphen erreicht worden ist. Sämtliche Knoten<br />

aus dem abgearbeiteten Bereich sind bereits als aktiv markiert. Wird hier eine Kante<br />

gelöscht, so besteht die Möglichkeit, dass dadurch ein Objekt unerreichbar wird. Auf<br />

die Funktionalität des Programms hat dies Fall keinen Einfluss, weil das Objekt ja nicht<br />

mehr verwendet wird. Im nächsten GC-Zyklus wird das Objekt dann korrekt als nicht<br />

mehr erreichbar erkannt und verarbeitet. Damit ist es nicht notwendig, lokale Operationen<br />

in einem Objekt, die höchstens Referenzen löschen können, einzuschränken.<br />

Weil nicht mehr alle Objekte innerhalb eines GC-Zyklus zuverlässig als unerreichbar<br />

erkannt werden können, wird die Vollständigkeit aus Definition 3.0.2 zu einer partiellen<br />

Vollständigkeit abgeschwächt.<br />

Definition 4.4.1 (Paritelle Vollständigkeit)<br />

Alle nicht mehr erreichbaren Objekte werden innerhalb von höchstens zwei aufeinander<br />

folgenden GC-Zyklen erkannt und freigegeben.<br />

Die beiden Operationen zum Hinzufügen von Referenzen verwenden zur Kommunikation<br />

das Bus-System und damit den Arbiter. Gibt der Arbiter während eines aktiven<br />

GC-Zyklus also keinem Objekt die Berechtigung den Bus zu verwenden, so kann auch<br />

kein Objekt neue Referenzen erhalten. Diese Deaktivierung des Bus-Systems bildet eine<br />

kombinierte Lese- und Schreibbarriere. Allerdings ist diese Barriere auf globale Kommunikation<br />

(also Kommunikation zwischen verschiedenen Objekten) beschränkt. Der<br />

Ablauf innerhalb einer ComputingPage bleibt davon unbeeinflusst.<br />

Der Sweep-Prozess basiert nur auf den Flags, die während der Traversierung des<br />

Mark-Prozesses gesetzt wurden. Eine Modifikation dieser Register durch normale Methoden<br />

der Objekte ist nicht nötig. Daher ist es möglich, die Barriere im Arbiter bereits<br />

nach dem Mark zu deaktivieren. Sweep-Prozess und normaler Programmablauf können<br />

dann ohne Einschränkungen parallel ablaufen.


34 KAPITEL 4. GARBAGE-COLLECTION IN HARDWARE<br />

4.5 Generational <strong>Collection</strong><br />

Die Generational <strong>Collection</strong> (siehe Abschnitt 3.5.3) ist eine Methode, um in einem GC-<br />

Zyklus nur eine Partition der aktiven Objekte auf nicht mehr erreichbare Objekte zu<br />

untersuchen. Diese Teilmengen sind so gewählt, dass sie mit höherer Wahrscheinlichkeit<br />

wieder freizugebende Objekte enthalten. Aufgrund der geringen Anzahl an maximal<br />

realisierbaren Objekten auf der aktuellen Plattform und dem hohen Verwaltungsaufwand,<br />

den die Generational <strong>Collection</strong> mit sich bringt, ist dieses Konzept nicht Bestandteil<br />

einer <strong>Garbage</strong>-<strong>Collection</strong> in Hardware.<br />

Ein möglicher Einsatz für die Zukunft wäre der Einsatz von Methoden der Generational<br />

<strong>Collection</strong> für die Auswahl eines Wurzelknotens zum Starten der <strong>Garbage</strong>-<br />

<strong>Collection</strong>. Falls mehrere Threads (und damit mehrere Wurzelknoten) auf dem FPGA<br />

zeitgleich ablaufen, wäre es wünschenswert, die <strong>Garbage</strong>-<strong>Collection</strong> auf dem Thread<br />

zu starten, der mit größerer Wahrscheinlichkeit nicht mehr erreichbare Objekte enthält.<br />

Hierzu könnten die entsprechenden Heuristiken verwendet werden. Es bleibt trotzdem<br />

ein nicht unerheblicher Mehraufwand in der Verwaltung der Objekte, um zwischen tatsächlich<br />

nicht mehr erreichbaren Objekten und Objekten der anderen Threads zu unterscheiden.<br />

Aus diesem Grund wurde auf eine entsprechenden Berücksichtigung bei der<br />

Implementierung verzichtet.<br />

4.6 Der Algorithmus der Hardware-<strong>Garbage</strong>-<strong>Collection</strong><br />

(HW-GC)<br />

Im Folgenden werden die Ansätze, die in den letzten Abschnitten entwickelt worden<br />

sind, zu einem konkreten Algorithmus zusammengefasst. Dies sind insbesondere das lokalisierte<br />

Traversierungsschema, sowie die nebenläufige Implementierung von <strong>Garbage</strong>-<br />

<strong>Collection</strong> und normalen Programmablauf.<br />

Dieser HW-GC Algorithmus ist speziell auf die Datenstruktur der verwendeten FPGA-<br />

Plattform ausgelegt. Die für die <strong>Garbage</strong>-<strong>Collection</strong> relevanten Punkte lauten in kurzen<br />

Stichworten zusammengefasst:<br />

• Jedes Objekt (jede ComputingPage) führt eine Liste mit den Referenzen aller Objekte,<br />

auf die sie zugreifen kann. Diese Liste wird ausschließlich vom Objekt<br />

selber modifiziert. Der <strong>Garbage</strong> Collector kann sie auslesen.<br />

• Neue Einträge können nur in zwei Fällen in die Referenzliste eingetragen werden:<br />

Erstens, wenn das Objekt ein neues Kind-Objekt erzeugt, oder zweitens, wenn<br />

eine neue Referenz als Ergebnis beim Methodenaufruf eines anderen Objektes<br />

zurückgeliefert wird. Beide Fälle erfordern eine Kommunikation über den Arbiter.


4.6. DER ALGORITHMUS DER HARDWARE-GARBAGE-COLLECTION<br />

(HW-GC) 35<br />

• Ohne Kommunikation mit dem Arbiter kann ein Objekt nur interne Funktionen<br />

abarbeiten. Dabei können Referenzen aus der Referenzliste gelöscht werden (z.B.<br />

s = NULL) aber keine neuen hinzukommen. Für neue Referenzen wäre in jedem<br />

Fall eine Kommunikation (Methodenaufruf oder Instantiierung) über den Arbiter<br />

nötig.<br />

• Es gibt innerhalb eines Prozesses immer nur ein aktives Objekt. Diese Bedingung<br />

ist für die Funktionalität unerheblich, hilft aber bei der späteren Analyse der Leistungsfähigkeit<br />

erheblich weiter. Im Sinne der <strong>Garbage</strong>-<strong>Collection</strong> ist die Anzahl<br />

der aktiven Knoten unerheblich. Wesentlich ist nur, dass zur Kommunikation untereinander<br />

ausschließlich der vom Arbiter verwaltete Bus zum Einsatz kommt.<br />

• Wenn man die Kommunikation über den Arbiter deaktiviert, können sich die Referenzlisten<br />

nur noch verkleinern. In dem aufgespannten Objektgraph können also<br />

keine neuen Kanten eingefügt werden. Bei der Traversierung dieses Baumes<br />

mittels eines Mark-Sweep-Algorithmus ist garantiert, dass sämtliche nicht markierten<br />

Objekte gelöscht werden dürfen.<br />

Damit ergibt sich der konkrete Algorithmus HW-GC. Zunächst wird der Algorithmus<br />

informell beschrieben:<br />

Algorithmus 1 Algorithmus HW-GC in einfachem Pseudocode<br />

1: Loop<br />

2: Deaktiviere vor der Mark-Phase die Kommunikation über den Arbiter<br />

3: Aktiviere alle ComputingPages, die in der Liste der Wurzelknoten referenziert<br />

sind<br />

4: Wiederhole<br />

5: Wenn (ein Objekt wurde aktiviert) dann<br />

6: Setze ein internes Flag<br />

7: Sende ein Heartbeat-Signal zum Arbiter<br />

8: Aktiviere alle Objekte, die in der Referenzliste enthalten sind<br />

9: Ende<br />

10: Bis die Traversierung abgeschlossen ist<br />

11: Die Kommunikation über den Arbiter wird wieder freigeschaltet<br />

12: Lese das Sweep-Signal aus und erstelle daraus die Liste der nicht besuchten Module.<br />

Diese können rekonfiguriert werden.<br />

13: Wiederhole<br />

14: Normaler Programmablauf. Es findet keine Speicherbereinigung statt.<br />

15: Bis ein neuer GC-Zyklus erforderlich ist<br />

16: Loop Ende<br />

Dieser Ansatz kommt auch mit mehreren lokalen Threads zurecht. Diese werden durch


36 KAPITEL 4. GARBAGE-COLLECTION IN HARDWARE<br />

mehrere Einträge in der Liste der Wurzelknoten realisiert. Eine Erweiterung auf mehrere<br />

zusammengeschaltete FPGAs ist ebenfalls möglich. Durch das abstrakte Adressierungsschema<br />

ist es möglich, die Kommunikation über die Grenzen eines Bausteins hinweg<br />

für den <strong>Garbage</strong> Collector transparent zu gestalten. In diesem Fall ist das Problem der<br />

Fragementierung zu berücksichtigen. Da die Kommunikation zwischen den verschiedenen<br />

Bausteinen voraussichtlich teurer ist als die lokale Kommunikation, sollten bei der<br />

<strong>Garbage</strong>-<strong>Collection</strong> so selten wie möglich Signale zur Aktivierung eines Moduls über<br />

Chip-Grenzen hinweg laufen. Wenn ein Modul über zwei verschiedene Kantenpfade erreichbar<br />

ist, dann sollte der Pfad bevorzugt werden, der lokal verläuft, selbst wenn er<br />

aus mehr Kanten besteht.<br />

Die Beschreibung des Algorithmus in Pseudocode ist zu Gunsten der Verständlichkeit<br />

semantisch uneindeutig. Das gleichzeitige Ablaufen von mehreren nebenläufigen Prozessen<br />

ist mit den üblichen Mitteln zur Beschreibung solcher Algorithmen nur schwer<br />

zu modellieren. Eine exaktere Darstellung des HW-GC-Algorithmus ist in Algorithmus<br />

4.4 dargestellt. Zur Beschreibung wird die Hardwarebeschreibungssprache VHDL<br />

verwendet. Einige komplexe Operationen sind zu informellen Beschreibungen vereinfacht<br />

worden. Der Code besteht aus den zwei wesentlichen Komponenten zur <strong>Garbage</strong>-<br />

<strong>Collection</strong>. In der linken Spalte ist die Funktionalität des Arbiters angegeben, die rechte<br />

Spalte enthält die Funktionalität einer einzelnen ComputingPage. Der vorgestellte Algorithmus<br />

kommt mit einem Minimum an Datenleitungen aus.<br />

Die Schnittstelle zwischen zwei ComputingPages besteht jeweils aus zwei unidirektionalen<br />

Leitungen, um Aktivierungssignale in Form von Adressen zu übertragen. In der<br />

aktuellen Implementierung sind die Adressen mit vier Bit Breite implementiert. Es wird<br />

also ein acht Bit breiter Bus zwischen zwei Modulen benötigt. Darüberhinaus benötigt<br />

der Algorithmus ein globales Enable-Signal zum Aktivieren der <strong>Garbage</strong>-<strong>Collection</strong>.<br />

Das Sweep-Signal muss seine Daten lediglich ein Richtung des Arbiters weiterleiten.<br />

Es kann also ebenfalls mit einem Bit realisiert werden.<br />

Eine weitere Optimierung der benötigten Systemressourcen ist möglich, weil während<br />

des Mark-Prozesses das normale Bus-System nicht verwendet wird. In dieser Zeit können<br />

die bereits vorhanden Datenstrukturen benutzt werden, um die Module zu aktivieren.<br />

Damit benötigt die <strong>Garbage</strong>-<strong>Collection</strong> gegenüber der bisherigen objektorientierten<br />

Hardwareumgebung lediglich zwei weitere Leitungen:<br />

1. Das Enable-Signal der <strong>Garbage</strong>-<strong>Collection</strong><br />

2. Das Sweep-Signal.


4.6. DER ALGORITHMUS DER HARDWARE-GARBAGE-COLLECTION<br />

(HW-GC) 37<br />

e n t i t y A r b i t e r i s<br />

port (<br />

SweepIn : in s t d _ l o g i c ;<br />

EnableGC : out s t d _ l o g i c ;<br />

L e f t O u t : out t A d d r e s s ;<br />

) ;<br />

case GCState i s<br />

−− normal program i s r u n n i n g<br />

−− w a i t u n t i l n e x t GC−Cycle<br />

when I d l e =><br />

i f ( DelayCounter = MaxTime ) then<br />

GCState <br />

EnableGC


38 KAPITEL 4. GARBAGE-COLLECTION IN HARDWARE


Kapitel 5<br />

Analyse des HW-GC-Algorithmus<br />

In diesem Kapitel sollen die Anforderungen und Möglichkeiten des Algorithmus zur<br />

hardwarebasierten <strong>Garbage</strong>-<strong>Collection</strong> (HW-GC) analysiert werden. Insbesondere soll<br />

ein Versuch unternommen werden, die Leistungsfähigkeit des HW-GC-Algorithmus zu<br />

bewerten. Mit dem Mark-Sweep-Algorithmus zur Speicherverwaltung und der Betrachtung<br />

einer ComputingPage als Objekt existieren zwischen der Infrastruktur eines herkömmlichen<br />

Desktop-PCs und der verwendeten FPGA-Plattform einige Gemeinsamkeiten.<br />

Trotzdem erschweren substanzielle Unterschiede den Vergleich der <strong>Garbage</strong>-<br />

<strong>Collection</strong> zwischen den beiden Systemen. Insbesondere Verfahren zur Analyse einer<br />

softwarebasierten Speicherverwaltung lassen sich nicht ohne weiteres auf den implementierten<br />

HW-GC abbilden.<br />

Von besonderem Interesse sind vor allem drei Gesichtspunkte:<br />

• Das FPGA erlaubt nur eine eingeschränkte Anzahl von Objekten. Die Plattform<br />

ist nur unzureichend auf die Verwaltung und Instanziierung vieler ComputingPages<br />

zugeschnitten. Eine Analogie hierzu wäre eine Softwareumgebung mit stark<br />

eingeschränktem Speicherplatz. Für solche Plattformen sind GC-Mechanismen<br />

und die zugehörigen Analysen jedoch unüblich.<br />

• Die Hardwareplattform bietet Möglichkeiten zur Implementierung eines voll synchronen<br />

und nebenläufigen Designs. In einer Softwareumgebung sind diese Möglichkeiten<br />

nicht gegeben.<br />

• Die verwendete Datenstruktur zum Speichern der Objekte besteht aus mehreren<br />

ComputingPages die über einen Arbiter miteinander kommunizieren. Die Struktur<br />

unterscheidet sich damit deutlich vom linear angeordneten Speicher eines<br />

Heaps, wie er in einer softwarebasierten Laufzeitumgebung zum Einsatz kommt.


40 KAPITEL 5. ANALYSE DES HW-GC-ALGORITHMUS<br />

5.1 Objektstruktur<br />

Die Leistungsfähigkeit von GC-Algorithmen hängt stark von der Struktur des Programms<br />

ab, das zur Laufzeit im System ausgeführt wird. Diese Strukturen können je nach implementiertem<br />

Programm sehr unterschiedlich sein. Das hat auch Auswirkungen auf die<br />

Leistungsfähigkeit der jeweiligen <strong>Garbage</strong>-<strong>Collection</strong>. Blackburn [22] hat eine Vielzahl<br />

verschiedener GC-Mechanismen und Programme miteinander verglichen. Seine Untersuchung<br />

kommt zu dem Schluss, dass es keinen optimalen <strong>Garbage</strong> Collector gibt. Es<br />

lassen sich zwar Klassen von Programmen bestimmen, die besonders stark von einem<br />

bestimmten Algorithmus zur <strong>Garbage</strong>-<strong>Collection</strong> profitieren, jedoch existieren ebenso<br />

Programme, für welche die Annahmen und Heuristiken des GC-Algorithmus ungünstig<br />

sind. Dabei handelt es sich nicht um synthetische Programme, die gezielt auf die<br />

Schwachstellen der <strong>Garbage</strong>-<strong>Collection</strong> abzielen, sondern um typische, auch in der Praxis<br />

zum Einsatz kommende Programme.<br />

Weil ein Vergleich mit anderen GC-Algorithmen nicht immer praktikabel ist, gibt es<br />

trotz der beschriebenen Probleme einige Ansätze zur Analyse einer einzelnen <strong>Garbage</strong>-<br />

<strong>Collection</strong>. So ist es möglich, das System mit Hilfe einer repräsentativen Menge von<br />

Programmen zu testen [21], [22]. Dieser Ansatz ermöglicht eine realistische Aussage<br />

der Leistungsfähigkeit. Hauptproblem der Methodik ist es, eine passende Menge von<br />

Programmen auszuwählen und sicherzustellen, dass ein Großteil aller möglichen Programme<br />

eine große Ähnlichkeit mit einem der Testprogramme bietet. Für den hier vorgestellten<br />

Ansatz ist dieser Lösungsweg wenig erfolgversprechend. Es existieren noch<br />

nicht ausreichend viele verschiedene Programme, um repräsentative Aussagen treffen<br />

zu können. Zudem bietet die geringe Maximalanzahl von Objekten auf der Evaluierungsplattform<br />

nur wenig Spielraum beim Entwurf neuer Programme.<br />

Für diese Arbeit ist der Ansatz der synthetischen Modelle erfolgversprechender. Dabei<br />

wird versucht, ein synthetisches Modell zu entwickeln, das Aussagen bezüglich der<br />

Objektstruktur beliebiger Programme zulässt. Die synthetischen Modelle lassen sich danach<br />

unterteilen, ob sie aus Struktur- oder Verhaltensanalysen gewonnen wurden. Die<br />

Strukturanalysen versuchen aus der Struktur des Speichers Rückschlüsse auf die Lebensdauer<br />

der referenzierten Objekte zu ziehen [23], [24]. Ergebnis dieser Arbeiten<br />

ist beispielsweise, dass die Lebensdauer eines Objektes, das vom Heap aus referenziert<br />

wird, höher ist, als die Lebensdauer eines Objektes, das vom Stack aus referenziert wird.<br />

Im Gegensatz zu Strukturanalyse versuchen die Verhaltensanalysen, mathemathische<br />

Modelle für die Lebensdauer beliebiger Objekte zu finden. Eine Kenntnis der Struktur<br />

erlaubt zwar möglicherweise bessere Modelle, ist aber nicht prinzipiell erforderlich.<br />

Stefanovic hat verschiedene derartige Modelle miteinander verglichen [25]. Seine Arbeit<br />

kommt zu dem Schluss, dass diese Modelle derzeit noch nicht sinnvoll einsetzbar<br />

sind. Die geschätzte Lebensdauer eines Objekts weicht zur Zeit noch zu stark von der<br />

tatsächlichen Lebensdauer ab. Eine Ausnahme bildet die, bereits 1983 von Lieberman<br />

[18] aufgestellte, weak generational hypothesis. Sie besagt, dass jüngere Objekte auch


5.2. SOFTWAREBASIERTES SIMULATIONSMODELL 41<br />

eine kürzere zukünftige Lebenserwartung haben. Diese Hypothese hat sich in der Praxis<br />

vielfach bewährt und bildet die Basis der Generational <strong>Collection</strong>, die bereits in<br />

Kapitel 3.5.3 vorgestellt wurde.<br />

Alle vorgestellten Modelle der Objektstruktur basieren auf statistischen Annahmen.<br />

Sie sind so ausgerichtet, dass sie für Anwendungen in einer klassischen Laufzeitumgebung<br />

(Desktop-PC) die besten Ergebnisse liefern. Sie lassen sich nur schlecht auf beliebige<br />

Programme übertragen, deren Struktur sich stark von einer Softwareanwendung<br />

unterscheidet. Weil sich die hier verwendete Hardwareumgebung schon in der geringen<br />

Maximalanzahl von Objekten stark von klassischen Laufzeitumgebungen unterscheidet,<br />

sind auch deren Heuristiken nur unzureichend übertragbar.<br />

5.2 Softwarebasiertes Simulationsmodell<br />

Die erste Aufgabe einer Analyse muss die Überprüfung der Funktionalität des entwickelten<br />

Algorithmus sein. Eine erste Evaluation der Algorithmen fand in einem Software-<br />

Simulationsmodell auf Java-Basis [26], [27] statt. Ziel des Modells war es vor allem, zu<br />

überprüfen ob die Nebenläufigkeit der <strong>Garbage</strong>-<strong>Collection</strong> zu unzulässigen Objektstrukturen<br />

führen kann. Hierzu ist vor allem eine Simulation der Traversierung des Mark-<br />

Prozesses notwendig.<br />

Durch den Einsatz von mehreren Threads und einer Multi-Prozessor-Plattform lassen<br />

sich die nebenläufigen Aspekte der HW-GC evaluieren.<br />

Um in der Softwaresimulation verlässliche Aussagen treffen zu können, ist die Implementierung<br />

einer passenden Datenstruktur notwendig. Diese Datenstruktur muss das<br />

Verhalten der ComputingPages der Hardwareumgebung möglichst gut annähern.<br />

Im Java-basierten Simulationsmodell wird eine Struktur mit 16 ComputingPages erzeugt.<br />

Diese besitzen eine zufällig generierte Menge von Referenzen aufeinander. In<br />

zwei parallelen Threads wird eine nebenläufige <strong>Garbage</strong>-<strong>Collection</strong> simuliert. Ein Thread<br />

simuliert den Mutator, während der andere Thread die Aufgabe des Collectors übernimmt.<br />

Der HW-GC-Algorithmus aus Kapitel 4 deaktiviert während der aktiven <strong>Garbage</strong>-<br />

<strong>Collection</strong> die Kommunikation zwischen verschiedenen Modulen. Damit reicht es für<br />

die Simulation aus, wenn der erste Thread lokale Operationen innerhalb einzelner Objekte<br />

simuliert. Er löscht zufällige Referenzen aus beliebigen ComputingPages. Die<br />

Aufgabe des zweiten Thread ist die Simulation des <strong>Garbage</strong>Collectors. Er führt die Traversierung<br />

durch und markiert erreichbare Objekte. Nach Ende des Traversierung werden<br />

beide Threads gestoppt. Mit Hilfe eines weiteren Algorithmus zur Traversierung<br />

wird überprüft, ob tatsächlich alle erreichbaren Objekte als solche markiert wurden.<br />

Zur Evaluation der Funktionalität ist es notwendig, verschiedene Objektgraphen zu<br />

generieren. Die generierten Objektgraphen müssen ausreichend allgemein sein, um alle<br />

vorkommenden Fälle zur Traversierung abzudecken. Darüberhinaus sollte die generierte<br />

Struktur gewisse Bedingungen erfüllen, die es erlauben, die möglichen Problemfälle des


42 KAPITEL 5. ANALYSE DES HW-GC-ALGORITHMUS<br />

Algorithmus aufzuspüren. Für aussagekräftige Tests muss die konkrete Struktur zufällig<br />

generiert werden. Die Java-Umgebung verfährt dabei nach folgendem Algorithmus:<br />

1. Zuerst wird eine einstellbare Anzahl von Knoten zufällig aus den vorhandenen 16<br />

ComputingPages ausgewählt und als Wurzelknoten deklariert.<br />

2. Für jeden Knoten wird eine ebenfalls einstellbare Menge an Referenzen auf andere<br />

Knoten generiert und abgespeichert. Werden in diesem Schritt mehrere gleiche<br />

Referenzen erzeugt, wird die zweite generierte Referenz ignoriert. Für die<br />

<strong>Garbage</strong>-<strong>Collection</strong> ist nur relevant, dass der andere Knoten erreichbar ist, nicht<br />

jedoch über wieviele Referenzen. Damit wird zudem erreicht, das die Anzahl der<br />

Referenzen nicht konstant ist, sondern von eins (wenn immer die gleiche Referenze<br />

generiert wurde) bis zu dem eingestellten Maximum an Referenzen (wenn<br />

alle generierten Referenzen unterschiedlich sind) variiert.<br />

Um <strong>Garbage</strong>-<strong>Collection</strong>-Algorithmen optimal zu testen, sind die beiden Parameter<br />

“Anzahl der Wurzelknoten” (#W urzelKnoten) und “Anzahl an Referenzen in einem<br />

Knoten” (#KnotenReferenzen) nach zwei Merkmalen optimiert: Der Anzahl der erreichbaren<br />

Knoten und der Anzahl der Kanten im Graph.<br />

Die Anzahl der erreichbaren Knoten sollte möglichst maximal sein. Für die Traversierung<br />

im Mark-Prozess sind nur die erreichbaren Knoten ausschlaggebend, die übrigen<br />

werden in diesem Schritt ignoriert. Ist nur ein einzelner Knoten (der Wurzelknoten) erreichbar,<br />

gibt es keine ausgehenden Kanten. Besteht der Graph aus zwei erreichbaren<br />

Knoten, kann er zwei verschiedene Formen haben. Weil einer der beiden Knoten ein<br />

Wurzelknoten ist und der andere Knoten von der Wurzel aus erreichbar ist, muss eine<br />

Kante von der Wurzel zum zweiten Knoten existieren. Die zwei möglichen Objektgraphen<br />

unterscheiden sich darin, ob auch eine Kante vom zweiten Knoten zur Wurzel<br />

zurückverweist. Die Anzahl der möglichen Objektgraphen lässt sich allgemein berechnen.<br />

In einem Graph mit n Objekten gibt es maximal ∑ n<br />

i=0<br />

n∗(n−1)<br />

2<br />

gerichtete Kanten.<br />

Es ist offensichtlich, dass eine große Anzahl von erreichbaren Knoten auch eine Vielzahl<br />

verschiedener möglicher Kantenkombinationen garantiert. Diese Vielzahl ist in der<br />

Simulation wünschenswert um ein möglichst breites Spektrum an Testfällen abzudecken.<br />

Um die Anzahl von erreichbaren Knoten in einem zufällig generierten Graphen zu<br />

beeinflussen, bietet sich die Modifikation der Anzahl von Referenzen innerhalb eines<br />

Knotens an. Enthält jedes Objekt mehr Referenzen, dann weist der Objektgraph mit<br />

großer Wahrscheinlichkeit viele erreichbare Knoten auf. Abbildung 5.1 veranschaulicht<br />

diesen Sachverhalt für vier Knoten. Während es mit nur einer ausgehenden Kante pro<br />

Knoten eine Vielzahl von Kombinationen gibt, bei der mindestens einer der Knoten<br />

nicht erreichbar ist, so gibt es für zwei ausgehende Kanten nur noch eine einzige Kombination<br />

sämtlicher Kanten, damit ein Objekt unerreichbar bleibt. Besitzt jeder Knoten


5.2. SOFTWAREBASIERTES SIMULATIONSMODELL 43<br />

Eine Kante pro Knoten<br />

Zwei Kanten pro Knoten<br />

Drei Kanten pro Knoten<br />

Eine Kante pro Knoten<br />

Zwei Kanten pro Knoten<br />

Abbildung 5.1: Objektgraphen mit verschiedener Anzahl an ausgehenden Kanten<br />

sogar drei ausgehende Kanten, so gibt es nur noch eine einzige Kombination der Kanten.<br />

Der Graph ist in diesem Fall vollständig; alle Objekte sind erreichbar.<br />

Zur Generierung einer möglichst geeigneten Objektstruktur wird also in einem ersten<br />

Schritt evaluiert, bei welchen Parametern nach der Generierung sämtliche Knoten vom<br />

Wurzelknoten aus erreichbar sind. Diese Bedingung wird von einem breiten Bereich<br />

möglicher Parameter erfüllt.<br />

Um eine maximale Erreichbarkeit aller Knoten zu gewährleisten, wäre es am einfachsten,<br />

einen vollständigen Graphen anzulegen. Ein vollständiger Graph enthält alle Kanten,<br />

die möglich sind. Jeder Knoten in diesem Graphen ist auch erreichbar. Zur Simulation<br />

bietet der vollständige Graph jedoch einen gravierenden Nachteil: Wie man in<br />

Abbildung 5.1 sehen kann, hat das Entfernen einer einzelnen Kante keinen Einfluss auf<br />

die Erreichbarkeit. Für die Evaluation der nebenläufigen <strong>Garbage</strong>-<strong>Collection</strong> sind jedoch<br />

insbesondere solche Fälle interessant, bei denen das Löschen einer Kante (durch<br />

den Mutator) die Objektstruktur ändert. Diese Modifikation während eines aktiven GC-<br />

Zyklus zeichnet einen nebenläufigen Mechanismus aus und ist Ziel der Softwareevaluation.


44 KAPITEL 5. ANALYSE DES HW-GC-ALGORITHMUS<br />

Ein Graph, in dem das Löschen jeder beliebigen Kante Einfluss auf die Erreichbarkeit<br />

der Objekte hat, ist der minimale aufspannende Graph [20]. Dieser Graph besitzt<br />

für n Knoten eine minimale Anzahl von Kanten, so dass dabei immer noch sämtliche<br />

Knoten erreichbar sind. Hierfür werden n − 1 Kanten benötigt. Ein möglicher minimal<br />

aufspannender Graph für vier Knoten ist in Abbildung 5.2 angegeben.<br />

Abbildung 5.2: Minimaler aufspannender Graph<br />

Der minimal aufspannende Graph entsteht, wenn ein Minimum an Kanten in jedem<br />

Knoten erzeugt werden. Diese Forderung nach möglichst wenig Kanten steht im Gegensatz<br />

zur Forderung nach möglichst vielen Kanten, um die Erreichbarkeit aller Knoten<br />

zu gewährleisten.<br />

Es gilt also durch statistische Messungen in der Simulation festzustellen, welche Parameter<br />

zu Generierung des Objektgraphen zu wählen sind, so dass zwar alle 16 ComputingPages<br />

erreichbar sind, die Anzahl der Kanten im Graphen jedoch so klein wie<br />

möglich ist. Die Anzahl der Kanten dient dabei als heuristisches Maß für die “Minimalität”<br />

des Graphen. Es kann nicht sichergestellt werden, dass das Löschen einer Kante in<br />

einem Graphen mit weniger Kanten tatsächlich häufiger die Objektstruktur ändert, als<br />

dies in einem Graphen mit mehr Kanten der Fall ist. Um eine hinreichend große Menge<br />

verschiedener Objektgraphen zu erstellen, ist diese Näherung jedoch ausreichend.<br />

In Abbildung 5.3 sind die Parameter für die Anzahl der erreichbaren Knoten aufgetragen.<br />

Die vier Kurven geben die Anzahl der Wurzelknoten an, die Ergebnisse sind<br />

jeweils über 50 Messreihen gemittelt. Man sieht, dass sich die Kurven ab zwei Referenzen<br />

kaum noch unterscheiden. Das ist darin begründet, dass in diesem Fall mit weitere<br />

Wurzelknoten mit großer Wahrscheinlichkeit auf einem bereits erreichbaren Knoten<br />

zum Liegen kommen.<br />

Wegen der hohen Ähnlichkeit der einzelnen Kurven beschränken wir uns im weiteren<br />

auf Graphen mit zwei Wurzeln. Eine einzige Wurzel würde zwar ebenfalls ausreichen,<br />

um jedoch die Option zu besitzen, Konzepte auch für mehrere die Realisierung mehrerer<br />

Threads zu überprüfen, fiel die Entscheidung auf einen Graphen mit zwei Wurzelknoten.


5.3. QUALITATIVE BETRACHTUNG 45<br />

ErreichbareKnoten<br />

16<br />

14<br />

12<br />

10<br />

8<br />

6<br />

4<br />

2<br />

0<br />

Eine Wurzel<br />

Zwei Wurzeln<br />

Drei Wurzeln<br />

Vier Wurzeln<br />

0 1 2 3 4 5 6 7<br />

KnotenReferenzen<br />

Abbildung 5.3: Erreichbare Knoten in Zufallsgraphen<br />

5.3 Qualitative Betrachtung<br />

Kernstück einer qualitativen Betrachtung der entwickelten Algorithmen muss eine Untersuchung<br />

ihrer korrekten Funktionalität sein. Diese besteht bei der <strong>Garbage</strong>-<strong>Collection</strong><br />

aus vier verschiedenen Aspekten, die im Zusammenspiel ein funktionierendes System<br />

ausmachen.<br />

Theorem 5.3.1 (Funktionalität der <strong>Garbage</strong>-<strong>Collection</strong>)<br />

- Der Algorithmus muss korrekt sein (Siehe Definition 3.0.1).<br />

- Der Algorithmus muss partiell vollständig sein (Siehe Definition 4.4.1).<br />

- Bei Bedarf muss der nächste GC-Zyklus gestartet werden (Anlauf-Garantie).<br />

- Ein GC-Zyklus muss terminieren.<br />

Zusätzlich zu den beiden Begriffen der Korrektheit und partiellen Vollständigkeit werden<br />

also zwei Forderungen bezüglich der Laufzeit der <strong>Garbage</strong>-<strong>Collection</strong> gestellt.<br />

Die Anlaufgarantie gewährleistet, dass eine Anwendung solange Speicher allozieren<br />

kann, wie noch welcher verfügbar ist. Ohne diese Forderung wäre ein Implementierung<br />

denkbar, die zwar einen absolut korrekten GC-Mechanismus beinhaltet, ihn jedoch niemals<br />

verwendet. Belegter Speicher wird somit nicht mehr freigegeben.


46 KAPITEL 5. ANALYSE DES HW-GC-ALGORITHMUS<br />

Die Terminierung des Algorithmus fordert eine endliche Laufzeit der <strong>Garbage</strong>-<strong>Collection</strong>.<br />

Würde der Algorithmus nicht terminieren, könnte man einen Algorithmus entwickeln<br />

der unbegrenzt lange für die Traversierung der Daten benötigt. Obwohl solch ein<br />

Algorithmus durchaus korrekt und vollständig sein könnte, ist er für die Praxis untauglich.<br />

Es wäre wünschenswert, die Gültigkeit der Forderungen 5.3.1 mit Hilfe der Methoden<br />

der formalen Verifikation zu beweisen. Allerdings verwenden solche Verifikationsverfahren<br />

üblicherweise eine funktionale Sprache, um die zu verifizierenden Aussagen zu<br />

formulieren. Um Aussagen bezüglich anderer Sprachen (wie in unserem Fall VHDL)<br />

zu treffen, ist es entweder erforderlich, die Algorithmen noch einmal neu zu formulieren,<br />

oder eine Übersetzung aus der Implementierungssprache in die Verifikationssprache<br />

durchzuführen. Beide Ansätze sind komplex [11] und fehleranfällig, so dass sie den<br />

Rahmen der vorliegenden Arbeit sprengen würden. Deswegen beschränkt sich die folgende<br />

qualitative Analyse des HW-GC-Algorithmus auf einen halb-formellen Beweis.<br />

Die Argumentation ist in den meisten Fällen direkt einsichtig und erfordert keine weiteren<br />

formalen Hilfen.<br />

Der Beweis wird nach den einzelnen Prozessen der <strong>Garbage</strong>-<strong>Collection</strong> aufgeteilt. Für<br />

jeden dieser Teile wird gezeigt, dass die Forderungen aus Theorem 5.3.1 nicht verletzt<br />

werden. Diese einzelnen Prozesse der <strong>Garbage</strong>-<strong>Collection</strong> sind:<br />

1. Warten auf den nächsten <strong>Garbage</strong>-<strong>Collection</strong>-Zyklus<br />

2. Die Traversierung im Mark-Prozess<br />

3. Der Sweep-Prozess<br />

5.3.1 Warten auf den nächsten <strong>Garbage</strong>-<strong>Collection</strong>-Zyklus<br />

In dieser Phase ist die <strong>Garbage</strong>-<strong>Collection</strong> im Ruhezustand. Es findet der normale Programmablauf<br />

statt. Die einzelnen ComputingPages können über den Arbiter miteinander<br />

kommunizieren.<br />

Je nach Implementierung sind für die Anlauf-Garantie zwei unterschiedliche Situationen<br />

denkbar. Wenn der nächste GC-Zyklus nach endlicher Zeit automatisch wieder<br />

startet, ist diese Forderung trivialerweise erfüllt. In diesem Fall kann sogar von einer<br />

Terminierung dieses Teilschritts gesprochen werden.<br />

Es sind jedoch Implementierungen der <strong>Garbage</strong>-<strong>Collection</strong> denkbar, bei denen der<br />

nächste Bereinigungsvorgang erst dann startet, wenn die Anforderung eines weiteren,<br />

freien Speicherbereichs nicht mehr erfüllt werden kann. Ein GC-Zyklus wird also nur<br />

ausgeführt, wenn das Freigeben von nicht mehr benötigtem Speicher unbedingt notwendig<br />

ist, um den weiteren Programmablauf zu gewährleisten.<br />

Beim Ablauf eines Programms, dass nur konstanten Speicher benötigt, wird somit niemals<br />

ein GC-Zyklus gestartet. Eine garantierte Terminierung dieses Schrittes ist damit


5.3. QUALITATIVE BETRACHTUNG 47<br />

nicht mehr gegeben, weshalb die Anlauf-Garantie als ein “verallgemeinerter” Terminierungsbegriff<br />

eingeführt wurde.<br />

Die HW-GC-Implementierung verwendet einen einfachen Zähler, um den nächsten<br />

<strong>Garbage</strong>-<strong>Collection</strong>-Zyklus zu aktivieren. Offensichtlich erfüllt dieser Ansatz die Anlauf-<br />

Garantie: Wird freier Speicher angefordert, ohne dass dieser verfügbar ist, so existiert<br />

eine definierte Zeitschranke, innerhalb derer der nächste GC-Zyklus angestoßen wird.<br />

Die Forderungen nach Korrektheit und Vollständigkeit können hier ignoriert werden.<br />

Da der eigentliche Mark-Sweep-Prozess noch nicht eingesetzt hat, lassen sich auch noch<br />

keine Aussagen bezüglich dieser beiden Faktoren treffen. Die Forderung nach Terminierung<br />

bezieht sich ebenfalls nur auf den eigentlichen Mark-Sweep-Vorgang. Sie kann<br />

in diesem Schritt ebenfalls ignoriert werden.<br />

Damit gilt:<br />

Theorem 5.3.2<br />

Die Forderungen aus Theorem 5.3.1 werden beim Warten auf den nächsten GC-Zyklus<br />

nicht verletzt.<br />

5.3.2 Die Traversierung im Mark-Prozess<br />

Der Mark-Prozess erfordert den aufwändigsten Beweis. Das ist im Wesentlichen in der<br />

komplexen Struktur der Traversierung begründet.<br />

Wir beginnen den Beweis der Forderungen aus Theorem 5.3.1 mit dem Nachweis der<br />

Korrektheit. Die Korrektheit fordert, dass ein Objekt nur dann freigegeben wird, wenn<br />

es nicht mehr erreichbar ist. Dazu muss gezeigt werden, dass alle Referenzen, die in<br />

einem Objekt abgelegt sind, tatsächlich dazu genutzt werden, die zugehörigen Module<br />

zu aktivieren. Alle Referenzen sind in zwei Listen abgelegt. Diese Listen werden beide<br />

vollständig der Reihe nach abgearbeitet. Die Adressen der einen Liste werden dabei zu<br />

Modulen mit kleineren, die der anderen zu denen mit größeren Adressen geschickt. Die<br />

Implementierung stellt sicher, dass die Referenzen bereits beim Abspeichern der richtigen<br />

Liste zugeteilt werden. Es bleibt zu zeigen, dass die Einträge aus den Referenzlisten<br />

auch gesendet werden und nicht etwa ein Deadlock oder ähnliches auftritt. Ohne<br />

Beschränkung der Allgemeinheit wird im folgenden der auf dem FPGA von rechts nach<br />

links laufende Bus betrachtet. Liegt am rechten Eingang ein gültiges Signal an, so hat<br />

dieses eine höhere Priorität und wird zuerst an den linken Ausgang angelegt. Um sicher<br />

zu stellen, dass alle eigenen Referenzen gesendet werden, muss also gezeigt werden,<br />

dass es eine obere Schranke für die Anzahl der Adressen am rechten Eingang gibt.<br />

Eine solche Schranke existiert, weil rechts von der betrachtete ComputingPage nur<br />

endlich viele weitere Module liegen, die wiederum nur eine endliche Anzahl von Referenzen<br />

enthalten. Es ist also möglich, die eigenen Referenzen in endlicher Zeit abzuarbeiten.


48 KAPITEL 5. ANALYSE DES HW-GC-ALGORITHMUS<br />

Der Beweis der Terminierung ist eng verwandt mit dem Beweis zur Korrektheit. Wie<br />

bereits gezeigt wurde, existieren auf beiden Seiten einer beliebigen ComputingPage nur<br />

endlich viele weitere Module. Deswegen lässt sich eine zeitliche Obergrenze angeben,<br />

nach der alle dort enthaltenen Referenzen, abgearbeitet worden sind. Damit existiert<br />

natürlich auch für die gesamte Plattform eine zeitliche Obergrenze zur Abarbeitung<br />

aller Referenzen aus allen Modulen.<br />

Für die partielle Vollständigkeit ist eine Betrachtung der Nebenläufigkeit erforderlich.<br />

Für den Fall, das die Referenzen während des gesamten Mark-Prozesses konstant sind,<br />

ist diese Forderung offensichtlich erfüllt. Während der Traversierung können Referenzen<br />

gelöscht werden. Falls dabei der Objektgraph nicht geändert wird, gilt offensichtlich<br />

immer noch die Vollständigkeit. Wird ein Objekt durch den Löschvorgang unerreichbar,<br />

so gilt zu unterscheiden: Wurden diese Referenzen noch nicht abgearbeitet, gibt es<br />

keine weiteren Auswirkungen. Der Objektgraph kann betrachtet werden, als wäre die<br />

Referenz schon seit Beginn der Traversierung entfernt. Wird eine Referenz hingegen<br />

erst gelöscht, nachdem sie bereits abgearbeitet wurde, wird sie im aktuellen GC-Zyklus<br />

noch als markiert betrachtet und kann nicht freigegeben werden. Nach Voraussetzung<br />

ist das ehemals referenzierte Objekt jetzt unerreichbar. Es kann zu keinem zukünftigen<br />

Zeitpunkt mehr erreichbar sein und wird deshalb im nächsten GC-Zyklus erkannt und<br />

freigegeben.<br />

Die Forderung der Anlauf-Garantie ist trivial erfüllt, weil im Mark-Prozess die <strong>Garbage</strong>-<strong>Collection</strong><br />

ja bereits angelaufen ist.<br />

Damit gilt:<br />

Theorem 5.3.3<br />

Die Forderungen aus Theorem 5.3.1 werden während der Traversierung im Mark-Prozess<br />

nicht verletzt.<br />

5.3.3 Der Sweep-Prozess<br />

Für den Sweep-Prozess gilt, dass die Anlauf-Garantie und Terminierung offensichtlich<br />

gelten. In diesem Prozess wird ein Shift-Register, dessen Breite identisch mit der Anzahl<br />

der ComputingPages ist, einmal vollständig seriell ausgelesen. Weil die Anzahl der<br />

ComputingPages endlich ist, ist damit auch das Auslesen endlich. Der Prozess terminiert<br />

somit. Da die <strong>Garbage</strong>-<strong>Collection</strong> noch immer aktiviert ist, ist die Anlauf-Garantie<br />

mit der gleichen Argumentation wie im Mark-Prozess erfüllt.<br />

Auf die Forderungen der Korrektheit und partiellen Vollständigkeit hat der Sweep-<br />

Prozess keinen Einfluss. In diesem Schritt werden in dem Sweep-Register die Flags<br />

ausgelesen, die während des Mark-Prozesses gesetzt worden sind. Wird eine Null gelesen,<br />

so ist das korrespondiere Objekt bei der Traversierung nicht aktiviert worden. Eine<br />

Eins bedeutet, dass das Objekt vom Wurzelknoten aus über Referenzen erreichbar ist.<br />

Der Sweep-Schritt liest diese Markierungen nur aus, modifiziert sie jedoch nicht. Die


5.4. QUANTITATIVE BETRACHTUNG 49<br />

Forderungen zur Korrektheit und partiellen Vollständigkeit sind also erfüllt, solange sie<br />

zu Beginn des Sweep-Prozess erfüllt waren.<br />

Theorem 5.3.4<br />

Die Forderungen aus Theorem 5.3.1 werden während des Sweep-Prozesses nicht verletzt.<br />

5.3.4 Funktionalität des HW-GC-Algorithmus<br />

Mit den Theoremen 5.3.2, 5.3.3 und 5.3.4 ist damit Theorem 5.3.1 nachgewiesen. Der<br />

implementierte Algorithmus erfüllt also die Aufgaben einer automatischen Speicherverwaltung.<br />

Dieser informelle Beweis trifft nur Aussagen bezüglich der textuellen Spezifikation<br />

des Algorithmus. Ein Beweis, dass die konkrete Implementierung (als VHDL-Code<br />

oder gar als Schaltbild) tatsächlich die Spezifikation erfüllt, würde den Rahmen dieser<br />

Arbeit sprengen.<br />

5.4 Quantitative Betrachtung<br />

Die quantitative Analyse eines Algorithmus versucht Aussagen bezüglich seiner Leistungsfähigkeit<br />

zu treffen. Es wird eine Metrik benötigt, in der solche Aussagen getroffen<br />

werden können. Dabei kann es sich entweder um eine relative Metrik handeln, die vergleichende<br />

Aussagen erlaubt. Die Aussage “Der Algorithmus A ist doppelt so schnell,<br />

wie Algorithmus B” wäre ein Beispiel hierfür. Der Vorteil einer solchen Methodik ist<br />

die Möglichkeit verschiedene Algorithmen ohne die Kenntnis ihrer Arbeitsweise vergleichen<br />

zu können. Es reicht aus, sie als “Black-Box”-System zu betrachten und zu<br />

messen. Allerdings lassen sich solche vergleichende Metriken nicht ohne Weiteres verallgemeinern.<br />

Schon durch die Verwendung einer anderen Hardwareplattform sind neue<br />

Erkenntnisse nicht mehr ohne Weiteres mit den alten Ergebnissen vereinbar.<br />

Günstiger ist die Entwicklung einer allgemeinen Metrik, die von einem konkreten Algorithmus<br />

abstrahiert und Merkmale findet, die für alle Algorithmen einer Klasse bestimmbar<br />

sind. Diese Merkmale lassen sich direkt messen und erlauben auch einen Vergleich<br />

zwischen verschiedenen Untersuchungen. Eine Metrik soll so allgemein gefasst<br />

sein, dass sie vielseitig anwendbar ist. Die Schwierigkeit liegt darin, sinnvolle Metriken<br />

zu finden.<br />

Die bekannteste Metrik ist die Laufzeitmessung. Durch Angabe der Laufzeit in Sekunden<br />

lassen sich beliebige Algorithmen miteinander vergleichen. Um einen präziseren<br />

Vergleich zu ermöglichen, ist es sinnvoll, speziellere Metriken zu entwerfen. So ist<br />

beim Vergleich zweier Sortieralgorithmen nicht nur die gesamte Laufzeit von Interesse.<br />

Ebenso wichtig ist es zu bestimmen, wie viele Speicherzugriffe erforderlich sind und


50 KAPITEL 5. ANALYSE DES HW-GC-ALGORITHMUS<br />

wieviel Zeit in der Ein/Ausgabe verbracht wird. Es sind also Metriken notwendig, die<br />

Vergleichsmöglichkeiten zwischen möglichst vielen verschiedenen Implementierungen<br />

bieten, gleichzeitig aber auf die wichtigen Aspekte der Algorithmen eingehen. Im Folgenden<br />

werden solche Metriken für die <strong>Garbage</strong>-<strong>Collection</strong> entwickelt. Sie berücksichtigen<br />

insbesondere den Einfluss der Nebenläufigkeit, die sich im Ablauf der Traversierung<br />

und in der gleichzeitigen Ausführung von Mutator und Collector niederschlägt.<br />

5.4.1 Laufzeit der Traversierung<br />

Maßgeblich für die Laufzeit eines vollständigen GC-Zyklus ist die Laufzeit der Traversierung<br />

im Mark-Prozess. Um die Leistungsfähigkeit einer Graphentraversierung zu<br />

beurteilen bietet es sich an, anzugeben, wieviel Verarbeitungsschritte sie erfordert. Dieser<br />

Wert hängt von der Struktur der Knoten und der Struktur der Kanten ab.<br />

Der Sweep-Prozess erfordert eine einmalige lineare Traversierung aller Objekte. Für<br />

n Objekte erfordert er somit n Schritte. Der Aufwand für den Mark-Prozess hängt hingegen<br />

vom Objektgraphen ab. Für n ′ erreichbare Objekte, die im Mittel m Referenzen<br />

auf weitere Objekte enthalten, erfordert ein einfacher sequenzieller Ansatz zur Traversierung<br />

bereits m∗n ′ Schritte. Es ist anzumerken, dass es höchstens so viele erreichbare<br />

Objekte gibt, wie insgesamt vorhanden sind. Es ist also n ′ ≤ n. Trotzdem ist der Mark-<br />

Prozess wegen des Einflusses der Referenzen im Normalfall deutlich aufwändiger.<br />

Metrik 5.4.1 (Laufzeit-Effizienz)<br />

Die Laufzeit eines Algorithmus zur <strong>Garbage</strong>-<strong>Collection</strong> wird maßgeblich von der Laufzeit<br />

des Mark-Prozess beeinflusst. Seine Effizienz lässt sich durch die Anzahl der Verarbeitungschritte<br />

angeben, die notwendig sind, um alle erreichbaren Objekte zu besuchen.<br />

Wie bereits erwähnt, wird bei einer sequenziellen Traversierung aller Knoten eine Liste<br />

von Referenzen mitgeführt. In jedem Verarbeitungsschritt muss geprüft werden, ob<br />

eine Referenz aus dieser Liste zu einem Knoten führt, der bisher noch nicht besucht wurde.<br />

Ist das der Fall, werden diese Referenzen in die Liste aufgenommen. Der Algorithmus<br />

erfordert also m Verarbeitungsschritte. Alle Referenzen, die im Graphen erreichbar<br />

sind, müssen abgearbeitet werden.<br />

Satz 5.4.1 (Sequenzielle Traversierung)<br />

Die sequenzielle Traversierung eines Objektgraphen mit m erreichbaren Kanten benötigt<br />

m Verarbeitungsschritte.<br />

Bearbeitet man alle Referenzen eines Knotens gleichzeitigt, so erhält man eine parallele<br />

Traversierung des Objektgraphen. In Abbildung 4.2 ist eine solche illustriert. Man<br />

erkennt, dass in jedem Bearbeitungschritt alle Objekte in einer bestimmten Entfernung<br />

zum Wurzelknoten bearbeitet werden. Die Laufzeit ist also durch den längsten Pfad von


5.4. QUANTITATIVE BETRACHTUNG 51<br />

einem Wurzelknoten zu einem anderen erreichbaren Knoten bestimmt. In einer Worst-<br />

Case-Betrachtung ist die Länge dieses Pfades mit der Menge aller erreichbaren Objekte<br />

n ′ identisch. Dabei sind alle Objekte in einer Reihe angeordnet und enthalten jeweils<br />

die Referenz auf ihren nächsten Nachbarn. Üblicherweise ist der längste Pfad deutlich<br />

kleiner als n ′ . Jedes erreichbare Objekt erfordert mindestens eine darauf verweisende<br />

Kante, weil es sonst nicht zu erreichen wäre. Damit gilt, das n ′ ≤ m ist und somit<br />

eine parallele Traversierung mit weniger Verarbeitungsschritten als eine sequenzielle<br />

Traversierung ablaufen kann.<br />

Satz 5.4.2 (Parallele Traversierung)<br />

Die parallele Traversierung eines Objektgraphen mit n ′ erreichbaren Knoten und m<br />

Kanten benötigt k Verarbeitungsschritte. k ist die maximale Entfernung eines erreichbaren<br />

Objektes von einer Wurzel. Es gilt k ≤ n ′ ≤ m.<br />

Die realisierte Traversierung aus dem HW-GC-Algorithmus ist in ihrer Leistungsfähigkeit<br />

zwischen der sequenziellen und parallelen Traversierung anzusiedeln. Mögliche<br />

Kollisionen auf dem implementierten Bus-System haben großen Einfluss auf die Traversierung.<br />

Eine ComputingPage kann möglicherweise keine eigenen Daten (Aktivierungen) versenden,<br />

weil es die Signale eines anderen Objekts weiterleiten muss. Betrachtet man den<br />

Extremfall, dann treten jedesmal Kollisionen auf, solange noch andere Objekte Daten<br />

versenden. Ein Objekt kann also erst dann seinen Daten auf den Bus schreiben, wenn<br />

die Objekte mit höherer Priorität ihre Referenzlisten abgearbeitet haben. Man erhält ein<br />

sequenzielles Traversierungsschema. Wenn umgekehrt der Objektgraph so aufgebaut<br />

ist, dass es in keinem Fall zu einer Kollision der Busressourcen kommt, dann können<br />

alle aktivierten Objekte gleichzeitig ihre Referenzen abarbeiten und man erhält eine<br />

Traversierungsreihenfolge, die mehr der parallelen Traversierung ähnelt.<br />

Satz 5.4.3 (HW-GC Traversierung)<br />

Die Traversierung im HW-GC-Algorithmus benötigt maximal so viele Schritte wie die<br />

sequenzielle Traversierung. Sie benötigt mindestens genau so viele Verarbeitungsschritte<br />

wie die parallele Traversierung.<br />

Inwieweit der implementierte HW-GC-Algorithmus eher einer sequenziellen oder parallelen<br />

Traversierung ähnelt, hängt von der jeweiligen Objektstruktur ab. Nach Abschnitt<br />

5.1 lassen sich keine verlässlichen Aussagen über diese Struktur treffen. Es muss<br />

daher bei dieser Abschätzung der Leistungsfähigkeit bleiben. Es lässt sich jedoch festellen,<br />

dass die Anzahl der Kollisionen von dem Verhältnis Kanten abhängt und durch eine<br />

Knoten<br />

Umsortierung der Objekte beeinflusst werden kann.


52 KAPITEL 5. ANALYSE DES HW-GC-ALGORITHMUS<br />

1<br />

a<br />

1<br />

a<br />

1<br />

a<br />

2<br />

6<br />

2<br />

3<br />

2<br />

2<br />

3<br />

5<br />

7<br />

4 5<br />

6<br />

3<br />

3 3<br />

4<br />

b<br />

7<br />

b<br />

4<br />

b<br />

Sequenzielle Traversierung<br />

mit Tiefensuche<br />

Sequenzielle Traversierung<br />

mit Breitensuche<br />

Nebenläufige Traversierung<br />

Abbildung 5.4: Verschiedene Traversierungen<br />

5.4.2 PageMiss<br />

Wird während der <strong>Garbage</strong>-<strong>Collection</strong> eine Kante gelöscht, die bereits traversiert wurde,<br />

besteht die Möglichkeit, dass ein Objekt als aktiv markiert ist, obwohl es tatsächlich<br />

nicht mehr erreichbar ist. Solche Objekte werden erst im nächsten GC-Zyklus korrekt<br />

als <strong>Garbage</strong> erkannt. Diese Situation wird als PageMiss bezeichnet, weil die ComputingPage<br />

bei der Zusammenstellung von nicht mehr benötigten Objekten verfehlt wird.<br />

Die Häufigkeit von PageMisses liefert eine aussagekräftige Metrik zur Analyse einer<br />

nebenläufigen <strong>Garbage</strong>-<strong>Collection</strong>. Weil ein vom PageMiss betroffenes Objekt erst im<br />

folgenden Zyklus freigegeben wird, steigt die mittlere Laufzeit zum Freigeben eines<br />

Objektes an.<br />

Metrik 5.4.2 (PageMiss-Effizienz)<br />

In einem nebenläufigen GC-Algorithmus kann es dazu kommen, dass ein Objekt innerhalb<br />

eines GC-Zyklus als aktiv markiert bleibt, obwohl es bereits nicht erreichbar ist.<br />

Diese Situation wird als PageMiss bezeichnet und ist ein Maß für die Leistungsfähigkeit<br />

des Algorithmus.<br />

Gibt eine Implementierung beispielsweise sämtliche Objekte erst nach zwei Durchläufen<br />

frei, so hätte sich die Laufzeit, bis ein Objekt korrekt als <strong>Garbage</strong> erkannt wird,<br />

gegenüber der Zeit für einen GC-Zyklus verdoppelt. Tritt umgekehrt in einer Implementierung<br />

für kein Objekt ein PageMiss auf, so ist die mittlere Laufzeit zur Freigabe eines<br />

Objekts identisch mit der Laufzeit eines GC-Zyklus.<br />

Für die Anzahl der PageMisses spielt die gewählte Traversierungsreihenfolge eine entscheidende<br />

Rolle. In Abbildung 5.4 sind der Objektgraph und seine Traversierung mit<br />

drei verschiedenen Verfahren dargestellt. Bei der Tiefen- und der Breitensuche handelt<br />

es sich um sequenzielle Verfahren. Die parallele Suche kann mehrere Knoten zugleich<br />

verarbeiten.<br />

Angenommen ein Mutator löscht zum Zeitpunkt 4 die, mit ’a’ markierte Kante. Zum<br />

Zeitpunkt 5 befindet sich die Traversierung im entsprechend mit 5 markiertem Knoten.


5.4. QUANTITATIVE BETRACHTUNG 53<br />

Knoten, die mit einer kleineren Zahl markiert sind, wurden bereits besucht, Knoten<br />

mit höherer Zahl noch nicht. Nach der Traversierung entstehen die in Abbildung 5.5<br />

aufgeführten Objektgraphen. Die gestrichelten Linien markieren Teile, die immer noch<br />

aktiv markiert sind, obwohl sie tatsächlich bereits nicht mehr erreichbar sind. Es fällt<br />

auf, dass Breitensuche und Tiefensuche zu unterschiedlichen Ergebnissen gelangt sind.<br />

Dies ist insofern überraschend, als die beiden in einem nicht nebenläufigen Algorithmus<br />

bezüglich Funktionalität und Laufzeit identisch sind.<br />

1<br />

1<br />

a<br />

1<br />

a<br />

2<br />

2<br />

3<br />

2<br />

2<br />

3<br />

5<br />

4 5<br />

6<br />

3<br />

3 3<br />

b<br />

b<br />

b<br />

4<br />

7<br />

4<br />

Sequenzielle Traversierung<br />

mit Tiefensuche<br />

Sequenzielle Traversierung<br />

mit Breitensuche<br />

Nebenläufige Traversierung<br />

Abbildung 5.5: Neuer Objektgraph nach löschen der Kante a) zum Zeitpunkt 5<br />

Das geschilderte Beispiel beim Entfernen der Kante ’a’ deutet darauf hin, dass die Tiefensuche<br />

der Breitensuche überlegen ist. Wird im Ausgangsbeispiel statt ’a’ die Kante<br />

’b’ zum Zeitpunkt 4 entfernt, so sieht man, dass auch die Breitensuche ein besseres Ergebnis<br />

erzeugen kann. In Abbildung 5.6 sieht man, dass die Breitensuche den Objektgraph<br />

korrekt erkannt hat. Bei der Breitensuche ist hingegen noch ein Objekt vorhanden,<br />

das bereits nicht mehr erreichbar ist.<br />

1<br />

a<br />

1<br />

a<br />

1<br />

a<br />

2<br />

6<br />

2<br />

3<br />

2<br />

2<br />

3<br />

5<br />

7<br />

4 5<br />

6<br />

3<br />

3 3<br />

4<br />

b<br />

4<br />

b<br />

Sequenzielle Traversierung<br />

mit Tiefensuche<br />

Sequenzielle Traversierung<br />

mit Breitensuche<br />

Nebenläufige Traversierung<br />

Abbildung 5.6: Neuer Objektgraph nach löschen der Kante b) zum Zeitpunkt 4<br />

Allgemein gilt, das die Breitensuche Objekte, die weit unten im Baum stehen, erst später<br />

als die Tiefensuche abarbeitet. Berücksichtigt man die weak generational hypothesis,<br />

die besagt, dass junge Objekte (diese stehen weiter unten im Baum) häufiger gelöscht


54 KAPITEL 5. ANALYSE DES HW-GC-ALGORITHMUS<br />

werden, so ist Wahrscheinlichkeit, dass eine Kante noch vor ihrer Abarbeitung entfernt<br />

wird bei der Breitensuche höher. Die Breitensuche verspricht also, eine geringere Anzahl<br />

von PageMisses zu erzeugen.<br />

Eine Tiefensuche durchläuft nacheinander alle Äste bis zum Ende. Gegenüber der<br />

Breitensuche werden somit häufiger Knoten, die nah an einer Wurzel liegen, erst spät<br />

abgearbeitet. Würde in solch einem Knoten eine Referenz gelöscht, wäre durch diese<br />

einzige Operation ein ganzer Teilbaum nicht mehr erreichbar. Die Breitensuche würde<br />

in diesem Fall eine sehr hohe Anzahl von PageMisses erzeugen.<br />

Es gilt also, bei der Entscheidung zwischen Breiten- und Tiefensuche abzuwägen, ob<br />

das seltene Wegfallen von großen Teilbäumen (bei der Tiefensuche) oder aber das häufige<br />

Wegfallen weniger Objekte (bei der Breitensuche) stärker berücksichtigt werden<br />

soll. Blackburn hat in seinen Untersuchungen gezeigt [22], dass in den meisten Fällen<br />

die Tiefensuche bessere Ergebnisse liefert.<br />

Die parallele Traversierung wurde bisher nicht berücksichtigt. Sie erzeugt in beiden<br />

Beispielen ebensoviele PageMisses, wie die jeweils ungeeignetere sequenzielle Traversierung.<br />

Dies ist jedoch nicht in einer ungünstigen Traversierungsreihenfolge begründet,<br />

sondern in deren hoher Geschwindigkeit. Weil diese Traversierung nur vier Takte benötigt,<br />

ist der GC-Zyklus beim Löschen der Kanten ’a’ und ’b’ bereits abgeschlossen. Deshalb<br />

wäre es eigentlich korrekt, die Kantenoperation bereits dem nächsten GC-Zyklus<br />

zuzuordnen. In diesem Zyklus würde bei beiden Beispielen der tatsächliche Objektgraph<br />

erkannt, ein PageMiss tritt nicht auf.<br />

Wie erläutert, basiert die Traversierung der vorgestellten Implementierung auf der parallelen<br />

Traversierung. Weil Objekte, die nah an der Wurzel stehen, vor Knoten, die weiter<br />

von der Wurzel entfernt stehen, traversiert werden, besitzt sie mehr Ähnlichkeit mit<br />

einer Breitensuche. Das komplexe Verhalten des Bus-Systems, insbesondere seine zeitliche<br />

Verzögerung der Signale mit Registern und die Auflösung von Ressourcenkonflikten,<br />

macht einen direkten Vergleich mit den Verfahren der Tiefen- oder Breitensuche<br />

unmöglich.<br />

Eine schnellere und ressourcenschonende Hardwarekommunikation wurde deshalb<br />

gegenüber den klassischen Traversierungsverfahren bevorzugt. Eine höhere Anzahl von<br />

PageMisses kann durch die häufigere Ausführung der <strong>Garbage</strong>-<strong>Collection</strong> ausgeglichen<br />

werden. Dies ist möglich, weil ein einzelner GC-Zyklus schneller ablaufen kann.<br />

Neben der implementierten Traversierungsreihenfolge lassen sich noch andere Kriterien<br />

finden, die einen Einfluss auf die Zahl der PageMisses haben:<br />

• Ein Kriterium ist das Verhältnis <strong>Garbage</strong>CollectorGeschwindigkeit . Dieser Faktor gibt<br />

MutatorGeschwindigkeit<br />

an, wie schnell der <strong>Garbage</strong> Collector im Vergleich zum Programm arbeitet. Je<br />

größer dieser Wert, desto schneller arbeitet der <strong>Garbage</strong> Collector. Im Idealfall<br />

kann ein GC-Zyklus komplett zwischen zwei Anweisungen des Mutators ausgeführt<br />

werden. Anstelle einer nebenläufigen <strong>Garbage</strong>-<strong>Collection</strong> erhält man wieder<br />

eine einfache, sequenzielle <strong>Garbage</strong>-<strong>Collection</strong>, in der kein PageMiss auftreten


5.5. HÄUFIGKEIT DER GC-ZYKLEN 55<br />

kann.<br />

• Ein weiterer Faktor für die Anzahl der PageMisses ist die Dichte des Graphen.<br />

Ein PageMiss kann nur dann auftreten, wenn durch das Löschen einer Referenz<br />

auch ein Objekt aus dem Objektgraphen entfernt werden kann. Je mehr Wege<br />

es von den Wurzelknoten zu den erreichbaren Knoten gibt, desto seltener wird<br />

beim Löschen einer Referenz tatsächlich ein Objekt unerreichbar. Je mehr Wege<br />

zu den Objekten es gibt, um so dichter ist der Graph. Eine präzise und kompakte<br />

Formulierung der Dichte ist sehr aufwändig. Ein gut geeignetes Maß, das, ist die<br />

Anzahl aller Kanten, die im aktuellen Objektgraph vorkommen. Bei Bedarf kann<br />

diese Maß noch bezüglich der Anzahl der erreichbaren Objekte normiert werden,<br />

AnzahlallerKanten<br />

indem man<br />

bildet. Damit ist die Dichte nicht mehr von der<br />

AnzahlerreichbareObjekte<br />

Größe des betrachtete Objektgraphen abhängig.<br />

• Wird auf der vorgestellten Plattform ein herkömmliches objektorientiertes Programm<br />

implementiert, dann ist pro Thread (d.h. pro Wurzelknoten) nur eine ComputingPage<br />

während eines GC-Laufs aktiv. Weil der Arbiter in dieser Phase Kommunikation<br />

unterbindet, können auch nur Referenzen in diesem Knoten gelöscht<br />

werden. Außerdem können maximal alle Referenzen entfernt werden, die im Knoten<br />

vorher vorhanden waren.<br />

5.5 Häufigkeit der GC-Zyklen<br />

Mit Hilfe der entwickelten Metriken lässt sich die Leistung des implementierten HW-<br />

GC-Algorithmus bestimmen und mit andere Implementierungen vergleichen. Darüber<br />

hinaus ermöglichen sie eine Optimierung der bestehenden Implementierung.<br />

Die Laufzeit eines GC-Zyklus ist weitgehend festgelegt. Sie hängt von der implementierten<br />

Datenstruktur zur Traversierung und der jeweiligen Objektstruktur ab. Neben<br />

kleinen Optimierungen, die möglicherweise das Einsparen einiger Taktzyklen erlauben,<br />

wäre zur Reduzierung der Laufzeit nur noch das Umkopieren der Objekte von Interesse.<br />

Die Kommunikation zwischen zwei Objekten, deren ComputingPages fünf Blöcke<br />

auseinanderliegen, benötigt aufgrund der dazwischenliegenden Register mindestens<br />

fünf Takte. Würde man die beiden Objekte in zwei nebeneinanderliegende ComputingPages<br />

verschieben, wäre eine Kommunikation in nur einem Takt möglich (siehe<br />

Abschnitt 4.3). Diese Art der Optimierung wird aber aufgrund der eingeschränkten<br />

Möglichkeiten aktueller partiell rekonfigurierbarer Architekturen weder kurz- noch mittelfristig<br />

zu realisieren sein.<br />

Ein weiterer Parameter zur Leistungssteigerung ist die Wahl einer optimalen Wartezeit<br />

zwischen zwei GC-Zyklen. Während eines GC-Zyklus ist die Kommunikation über den<br />

Arbiter deaktiviert. Erfordert der Programmablauf in dieser Zeit eine solche Kommunikation,<br />

dann kommt es zu einer Verzögerung: Das Programm muss warten, bis der


56 KAPITEL 5. ANALYSE DES HW-GC-ALGORITHMUS<br />

GC-Zyklus abgeschlossen ist. Um solche Verzögerungen zu vermeiden, ist also eine<br />

möglichst seltene Ausführung der <strong>Garbage</strong>-<strong>Collection</strong> zu empfehlen. Umgekehrt muss<br />

vermieden werden, dass der Programmablauf die Instanziierung weiterer Objekte anfordert,<br />

aber aktuell kein freier Speicherplatz zu Verfügung steht. Auch in diesem Fall<br />

muss das Programm ausgesetzt werden, bis ein GC-Zyklus abgeschlossen ist und freizugebende<br />

Objekte identifiziert hat. In diesem Fall hätte der GC-Zyklus bereits früher<br />

gestartet werden müssen, einen Verzögerung hätte durch eine nebenläufige Implementierung<br />

vermieden werden können.<br />

In der vorgestellten Implementierung wird das Starten der <strong>Garbage</strong>-<strong>Collection</strong> über<br />

eine einfache und konstante Verzögerung realisiert. Diese Methode ermöglicht eine<br />

kompakte und wenig fehleranfällige Implementierung. Während der Entwicklung sind<br />

verschiedene Kriterien entwickelt worden, die in einer Ausbaustufe zum optimierten<br />

Starten der <strong>Garbage</strong>-<strong>Collection</strong> eingesetzt werden können. Diese Kriterien sind im einzelnen:<br />

• Die Anzahl der freien Objekte. Solange noch ausreichend viele freie Objekte vorhanden<br />

sind, ist ein GC-Zyklus nicht erforderlich.<br />

• Der Erfolg der vorherigen GC-Zyklen. Wenn in den letzten Zyklen nur wenige<br />

oder gar keine weiteren freien Objekte gefunden wurden, so wird auch der nächsten<br />

GC-Zyklus wahrscheinlich nicht mehr Speicher freigeben können. In diesem<br />

Fall kann der Start der nächsten <strong>Garbage</strong>-<strong>Collection</strong> nach hinten verschoben werden.<br />

• Wenn die Ausführungszeit der <strong>Garbage</strong>-<strong>Collection</strong> gegenüber der Ausführungszeit<br />

einer Methode des Mutators kurz ist, dann ist auch der Arbiter nur für kurze<br />

Zeit gesperrt. Die Gefahr, den Mutator zu behindern ist damit geringer und die<br />

<strong>Garbage</strong>-<strong>Collection</strong> kann häufiger laufen. Um die Ausführungszeiten zu vergleichen,<br />

ist eine Analyse des Programmablaufs notwendig um zumindest näherungsweise<br />

zu bestimmen, wie oft eine Kommunikation über den Arbiter benötigt wird.


Kapitel 6<br />

Zusammenfassung<br />

6.1 Ergebnisse<br />

In dieser Diplomarbeit wurde die Realisierbarkeit einer automatischen Speicherverwaltung<br />

(<strong>Garbage</strong>-<strong>Collection</strong>) für rekonfigurierbare Hardwarestrukturen untersucht. Die<br />

Speicherverwaltung ist dabei in eine neuartige, objektorientierte Entwurfsmethodik eingebunden,<br />

deren Ziel eine flexiblere Anwendungsentwicklung für Hardware ist.<br />

Zuerst wurden die existierenden Mechanismen zur <strong>Garbage</strong>-<strong>Collection</strong> erarbeitet und<br />

bezüglich ihrer Tauglichkeit für rekonfigurierbare Hardwarestrukturen untersucht. Die<br />

Mark-Sweep-<strong>Collection</strong> hat sich hierbei als günstigstes Konzept erwiesen. Aufbauend<br />

auf dem Basis-Algorithmus wurden die Erweiterungsmöglichkeiten der Mark-Sweep-<br />

<strong>Collection</strong> untersucht. Die Erweiterung zu einem nebenläufigen Algorithmus ist für eine<br />

Umsetzung in Hardware besonders geeignet. Sie steigert die Geschwindigkeit der<br />

<strong>Garbage</strong>-<strong>Collection</strong> erheblich und ist in Hardware einfach zu realisieren.<br />

In einem nächsten Schritt sind die ausgewählten Mechanismen entsprechend der Rahmenbedingungen<br />

einer FPGA-basierten Hardwareumgebung modifiziert und angepasst<br />

worden. Insbesondere für die Traversierung im Mark-Schritt wurden besondere Strukturen<br />

entwickelt, um die Bedingungen der Plattform optimal auszunutzen. Schließlich<br />

wurden die einzelnen Mechanismen zu einem einzigen Algorithmus, dem HW-GC-<br />

Algorithmus, zusammengeführt.<br />

Es wurde gezeigt, dass die Leistungsfähigkeit des Algorithmus von den benötigten<br />

Systemressourcen abhängt, insbesondere den Kommunikationsressourcen. Der vorgestellte<br />

Algorithmus ist auf geringen Ressourcenbedarf ausgerichtet und benötigt nur<br />

zwei zusätzliche globale Signale. Für die übrigen Signale lassen sich die existierenden<br />

Kommunikationsressourcen nutzen, die während der <strong>Garbage</strong>-<strong>Collection</strong> durch den Arbiter<br />

inaktiv geschaltet sind.<br />

Dank der Ausnutzung nebenläufiger Mechanismen bei der Traversierung der Objekte<br />

und dem parallelen Ausführen von Mutator und Collector bleibt die <strong>Garbage</strong>-<strong>Collection</strong>


58 KAPITEL 6. ZUSAMMENFASSUNG<br />

trotzdem hoch performant.<br />

Der Algorithmus wurde mit Hilfe einer Softwaresimulation auf seine Korrektheit geprüft.<br />

Eine informelle Verifikation hat die korrekte Funktionalität des entworfenen Algorithmus<br />

bestätigt.<br />

Die existierenden Methodiken zur Messung der Leistungsfähigkeit haben sich als unzureichend<br />

erwiesen. Sie lassen sich nicht auf den vorgestellten Algorithmus anwenden,<br />

weil sie von klassischen Softwareumgebungen ausgehen.<br />

Um die Leistungsfähigkeit des hardwarebasierten GC-Algorithmus quantitativ beurteilen<br />

zu können, mussten neue Metriken entwickelt werden. Sie erlauben einen Vergleich<br />

des HW-GC-Algorithmus mit anderen Implementierungen und bilden die Grundlage<br />

für weitere Optimierungen des Algorithmus.<br />

6.2 Ausblick<br />

Der vorgestellte Algorithmus wirft weitere Problemstellungen auf, die im Rahmen dieser<br />

Arbeit nicht behandelt werden konnten.<br />

Zum Zeitpunkt der Fertigstellung dieser Arbeit ist die Laufzeitumgebung zur Ausführung<br />

objektorientierter Programme noch nicht abgeschlossen. Der HW-GC-Algorithmus<br />

ist, soweit möglich, in die bestehende Implementierung integriert. Die Ausführung einer<br />

reellen Anwendung ist jedoch noch nicht möglich. Präzisere Aussagen bezüglich der<br />

Leistungsfähigkeit der Implementierung lassen sich treffen, sobald diese Möglichkeit<br />

besteht. Allerdings limitieren weiterhin die eingeschränkten Ressourcen der Hardwareplattform<br />

die Anzahl der realisierbaren Programme.<br />

Eine Möglichkeit, die maximal verfügbaren Systemressourcen zu erweitern ist es,<br />

mehrere gekoppelte FPGAs zu verwenden. Es ist abzusehen, dass die Kommunikation<br />

mit einem anderen Chip deutlich mehr Zeit erfordern wird, als innerhalb eines Chips.<br />

Die Auswirkungen einer solchen, verteilten Architektur auf den Traversierungsalgorithmus<br />

sollten untersucht werden.<br />

Desweiteren gilt es zu überprüfen, welche Möglichkeiten zukünftige dynamisch rekonfigurierbare<br />

Plattformen bieten. Neben einer höheren Zahl von Objekten sind insbesondere<br />

die Möglichkeiten zum Verschieben von logischen Blöcken interessant, weil<br />

für die Laufzeit der Traversierung die geometrische Position einer ComputingPage von<br />

Bedeutung ist. Wenn die Möglichkeit besteht, diese Position mit vertretbarem Zeitaufwand<br />

zu verändern, sollte untersucht werden, ob die <strong>Garbage</strong>-<strong>Collection</strong> davon profitieren<br />

kann.<br />

Die Möglichkeiten zur Laufzeitoptimierung der <strong>Garbage</strong>-<strong>Collection</strong> müssen weiter<br />

verfeinert werden. Hierfür ist die Wartezeit zwischen zwei GC-Läufen von besonderer<br />

Relevanz, weil sie sich beliebig festlegen lässt. Besonders interessant wäre eine, auf<br />

dem Erfolg des letzten GC-Zyklus basierende, dynamische Anpassung der Zeit zwischen<br />

zwei Wartezyklen, da Lösung nur wenig zusätzlichen Aufwand erfordert. Ihre


6.2. AUSBLICK 59<br />

Leistungsfähigkeit muss allerdings erst evaluiert werden.<br />

Andere, komplexere Algorithmen zur <strong>Garbage</strong>-<strong>Collection</strong>, wie etwa die Generational<br />

<strong>Collection</strong> erfordern einen deutlichen Mehraufwand bei der Verwaltung der Objekte.<br />

Es muss untersucht werden, ab welcher Komplexität der implementierten Programme<br />

dieser Mehraufwand gerechtfertigt ist und zu einer höheren Gesamtleistung führt.<br />

Schließlich sollte in Betracht gezogen werden, ob sich der entworfene Algorithmus<br />

auch bei anderen Problemstellungen einsetzen lässt. Insbesondere die effiziente Traversierung<br />

eines Graphen bietet sich für eine Vielzahl von Anwendungen an.


60 KAPITEL 6. ZUSAMMENFASSUNG


Literaturverzeichnis<br />

[1] B. Mayer, “Object-Oriented Software Construction, Second Edition”, Prentice<br />

Hall ISBN 0-13-629155-4 March 21, 2000.<br />

[2] A. Kühn, S. Huss, “Dynamically Reconfigurable Hardware for Object Oriented<br />

Processing” International Conference on Parallel Computing in Electrical<br />

Engineering, Dresden, Germany September 2004.<br />

[3] Xilinx, “Programmable Logic Data Book,” 2001.<br />

[4] Xilinx, “XAPP290 : Two Flows for Partial Reconfiguration : Module Based or<br />

Small Bit Manipulations,” Xilinx Application Notes, 2004<br />

[5] Xilinx, Inc., “ISE Foundation 6.3i”<br />

http://www.xilinx.com/products/design_resources/design_tool/<br />

[6] Alpha Data Parallel Systems Ltd., “ADC-PMC-64 User Manual”, Ver. 1.1, 2002.<br />

[7] Synplicity, Inc., “Synplify Pro 8.0”<br />

http://www.synplicity.com/products/synplifypro/index.html<br />

[8] B.W. Kernighan, D. Ritchie, “C Programming Language, Second Edition”,<br />

Prentice Hall ISBN 0131103628<br />

[9] P. Roy, S. Seshadri, A. Silberschatz, S. Sudarshan, S. Ashwin, “<strong>Garbage</strong> collection<br />

in object-oriented databases using transactional cyclic reference counting”<br />

The VLDB Journal, 7(3):179-193, 1998.<br />

[10] A. Diwan, D. Tarditi, E. Moss, “Memory subsystem performance of programs<br />

using copying garbage collection”, Proceedings of the 21st ACM SIGPLAN-<br />

SIGACT symposium on Principles of programming languages, February 1994<br />

[11] E. W. Dijkstra, L. Lamport, A. J. Martin, C. S. Scholten, E. F. M. Stevens, “Onthe-Fly<br />

<strong>Garbage</strong> <strong>Collection</strong>: An Exercise in Cooperation”, Communications of<br />

the ACM, 21(11):966-957, November 1978.


62 LITERATURVERZEICHNIS<br />

[12] L. Huelsbergen, P. Winterbottom, “Very Concurrent Mark-&-Sweep <strong>Garbage</strong><br />

<strong>Collection</strong> without Fine-Grain Synchronization”, ACM SIGPLAN Notices Volume<br />

34 Issue 3, Proceedings of the 1st international symposium on Memory<br />

management, October 1998<br />

[13] M. Ben-Ari, “Algorithms for On-the-fly <strong>Garbage</strong> <strong>Collection</strong>”, ACM Transactions<br />

on Programming Languages and Systems (TOPLAS), Volume 6 Issue 3, July<br />

1984<br />

[14] D. Doligez, G. Gonthier, “Portable, unobtrusive garbage collection for multiprocessor<br />

systems”, Proceedings of the 21st ACM SIGPLAN-SIGACT symposium<br />

on Principles of programming languages, February 1994<br />

[15] T. Domani, E. K. Kolodner, E. Lewis, E. E. Salant, K. Barabash, I. Lahan, Y.<br />

Levanoni, E. Petrank, I. Yanorer, “Implementing an on-the-fly garbage collector<br />

for Java”, ACM SIGPLAN Notices , Proceedings of the 2nd international<br />

symposium on Memory management, Volume 36 Issue 1, October 2000<br />

[16] D. Doligez, X. Leroy, “A concurrent, generational garbage collector for a multithreaded<br />

implementation of ML”, Proceedings of the 20th ACM SIGPLAN-<br />

SIGACT symposium on Principles of programming languages, March 1993<br />

[17] J. Seligmann, S. Grarup, “Incremental Mature <strong>Garbage</strong> <strong>Collection</strong> Using the<br />

Train Algorithm”, Proceedings of ECOOP’95, Ninth European Conference on<br />

Object-Oriented Programming, Lecture Notes in Computer Science, Vol. 952,<br />

pp. 235-252, 1995<br />

[18] H. Lieberman, C. Hewitt, “A Real-Time <strong>Garbage</strong> Collector Based on the Lifetime<br />

of Objects”, Communications of the ACM, 26(6):419-429, June 1983.<br />

[19] M. Hirzel, A. Diwan, M. Hertz, “Connectivity-based garbage collection”, ACM<br />

SIGPLAN Notices , Proceedings of the 18th annual ACM SIGPLAN conference<br />

on Object-oriented programing, systems, languages, and applications, Volume<br />

38 Issue 11, October 2003<br />

[20] T.H. Cormen, C.E. Leiserson, R.L. Rivest, “Introduction to Algorithms”, MIT<br />

Press, ISDB 0-262-53091-0<br />

[21] B. Zorn, D. Grunwald “Evaluating Models of Memory Allocation”, ACM Transactions<br />

on Modeling and Computer Simulation, 4(1):107-131, January 1994<br />

[22] S. Blackburn, P. Cheng, K.S. McKinley, “Myths and Realities: The Performance<br />

Impact of <strong>Garbage</strong> <strong>Collection</strong>” Proceedings of ACM SIGMETRICS/Performance,<br />

ACM 1-58113-873-3/04/0006, June 2004


LITERATURVERZEICHNIS 63<br />

[23] M. Hirzel, J. Henkel, A. Diwan, M. Hind “Understanding the Connectivity of<br />

Heap Objects” Proceedings of ISSM ’02, ACM 1-58113-539-4/02/0006, June<br />

2002<br />

[24] C. Ruggieri, T.P. Murtagh, “Lieftime Analysis of Dynamically Allogcated Objects”<br />

Proceedings of 15th Annual ACM SIGACT-SIGPLAN Symposium on Principles<br />

of Programming Languages, January 19989<br />

[25] D. Stefanovic, K.S. McKinley, J.E.B. Moss, “On Models for Object Lifetime<br />

Distribuntions” Proceedings of ISSM ’00, ACM 1-58113-263-8/00/10, October<br />

2000<br />

[26] S. Oaks, H. Wong, “Java Threads, Third Edition” O’Reilly, ISBN 0596007825<br />

[27] J. Gosling, B. Joy, G. Steele, G. Bracha, “Java(TM) Language Specification,<br />

Third Edition” Addison-Wesley Professional, ISBN: 0321246780

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!