Leopold–Franzens–Universität Innsbruck Institut für Informatik Forschungsgruppe DPS (Distributed and Parallel Systems) Workflow Simulation Execution Bachelorarbeit Betreuer: Kassian Plankensteiner Felix Koenig (0917104) [email protected] Innsbruck 22. Mai 2012 Zusammenfassung Workflow Execution Simulation ist eine Arbeit, welche die Simulation von Workflows auf Grids realisiert. Es sollen Workflows in Form von gerichteten azyklischen Graphen erstellt werden können und mittels diversen Scheduling Verfahren auf die Ressourcen verteilt werden. Zudem muss eine Ausführung des Workflows simuliert werden. Die Algorithmen und deren Implementierung sind in der Thesis aus theoretischer und praktischer Sicht beschrieben. Das Programm verwendet die Funktionalität zur Erstellung von Grids und der Ausführung von Jobs die von dem Simulator GroudSim bereitgestellt wird. Inhaltsverzeichnis 1 Einführung 1.1 Definition von Grids und Clouds . . . . . . . . . . . . . . . . . . 1.2 Problem und Motivation . . . . . . . . . . . . . . . . . . . . . . . 1.2.1 Fehlerverhalten und erneutes Senden von Jobs . . . . . . 1 1 2 3 2 Theoretische Grundlagen 4 2.1 Verwendete Workflows . . . . . . . . . . . . . . . . . . . . . . . . 4 2.1.1 Wien2k . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 2.1.2 Povray . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 2.1.3 Balanced-Graph . . . . . . . . . . . . . . . . . . . . . . . 5 2.1.4 Leveled-Graph-With-Preselected-Maximum-Link-Complexity (LDPMC) . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 2.1.5 LU-Graph . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.2 Heterogeneous-Earliest-Finish-Time-Algorithmus(HEFT) . . . . . 7 2.2.1 Das Scheduling-Problem . . . . . . . . . . . . . . . . . . . 7 2.2.2 Priorisierungsphase . . . . . . . . . . . . . . . . . . . . . . 8 2.2.3 Zuordnungsphase zu den Prozessoren . . . . . . . . . . . . 8 2.3 Fehlertoleranz und Fehlerreaktion . . . . . . . . . . . . . . . . . . 9 2.4 HEFT-Replication-Algorithmus . . . . . . . . . . . . . . . . . . . 10 2.5 Replicate All . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.6 Resubmission-Impact Verfahren . . . . . . . . . . . . . . . . . . . 10 2.6.1 Algorithmus . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.7 Dynamic-Enactment Strategie . . . . . . . . . . . . . . . . . . . . 11 2.7.1 Allgemeines . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.7.2 Der Algorithmus . . . . . . . . . . . . . . . . . . . . . . . 12 2.7.3 RI-Rescheduling . . . . . . . . . . . . . . . . . . . . . . . 13 3 Implementierung 3.1 Ziele des Simulators und Programmeingabeinformationen . . . 3.2 Verwendung und Konzepte von GroudSim . . . . . . . . . . . . 3.3 Programmaufbau . . . . . . . . . . . . . . . . . . . . . . . . . . 3.3.1 Workflow Generator . . . . . . . . . . . . . . . . . . . . 3.4 Scheduling-Komponente . . . . . . . . . . . . . . . . . . . . . . 3.4.1 HEFT-Implementierung . . . . . . . . . . . . . . . . . . 3.4.2 HEFT-Replication-Implementierung ohne erneutes Scheduling . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.4.3 Resubmission-Impact Implementierung . . . . . . . . . . 3.4.4 Parallelisierung von Resubmission-Impact . . . . . . . . 3.5 Die Ausführungskomponente . . . . . . . . . . . . . . . . . . . iv . . . . . . 15 15 15 17 17 22 22 . . . . 24 24 25 26 3.5.1 3.5.2 3.5.3 Schnittstelle zu GroudSim . . . . . . . . . . . . . . . . . . 27 Implementierung des Enactment Verfahrens . . . . . . . . 27 Weitere Eingabedateien anhand von Beispielen erklärt . . 29 4 Experimente 4.1 Performancetest . . . . . . . . . . . . . . . . . 4.2 Kosten- und Laufzeitenvergleich verschiedener 4.2.1 Erfolgsrate . . . . . . . . . . . . . . . 4.2.2 Laufzeitenvergleich . . . . . . . . . . . 4.2.3 Kostenvergleich . . . . . . . . . . . . . 5 Zusammenfassung . . . . . . . . . Fehlerszenarien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 31 32 32 33 34 39 1 Einführung In den heutigen Anwendungsgebieten von Rechenmaschinen spielen Grids und Clouds eine wesentliche Rolle. Es gibt diverse Bereiche in der Wirtschaft und Forschung, bei welchen viel Rechenleistung verlangt wird und große Mengen an Daten verarbeitet werden müssen. Es kann dann durchaus von Nöten sein, diverse Grid- oder Cloudsysteme anzumieten um den Mangel an benötigten Rechenressourcen auszugleichen und auszubauen. Da die Anmietung solcher Systeme Geld kostet, wird man sich bemühen seine Ressourcen möglichst effizient zu nützen. Es geht somit um folgende Fragestellungen. ”Wie viele Ressourcen benötigt man mindestens um eine vorgegebene Gesamtlaufzeit einzuhalten?” und ”Wie wirken sich Systemfehler in den Ressourcen auf die Laufzeit aus?”. 1.1 Definition von Grids und Clouds Im obigen Teil wurden Grids und Clouds als mögliche Ressourcen angegeben. Als Grid bezeichnet man ein System welches drei Eigenschaften besitzt die im Folgenden beschrieben werden.[1]. Die erste Eigenschaft bezieht sich auf die koordination von Ressourcen, wie zum Beispiel Massenspeicher, Datenbanken, Anwendungen oder Rechner. Diese Ressourcen können von diversen Benutzern angesprochen werden. Eine Ressource wäre in dieser Arbeit eine Grid Site, welche Prozessoren zur Ausführung verschiedener Programme anbietet. Im Kapitel über GroudSim sind diese Ressourcen genauer beschrieben. Ein Grid bietet zudem als zweite Eigenschaft Schnittstellen an, die der Authorisierung und dem Ressourcenzugriff dienen. Ein Grid koordiniert seine eigenen Ressourcen in einer Art und Weise, welche den Erwartungen des Benutzers gerecht werden soll. Dem Benutzer sollen Dienstgüte bereitgestellt werden, dies ist die dritte Eigenschaft von Grids. Es können neue Ressourcen kombiniert werden, um zum Beispiel die Gesamtleistung des Systems zu steigern. Beim Grid-Computing geht es hauptsächlich darum, einen virtuellen Hochleistungsrechner, welcher eigentlich aus mehreren gekoppelten Rechnern besteht, zu erzeugen. Weitere Informationen zu Grids findet man in [2]. Das Prinzip von Grids ist, dass sich mehrere Nutzer und Organisationen die Ressourcen teilen. Bei Clouds geht es darum, dass jeder Nutzer auch individuell Ressourcen anmieten kann. Zudem gibt es hier mehrere Varianten. Clouds können Firmenintern angelegt sein, dabei handelt es sich um private Clouds. Sie können öffentlich zugänglich sein, wie zum Beispiel der Cloud Service von Amazon. Daneben gibt es hybride Varianten, die beide Konzepte vereinen und Community-Clouds. Letzere bietet für Gemeinschaften und Mitglieder breiterer Organisationen, wie zum Beispiel Personen innerhalb einer Universität, Rechenresourcen an. Weitere Informatio- 1 1 Einführung nen zu Cloud Systemen findet man in [3] und [4]. Eine ausführlichere Definition von Cloud Computing, findet man in [5]. 1.2 Problem und Motivation Es seien n Ressourcen gegeben die Grids bzw. Clouds repräsentieren. Diesen Rechenmaschinen sollen nun k Jobs zugeteilt werden. Ein Job stellt meistens ein Programm, oder einen Arbeitsablauf dar welcher viel Rechenleistung und Zeit benötigt. Zwischen den Jobs bestehen sehr wohl Abhängigkeiten. Es ist somit möglich, dass das Ergebnis von Job A für Job B von Nöten ist. Man kann eine einfache geschachtelte Rechenoperation zu Rate ziehen. Wenn man den Term (5 · 2) + 10 betrachtet, wird man zum Schluss kommen, dass die Bekanntgabe des Ergebnisses nur dann erfolgen kann, wenn zuerst der Subterm (5 · 2) ausgewertet wird. Man könnte in unserem Fall die Operation (5 · 2) als Job A bezeichnen. Job B bildet die Operation x + 10 ab, wobei x das Ergebnis von Job A darstellt. Abstrakt gesehen bilden Job A und Job B einen gerichteten Graphen mit zwei Knoten. In der Praxis wird man viele Jobs mit vielen Abhängigkeiten vorfinden, weshalb sich ein großer Graph bildet. Diesen Graph bezeichnen wir in diesem Projekt als Workflow. Die Knoten werden in dieser Arbeit als solche identifiziert oder Jobs genannt. Die Ressourcen besitzen jeweils eine unterschiedliche Rechenleistung, die Jobs eine unterschiedliche Problemgröße. Das Problem besteht nun darin, den Graphen effizient auf die vorhandenen Ressourcen zu verteilen und möglichst optimale Start- und Endzeitpunkte für die Ausführung der jeweiligen Jobs zu definieren. Mit dem Kriterium ”optimal” wird hier das Ziel angesprochen, die Gesamtlaufzeit für alle Jobs zu minimieren. Betrachtet man die Zuteilung der 2 1.2 Problem und Motivation Jobs an die jeweiligen Ressourcen, also den Schedule der Jobs, ensteht wieder ein Graph welcher vom ursprünglichen Workflow abhängig ist. In diesem Graphen gibt es einen kritischen Pfad, dieser gibt in Summe die längste Laufzeit vor. Konkret gilt es diese Zeit zu minimieren. 1.2.1 Fehlerverhalten und erneutes Senden von Jobs Das Scheduling Problem kann mithilfe eines Algorithmus gelöst werden. Es ergibt sich jedoch noch ein weiteres Problem. Sind einige Ressourcen fehleranfällig, muss man aufgrund eines Systemfehlers Jobs teilweise abbrechen, um sie anschließend neu auszuführen. Somit wird die vorhergehende Berechnung der Start- und Endzeitpunkte der jeweiligen Jobs invalidiert, da sich das gesamte Szenario verzögert. Bei vielen Fehlern können teilweise große Abweichungen zum ursprünglichen Schedule entstehen. Die reale Fertigstellungszeit des Workflows wird sich von der zuvor berechneten stark unterscheiden. In vielen Fällen ist es nun nötig, das Scheduling anzupassen und neu zu errechnen. Um ein solches Verhalten zu testen benötigt man nun einen Simulator, welcher diese Szenarien simuliert und uns Auskunft über die benötigte Gesamtlaufzeit und den damit verbundenen Kosten liefert. Ziel dieser Arbeit ist es nun einen solchen Simulator zu realisieren. 3 2 Theoretische Grundlagen Um einen Simulator zu realisieren welcher dieses Workflow-Scheduling Problem löst und auf Fehlerverhalten in den Ressourcen reagiert, benötigt man diverse Algorithmen und Verfahren. Der Algorithmus welcher auf das Verteilungsproblem von Jobs auf Ressourcen eingeht und in dieser Simulation implementiert ist, trägt den Namen HEFT (Heterogeneous-Earliest-Finish-TimeAlgorithm). Des Weiteren sollte der Simulator Fehlerszenarien realisieren. Anschließend müssen Verfahren angewendet werden, welche die Auswirkungen der Fehler möglichst gering halten. Wenn häufig Fehler auftreten und fast immer die gleichen Jobs davon betroffen sind, so wird man sich überlegen, ob man nicht mehrere Kopien dieser Jobs auf verschiedene Ressourcen auslagert, anstatt diese Jobs andauernd wieder an die gleiche Ressource zu schicken. Eine wichtige Rolle bei dieser Überlegung spielt ebenfalls die benötigte Dauer der einzelnen Jobs. Benötigt ein Job viel Rechenleistung und dauert er sehr lange, so werden vermutlich mehrere Kopien von ihm erzeugt, da ein erneutes Ausführen des Jobs auf der Ressource vermutlich mehr Zeit beanspruchen würde. Das Resubmission-Impact Verfahren in Kombination mit HEFT-Replication, bietet hier einen möglichen Lösungsweg. Abschließend benötigt man ein Verfahren, welches den Gesamtablauf simuliert und die Algorithmen korrekt verwendet. Dieses Verfahren trägt den Namen Dynamic-Enactment und bildet den Kern der Simulation. Im Folgenden werden die einzelnen Algorithmen detailiert erklärt. Der Simulator unterstützt nur eine begrentzte Anzahl an Workflows. Die Eigenschaften und Beschaffenheit dieser Graphen wird auf den folgenden Seiten ebenfalls erörtert. 2.1 Verwendete Workflows Die Workflows welche in diesem Projekt verwendet werden sind allesamt gerichtete azyklische Graphen. Die Knoten stellen hierbei Jobs, dar. Gibt es eine Kante zwischen einem Job X zu einem Job Y, so muss der Job X zuerst abgearbeitet werden. Eventuell wird ein Datentransfer von X nach Y getätigt bevor man Y ausführen kann. Ein Knoten besitzt eine bestimmte Anzahl an Instruktionen, welche von den Ressourcen abgearbeitet werden müssen um den Job erfolgreich zu beenden. Diese Zahl wird auch die Problemgröße genannt. Die Kanten des Graphen können mit einem Wert gewichtet sein welcher die zu transferierende Datenmenge darstellt. Die Anzahl an Instruktionen pro Knoten ist vom Typus des Workflows abhängig. Auf das Verfahren zur Bestimmung der Instruktionsanzahl wird beim Kapitel über die Implementierung eingegangen. Für die Realisierung der Algorithmen, muss jeder Workflow jeweils aus einem ausgezeichneten Wurzel- und Endknoten bestehen. Ist dies nicht der Fall, so wird der fehlende Knoten mit einer minimalen Problemgröße hinzugefügt. 4 2.1 Verwendete Workflows 2.1.1 Wien2k Der Wien2k Workflow setzt sich aus einzelnen Iterationen zusammen. Eine Iteration besteht aus fünf Ebenen, einem Wurzelknoten n1, n Nachfolgeknoten, einem gemeinsamen Nachfolger n6 auf welchen wieder n Folgeknoten fallen. In der letzten Ebene befindet sich wieder ein einzelner Nachfolgeknoten n11. Es können i Iterationen aneinandergereiht werden, wodurch sich ein Workflow der Höhe i · 5 ergibt. Die Anzahl an Instruktionen für die n Knoten in der Ersten Ebene ist meistens um den Faktor 10 größer als jene für die n Knoten in der dritten Ebene. Abbildung 2.1: Ein Beispiel eines Wien2k-Graphen [6] 2.1.2 Povray Dieser Workflowtypus besteht lediglich aus 3 Ebenen. In der zweiten Ebene befinden sich n Knoten welche allesamt einen gemeinsamen Nachfolger n9 besitzen. Um den Graphen für Algorithmen zu standardisieren wird noch ein Wurzelknoten n1 vor den n Knoten in der ersten Ebene angehängt. Standardmäßig besteht der Povray Graph nur aus Ebene 2 und 3. Die Anzahl an Instruktionen von n9 hängt von der Anzahl und Problemgröße der Vorgänger ab. Abbildung 2.2: Ein Beispiel eines Povray-Graphen [6] 2.1.3 Balanced-Graph Dieser Graph ist dem Wien2k-Workflow sehr ähnlich. Er besteht aus einem Wurzelknoten und aus k Ebenen welche aus jeweils n Knoten bestehen und 5 2 Theoretische Grundlagen einen Block bilden. Die n Knoten innerhalb des Blockes haben wieder n Nachfolger, wobei jeweils ein Knoten genau einen Nachfolger besitzt. Die Knoten in der letzten der k Ebenen besitzen wieder einen gemeinsamen Nachfolger. Es kann in einem Balanced Graphen mehrere Blöcke geben, die jeweils durch einen gemeinsamen Vorgänger- beziehungsweise Nachfolgeknoten voneinander getrennt sind. Abbildung 2.3: Ein Beispiel eines Balanced-Graphen [6] 2.1.4 Leveled-Graph-With-Preselected-Maximum-Link-Complexity (LDPMC) Dieser Graph besitzt eine Anzahl an Ebenen k, in jeder Ebene gibt es bis zu n Knoten. Ab der zweiten Ebene bestimmt ein Wert l wieviele Vorgänger ein Knoten in der Ebene maximal besitzen darf. Die Auswahl und Anzahl der Vorgänger erfolgt jeweils durch Zufall wobei ein Vorgänger eines Knotens v maximal eine Ebene über v liegen darf. Es wird abschließend noch ein letzter Knoten in den Graphen eingefügt um ihn für Algorithmen zu standardisieren. Abbildung 2.4: Ein Beispiel eines LDPMC-Graphen [6] 6 2.2 Heterogeneous-Earliest-Finish-Time-Algorithmus(HEFT) 2.1.5 LU-Graph Bei diesem Workflow bestimmt ein einziger Faktor, nämlich die Anzahl k an Ebenen das Aussehen des gesamten Graphen. Die Höhe k ist dabei immer eine gerade Zahl. Betrachtet man den Graphen vom Endknoten zum Wurzelknoten so stellt man fest, dass sich die Anzahl an parallelen Knoten in jeder Ebene mit einer geraden Zahl um 1 erhöht. Abbildung 2.5: Ein Beispiel eines LU-Graphen [6] 2.2 Heterogeneous-Earliest-Finish-TimeAlgorithmus(HEFT) 2.2.1 Das Scheduling-Problem Der HEFT-Algorithmus bietet eine mögliche Lösung für eine effiziente Zuordnung der Jobs an die Ressourcen. Konkret ist ein Workflow W = (V, E) mit V Knoten gegeben, wobei V die Menge an v Knoten bezeichnet und E die Menge an e Kanten. Der Knoten welcher keine Vorgänger besitzt wird als entry job bezeichnet, derjenige welche keine Nachfolger besitzt wird exit job genannt. In dieser Arbeit beschäftigen wir uns nur mit Grids als möglichen Ressourcen. Jeder Grid beinhaltet mehrere Prozessoren, insgesamt sind q Prozessoren aus einer Menge p Q verfügbar. Die Leistung der Prozessoren ist von Grid zu Grid verschieden. Die Knoten sollen nun unter Einhaltung der Abhängigkeiten zwischeneinander und unter minimierung der Ausführungszeit, den jeweiligen Prozessoren zugewiesen werden. Dieses Problem ist NP-Hart, um eine effiziente Implementierung ermöglichen zu können, wendet man hier ein heuristisches Verfahren, welches eine gute Lösung bietet, an. Der HEFT-Algorithmus ist der Klasse der List-Scheduling-Algorithmen zuzuordnen. Der Algorithmus wird in 2 Phasen eingeteilt. Phase 1 dient der Priorisierung der Jobs, Phase 2 dient der Zuordnung der Jobs an die einzelnen Prozessoren. Die generellen Schritte des Algorithmus sind: 7 2 Theoretische Grundlagen 1 2 3 4 5 6 7 8 Bilde Mittelwerte für die Laufzeiten von der Jobs und Kanten auf allen Ressourcen Berechne den Rank für alle Jobs, beginnend mit dem letzten Sortiere die Jobs nach den Ranks in absteigender Reihenfolge in einer Liste l solange es unzugeordnete Jobs gibt: Wähle den ersten Job n von l für jeden Prozessor p mache folgendes: Berechne den Fertigstellungszeitpunkt für n auf p EFT(n,p) Ordne Job n demjenigen Prozessor zu welcher den EFT-Wert minimiert 2.2.2 Priorisierungsphase Diese Phase bezieht sich auf die ersten drei Schritte im Algorithmus. Zunächst wird ein Mittelwert wmi für die Ausführungsdauer des jeweiligen Jobs ni gebildet. Dabei stellt W eine v × q Matrix dar, in welcher die Kosten wi,j für ni auf dem jeweiligen Prozessor pi eingetragen sind. Der Mittelwert wmi ergibt sich durch: q X wmi = wi,j /q[7] j=1 Zusätzlich zu wmi bildet man noch ein Mittelwert cmi,k über die Datentransferzeiten von Job ni zu Job nk . In diesem Projekt spielen diese Zeiten allerdings keine Rolle und sind mit einem Standardwert initialisiert. Anschließend geht es zu Schritt 2, der Berechnung des Wertes ranku für jeden Knoten. Ziel ist es, eine Liste an Knoten zu erstellen welche Schritt für Schritt den Ressourcen zugeordnet werden können. Hier kommt diesem Wert eine große Bedeutung hinzu. Der ranku bildet sich aus der Formel: ranku (ni ) = wmi + max (cmi,j + ranku (nj ))[7] nj succ(ni ) succ(ni ) ist die Menge an direkten Nachfolgern von ni . Der ranku errechnet sich aus einer Rekursion, beginnend mit dem Endknoten nlast des jeweiligen Graphen. Für nlast gilt somit folgende Rekursionsabbruchbedingung: ranku (nlast ) = wmlast [7] Nun werden die Knoten anhand des jeweiligen ranku , in absteigender Reihenfolge sortiert. Dies hat zur Folge, dass sich nun der Graph in Form einer Liste abzeichnet. Die jeweiligen Abhängigkeiten zwischen den Jobs bleiben bestehen. So befindet sich ein Job ni mit einem kleineren Index wie Job nj auch im Graphen auf mindestens der gleichen Ebene wie Job nj . Falls sich ni und nj im Workflow auf derselben Ebene befinden, so wird zufällig bestimmt ob ni einen kleineren Index in der Liste besitzt oder nicht. 2.2.3 Zuordnungsphase zu den Prozessoren In dieser Phase werden die Jobs schlussendlich den jeweiligen Prozessoren zugeteilt. Das entscheidende Kriterium für eine solche Zuteilung ist der EF T -Wert, 8 2.3 Fehlertoleranz und Fehlerreaktion dieser bezeichnet das frühestmögliche Abarbeitungsende des Jobs. Er wird aus dem EST -Wert, dem frühestmöglichen Startzeitpunkt berechnet. EST (ni , pj ) = max pavailable[j], max (AF T (nm ) + cm,i ) [7] nm pred(ni ) Der Startzeitpunkt für den Knoten ni auf dem Prozessor pj ergibt sich somit aus dem Maximum der fix zugeordneten Endzeiten AF T aller Vorgänger nm und deren Transferzeiten cm,i zu ni . Zusätzlich muss noch in Betracht gezogen werden, ob der Prozessor pj zum Startzeitpunkt und für die Dauer der Ausführung von nj verfügbar ist. Diese Zeit wird durch pavailable[j] dargestellt. Das wichtige ist hierbei, einen freien Zeitpunkt zu finden, in dem die Zeitspanne groß genug ist um den Knoten ni ausführen zu können. Der Beginn des frühestmöglichen, freien Zeitschlitzes, auf welchen dieses Kriterium zutrifft wird in pavailable[j] gespeichert. Das hat zur Folge, dass neue Knoten auch zwischen 2 bereits zugeteilten Knoten, unter Beibehaltung der Präzedenzen, eingefügt werden können. Der EF T -Wert ergibt sich nun folgendermaßen: EF T (ni , pj ) = wi,j + EST (ni , pj )[7] Der Endgültige Wert für die Endzeit AF T ergibt sich nun aus dem minimum aller EF T -Werte. AF T (ni ) = min (EF T (ni , pj )) [7] pj Q Der Knoten wird somit dem Prozessor pj zugeteilt. Folglich errechnet sich der AST -Wert, also der endgültige Startzeitpunkt durch folgende Formel: AST (ni ) = AF T (ni ) − wi,j [7] 2.3 Fehlertoleranz und Fehlerreaktion In diversen Szenarien muss mit fehleranfälligen Ressourcen gearbeitet werden. Häufig führt ein Ausfall eines Grids oder eines Prozessors zum Abbruch mehrerer Jobs. Die Frage ist nun, wie man auf solche Fehler reagieren kann, ohne dass sich die tatsächliche Laufzeit des Workflows stark von der Ursprunglichen unterscheidet. Im Allgemeinen gibt es 2 mögliche Lösungsansätze. Es können bereits im Vorhinein Replikate pro Job angefertigt werden. Sind genügend Ressourcen zur Verfügung, kann man die Replikate ebenfalls effizient auf die Ressourcen verteilen, ohne die Gesamtlaufzeit wesentlich zu verzögern. Dabei tolerieren r Replikas einen maximalen Ausfall von r Prozessoren. Der Vorteil ist, dass die Gesamtlaufzeit minimal verzögert wird. Der Nachteil besteht allerdings in einer höheren Ressourcennutzung. Die andere Variante würde zur Ausführungszeit des Workflows zum Einsatz kommen. Sobald ein Job fehlschlägt wird er nochmals an dieselbe Ressource gesendet, hierbei kann man ebenfalls ein Maximum von s erneuten Versuchen definieren. Der Vorteil dieser Variante ist, dass nicht mehr Ressourcen als ursprünglich vorgesehen benötigt werden. Der Nachteil besteht in einer teils großen Verzögerung der Gesamtlaufzeit. Ziel ist es nun beide Verfahren zu verwenden und dabei ein gutes Mittelmaß zwischen r und s zu finden. Die zuständigen Algorithmen zur Lösung dieses Problems lauten HEFT-Replication und Resubmission-Impact. 9 2 Theoretische Grundlagen 2.4 HEFT-Replication-Algorithmus Das HEFT-Replication Verfahren, behandelt die effiziente Zuteilung der Knoten und ihrer Replikas an die Ressourcen, mithilfe des HEFT-Algorithmus. Der zusätzliche Parameter in diesem Algorithmus ist der Vektor RV , welcher die vorgesehene Anzahl an Replikas ri pro Knoten ni V beinhaltet. Die Phasen des Algorithmus sind analog zu denen des HEFT-Verfahrens. Zunächst wird der ranku für jeden Knoten ni ermittelt, anschließend sortiert der Algorithmus die Knoten in einer Liste. Daraufhin erfolgt die Ressourcenzuteilung der Jobs, mittels Berechnung von AF T und AST . Hier gibt es allerdings einen entscheidenden Unterschied zu HEFT. Teilt man einen Knoten ni einer Ressource pi zu, so werden seine Vorgänger vi pred(ni ) überprüft. Der Algorithmus ermittelt ob jeder Nachfolger vsuccj succ(vi ) von vi bereits einer Ressource zugeordnet ist. Trifft dies zu, so werden ri RV Replikas von vi erzeugt. HEFT Replication ordnet sie dann, aufgrund der AF T -Berechnung wie sie HEFT vorgibt, den Ressourcen zu. Diese Strategie hat zur Folge, dass Replikas erst dann erzeugt und den Ressourcen zugeteilt werden, wenn alle Nachfolger bereits an eine Ressource gebunden sind. Dadurch ist es unwahrscheinlich, dass ein Replika einem Nicht-Replika eine schnelle Ressource entwendet. Betrachtet man das Ergebnis dieses Scheduling-Verfahrens als strikte Zuordnung, so entstehen allerdings auch offensichtliche Probleme. Schlägt die Abarbeitung eines Jobs fehl, so muss man sich auf seine Replikas verlassen, diese starten allerdings später als seine direkten Nachfolger, somit sind die Präzedenzen nicht mehr gewährleistet. Dieses Problem wird aber bei der Dynamic-Enactment Strategie noch einmal aufgegriffen und umgangen. Es bleibt allerdings noch eine Frage offen, nämlich die Ermittlung von ri . In meinem Simulator existieren 2 Varianten wie man diesen Wert berechnen kann. Diese Varianten werden nun vorgestellt. 2.5 Replicate All Diese Heuristik ist im Prinzip recht simpel. Sie beschäftigt sich mit der Berechnung von ri für jeden Knoten. Dabei wird der Funktion replicateAll einfach ein Parameter resmax übergeben. Replicate All konstruiert nun einen Vektor RV in dem pro ni die Werte ri gespeichert sind. Dabei wird ri mit repmax initialisiert. Nun erfolgt der Aufruf von HEFT-Replication mit RV . 2.6 Resubmission-Impact Verfahren Die Replicate-All Heuristik ist in diversen Fällen etwas unzureichend. Es werden zwar alle Knoten repliziert, allerdings ist die Anzahl an Replikas sehr hoch. Es entstehen somit 2 Nachteile. Zum Einen ist die Ressourcenbelegung ziemlich groß, zum Anderen kann es dadurch zu einer Beeinträchtigung der Gesamtausführungszeit des Workflows kommen. Dieser negative Effekt stellt sich vor allem dann ein, wenn wenige Ressourcen vorhanden sind. Wie oben beschrieben benötigt man oft ein ausgleichendes Verfahren. Dieses nennt sich Resubmission- 10 2.7 Dynamic-Enactment Strategie Impact (kurz RI). Es ist nicht sinnvoll von jedem Knoten Replikas zu erstellen, da ein etwaiges erneutes Senden von diversen Knoten oft nur wenig Auswirkungen auf die Gesamtlaufzeit hat. Ziel ist es nun, solche Jobs zu finden und die Anzahl an ri für jene Jobs klein zu halten, im Gegensatz zu Jobs bei welchen sich ein erneutes Senden stärker auf die Gesamtlaufzeit auswirken würde. 2.6.1 Algorithmus Die Parameter für den Algorithmus sind der Workflow W mit den Knoten V , die Ressourcen Q und die Werte resmax und repmax . Die beiden letzteren beschreiben jeweils die Maximale Anzahl an erneuten Sendungen eines Jobs und das Maximum an Replikas. Zu Beginn wird der HEFT Algorithmus mit dem Workflow W und den Ressourcen Q aufgerufen. Das Ergebnis speichert Resubmission Impact in einer Variablen namens hef t. Es werden nun der Reihe nach alle ni V abgearbeitet. Bei jedem Knoten ist die Anzahl an Instruktionen in einer Variable instructionsi gespeichert. Nun berechnet man den Aufwand, um einen Knoten mit der in resmax definierten Anzahl an Wiederholungen zu senden und speichert diesen in der Variable instructions0i . instructions0i = instructionsi · resmax [8] Anschließend wird für jeden Knoten ni der HEFT-Algorithmus aufgerufen, jeweils mit der neuen Anzahl an Instruktionen, instructions0i . Die Gesamtlaufzeit wird jeweils in der Variablen hef ti gespeichert. Nun errechnet der Algorithmus die Differenz di , der beiden Gesamtlaufzeiten di = hef ti − hef t. Der Wert von hef t muss nicht jedes mal neu berechnet werden, da er sich nie verändert. Je höher di ist, umso stärker beeinträchtigt ein erneutes Senden dieses Knotens die Gesamtlaufzeit. Nun wird für jeden Knoten ni ein normierter Wert berechnet, welcher die Gewichtung von di im Vergleich zu allen anderen nj beschreibt. Diesen speichert der Algorithmus in einer Variablen RIi , wobei der Wertebereich jeweils zwischen 0 und 1 liegt. RIi = di / max(dj )[8] Daraus errechnet man dann den tatsächlichen ri -Wert. ri = bRIi · repmax c[8] Statt f loor könnte man hier ebenfalls ceil verwenden. Bevor man HEFT-Replication startet, werden die einzelnen ri wieder in dem Vektor RV gespeichert. 2.7 Dynamic-Enactment Strategie 2.7.1 Allgemeines Das RI-Scheduling Verfahren schafft einen guten Ausgleich zwischen der Anzahl an Replikas und den erneuten Sendevorgängen. Allerdings basieren die 11 2 Theoretische Grundlagen Verfahren alle auf demselben Schema. Die Zuteilung der Ressourcen erfolgt jeweils bevor man den Workflow schlussendlich abarbeitet. Während der Laufzeit kommt den Algorithmen noch keine Funktionalität hinzu. Treten allerdings ungewöhnlich viele Fehler während dieser Zeit auf, so wird die Ausführungszeit des Workflows stark von der Ursprünglichen, durch HEFT oder HEFT-Replication berechneten, abweichen. Die Jobs werden dann vorwiegend erneut an die Ressourcen gesendet, da die Abarbeitung ihrer Replikas bereits fehlgeschlagen ist. Hier sollte man steuernd eingreifen, etwa durch ein erneutes Auslösen und Anpassen des Scheduling Algorithmus. Das Dynamic-Enactment Verfahren bemüht sich diesen Risikofaktor auszugleichen. Zudem wird dem User eine realistische Gesamtlaufzeit, eine sogenannte Soft-Deadline errechnet. 2.7.2 Der Algorithmus Im Prinzip ist das Dynamic-Enactment eine Erweiterung des RI Verfahrens. Die Übergabeparameter sind die gleichen, jedoch wird das Enactment um zwei Werte erweitert. Hierbei spezifiziert cmax die maximale Anzahl an Rescheduling Aktionen. Der zweite Parameter fr ist ein Faktor, welcher vor allem bei der Berechnung der Soft Deadline zu tragen kommt. Dieser Wert gibt einen Prozentsatz an, wie weit die reale Ausführungszeit von der im Scheduling berechneten abweichen darf, bevor ein erneuter Aufruf des Scheduling-Algorithmus getätigt wird. Zunächst ruft Dynamic-Enactment den HEFT-Replication Algorithmus auf um ein initiales Schedule zu berechnen. Die Gesamtlaufzeit des Schedule wird in der Variablen tW f gespeichert, der Zähler c welcher die Anzahl an Reschedules protokolliert, wird auf 1 gesetzt. Anschließend folgt die Ermittlung der Soft (c) Deadline td , nachdem ein Schwellwert tr für das Überschreiten der Laufzeit des Workflows bestimmt ist. t(c) r = tW f · fr [8] td = tW f · 1 + fr · cX max ! (1/k) [8] k=1 Um die oben angeführten Berechnungen verstehen zu können, sollte man die weiteren Prinzipien des Algorithmus verstanden haben, weshalb ich erst zum Schluss darauf eingehen werde. Im Folgenden werden nun alle ni V ermittelt, welche bereit sind an ihre Ressource gesendet zu werden. Dabei handelt es sich um Knoten, deren Vorgänger allesamt abgearbeitet worden sind, oder um jene ni welche keine Vorgänger besitzen. Diese Jobs sendet man dann an die jeweilige Ressource für deren Abarbeitung. Sobald ein Job komplett ausgeführt worden ist, wird seine benötigte Laufzeit treal ni mit der maximal erlaubten tni Verglichen. Ist die Abweichung zu groß, muss ein Rescheduling stattfinden. Die Bedingung dafür lautet folgendermaßen: (c) max 0, treal ni − tni > tr ∧ c < cmax [8] 12 2.7 Dynamic-Enactment Strategie Ist sie erfüllt, so bereitet der Algorithmus die Jobs für das Rescheduling vor. Dabei werden nur jene ni für eine Menge N Set ausgewählt, die der Algorithmus noch nicht abgearbeitet hat. Die Daten, deren Übertragung noch ausstehen werden ebenfalls berücksichtigt und in der Menge DSet gespeichert. Nun wendet der Algorithmus ein spezielles Verfahren namens RI-Rescheduling an, mit N Set und DSet als Übergabeparametern, welches die verbleibenden Jobs wieder möglichst effizient auf die Ressourcen verteilt. Die Variable c wird um 1 (c) erhöht und tr wird neu berechnet. t(c) r = tW f · fr · cX max (1/k)[8] k=1 Abschließend wird noch getestet ob der Workflow fertig abgearbeitet ist. Beispiel für den Start des Reschedulings (c) Um die Berechnungen von tr und td zu Verstehen soll nun ein kleines Beispiel (c) eingeführt werden. Konkret soll sich tr nach jedem Rescheduling dem Wert td - tW f mit immer enger werdenden Abständen nähern und dabei die Zeit, um welche sich die Ausführung des Workflows verzögert hat, representieren. Beginnt man mit tW f = 1000, fr = 0.2 und cmax = 4 so ergeben sich für (1) (c) tr = 200 und für td = 1407. Der Wert von tr nähert sich schrittweise dem von 407, der maximalen Verzögerung welche garantiert dass eine Gesamtlaufzeit (2) (3) von 1407 beibehalten wird. Die konkreten Werte wären somit tr = 300, tr = (4) 366, tr = 407. Zu beachten ist hier, dass sich der Workflow nach dem ersten Rescheduling um mindestens 200 verzögert hat, sonst würde kein Rescheduling erfolgen, siehe Gleichung oben. Bei c-fachem Rescheduling hat sich der Workflow (c) somit mindestens um den Faktor tr verzögert. Dies ist der Grund warum (c) (c ) man, je näher tr , dem Wert von tr max rückt, in immer kürzer werdenden Intervallen ein Rescheduling durchführt damit die Soft Deadline von tW f + (c ) tr max eingehalten wird. 2.7.3 RI-Rescheduling Vom vorhergehenden Kapitel ist bekannt, dass dieser Algorithmus aufgerufen wird wenn erkannt worden ist, dass sich die Ausführungszeit des Workflows bereits ungünstig verzögert hat. Die Übergabeparameter sind der Workflow W mit den im Dynamic-Enactment Verfahren berechneten Jobs N Set und den Daten DSet, den Ressourcen Q, repmax und c. Dieses Verfahren hat zum Ziel, dem bereits entstandenen Trend zur Verzögerung des Workflows entgegen zu wirken. Dies soll dadurch erfolgen, dass man die Anzahl an Replikas von den verbleibenden Knoten jeweils erhöht. Somit sollen Zeitverzögernde, erneute Sendungen von Jobs vermieden werden. Um dies zu erreichen, wird repi für jeden verbleibenden Job neu berechnet. p repi = b c RIi · repmax c[8] 13 2 Theoretische Grundlagen √ Durch c RIi wird RIi um den Faktor c erhöht, da sich RIi zwischen 0 und 1 befindet. Dies hat zur Folge, dass sich auch repi für ni erhöht. Somit kann man später auch Replikas für Knoten mit einem niedrigen initialen RI bilden. Anschließend wird RV wieder mit den neuen Werten von ri initialisiert und das HEFT-Replication Verfahren aufgerufen. 14 3 Implementierung Der Simulator ist in Java 1.6 implementiert. Dabei wurde das bereits existierende Programm GroudSim verwendet, welches mehrere Studenten in diversen Bachelorprojekten entwickelt haben. 3.1 Ziele des Simulators und Programmeingabeinformationen Der Simulator besteht genau genommen aus zwei Teilen. Der erste Teil wird als Workflow Generator bezeichnet und ist für die Erzeugung des Graphen verantwortlich. Die Anzahl der unterstützten Graphen in diesem Generator beschränkt sich auf jene, welche in den theoretischen Grundlagen besprochen worden sind. Man kann allerdings problemlos weitere Graphgeneratoren hinzufügen die andere Workflows erzeugen, solange man die Schnittstellen korrekt implementiert. Bei der Generierung des Workflows wird ebenfalls die Problemgröße (in Anzahl an Instruktionen gemessen) für jeden Knoten beziehungsweise Job berechnet. Der zweite Teil des Simulators besteht aus den Scheduling Algorithmen und dem Dynamic Enactment. Es müssen Graphen erzeugt, effizient mittels den Algorithmen auf Ressourcen verteilt, und anschließend mithilfe des Enactment Verfahrens ausgeführt werden können. Das Programm verwendet drei Dateien, diese gibt der Benutzer als Parameter beim Start der Ausführung an. Die erste Datei beinhaltet Informationen über die Erstellung der Workflows und deren Beschaffenheit im CSV Format. Je nach Graphtypus gibt es unterschiedliche Parameter mit verschiedenen Funktionsweisen, die bei der Erzeugung eine große Rolle spielen. Die zweite Datei, ebenfalls im CSV Format, ist für die Erstellung der Grids relevant. Im Programm werden nur Grids als mögliche Ressourcen verwendet. In dieser Datei werden somit die Anzahl an Grids, deren Leistungsstärke und Fehlerverhalten angegeben. Die dritte Datei beinhaltet Konfigurationen in Form von Schlüssel-Werte Paaren, welche den programminternen Ablauf steuern. Als Beispiel kann die Anzahl an maximalen Rescheduling Operationen angegeben werden, oder ein Wert welcher bestimmt ob das Programm Fehlerverhalten realisieren soll. 3.2 Verwendung und Konzepte von GroudSim Um einen Simulator zu realisieren, der auf diversen Ressourcen arbeitet, braucht es ein System welches solche Ressourcen erzeugen und verwalten kann. Der Simulator GroudSim ist in der Lage Ressourcen in Form von Grids und Clouds zu simulieren. Dieses hier beschriebene Programm verwendet die Funktionalität des Simulators, weshalb es wichtig ist kurz den Zusammenhang zwischen beiden 15 3 Implementierung Arbeiten zu erwähnen. Wichtig ist hier die Erzeugung von Grids, welche in meinem Simulator die Ressourcen repräsentieren. In GroudSim kann man Objekte vom Typ GridSite erstellen, wobei jede GridSite eine bestimmte Anzahl an Prozessoren mit einer entsprechenden Rechenleistung, die in der Abarbeitung von Millionen Instruktionen pro Sekunde angegeben wird, besitzt. Zudem kann man der GridSite einen Fehlergenerator zuweisen um die Dauer des Fehlers, die Anzahl an Prozessoren und die Zeit bis zum nächsten Fehler zu bestimmen. Eine Instanz der Klasse GridSite kann lediglich abzuarbeitende Jobs entgegennehmen und in einer Warteschlange für die zukünftige Ausführung speichern. Die Jobs werden aus der Klasse GroudJob gebildet. Sobald eine CPU nicht mehr von einem Job besetzt ist, weist die GridSite dem Prozessor den nächsten Job zu, insofern die Warteschlange nicht leer ist. Zuerst wird ein GridUser angelegt, welcher bestimmte GridSite Instanzen erstellen und Jobs an den Simulator schicken kann. Ist der User im System registriert, wird mittels Ereignissklassen kommuniziert. GroudSim generiert Informationen, wie das erfolgreiche Senden eines Jobs an einen Prozessor, die Fertigstellung der Jobs und die Fehlschläge bei der Abarbeitung. Diese Informationen werden sofort nach dem Auftreten in Events verpackt, wobei die aktuelle Simulationszeit ebenfalls mitprotokolliert wird. Beim Dynamic-Enactment Verfahren wird dann auf diese Ereignisse entsprechend reagiert. Die entscheidenden Klassen sind hier das JobSubmittedEventType, wenn ein Job an eine GridSite gesendet worden ist, das JobQueuedEventType, wenn sich ein Job in der Abarbeitungsschlange der GridSite befindet, das JobActivatedEventType, wenn ein Job aktiviert worden ist und das JobFailedEventType, wenn ein Job fehlschlägt. Um GroudJob Instanzen während der Ausführung in GroudSim zu beenden, erstellt man ein JobCancelEventType und übergibt es GroudSim. Ein Objekt vom Typ JobCancelDoneEventType signalisiert das erfolgreiche Beenden eines Jobs. Bei der Abarbeitung des jeweiligen Ereignisses ruft der Simulator die Implementierung der zugehörigen Methode im Objekt von GridUser auf. Der GridUser leitet das Ereignis weiter, indem er Methoden in der Fassadenklasse aufruft. Schlussendlich wird die Klasse, welche das Enactment Verfahren implementiert durch die Fassadenklasse benachrichtigt. 16 3.3 Programmaufbau 3.3 Programmaufbau Der Aufbau des Programms lässt sich generell in drei Komponenten einteilen, welche aufeinander angewiesen sind. Abbildung 3.1: Die Komponenten Den Workflow Generator, die Scheduling Komponente, mitsamt den Algorithmen für die Verteilung der Jobs auf die Ressourcen und den eigentlichen Simulator, die Ausführungskomponente. Letztere ist für das Enactment und die Simulation des Workflows unter Zuhilfenahme von GroudSim zuständig. 3.3.1 Workflow Generator In dieser Komponente finden sich Klassen für die Erstellung der unterstützten Workflows. Die Erzeugung des jeweiligen Workflows wurde mithilfe der Fabrikmethode realisiert. Es gibt eine Schnittstelle namens GraphGenerator, von welcher die konkreten Workflowgeneratoren erben. Die Schnittstelle definiert eine Methode namens public DAG generateDAG(), mit der die Graphenerstellung erfolgt. Eine Klasse GraphGeneratorFactory liefert den entsprechenden Generator, basierend auf den eingelesenen Informationen über den Typ des Graphen, zurück. Die Methode generateDAG() wird von der Klasse FileInputHandler aufgerufen. Dieselbe Klasse liest die graphspezifischen Informationen von der CSV Datei aus. Die Datei wird von InputTypeParser auf Korrektheit überprüft bevor sie dem FileInputHandler übergeben wird. Als Ergebnis wird eine Liste mit Workflows zurückgegeben. Der Workflow selbst, ist in der Klasse DAG abgebildet. Hier befinden sich Referenzen auf Wurzel- und Endknoten. Knoten werden mithilfe der Klasse GraphNode abgebildet, zwischen ihnen befinden sich Kanten welche die Klasse GraphEdge definiert. Ein Objekt vom Typ GraphNode beinhaltet alle wichtigen Informationen, wie Referenzen auf Replikas, Nachfolge- und Vorgängerknoten, die Anzahl an Ausführungen in GroudSim und die Problemgröße. . . . 17 3 Implementierung Graphspezifische Informationen in der Eingabedatei Um zu verstehen wie ein Graph erzeugt werden kann, muss man seine Beschaffenheit analysieren. Aufgrund der Struktur des Workflowtyps kann man dann diverse Parameter bestimmten und eine passende Eingabedatei dafür konstruieren. Bei jedem Workflow müssen der exakte Name, Werte für resmax und repmax und ein String angegeben werden, der entscheidet welchen Scheduling Algorithmus das Enactment Verfahren verwendet. Dieser muss entweder HEFT, RI oder ReplicateAll lauten. Diese Werte gibt der Aufrufende jeweils am Ende der CSV Datei an. Ein weiterer wichtiger Punkt ist nicht nur die Generierung des Workflows, sondern auch die gleichzeitige Bestimmung der Problemgöße p eines jeden Knotens. Dabei hängen die spezifischen Grapheigenschaften eng mit der Ermittlung der Problemgröße zusammen. Es werden pro Graph jeweils 2 Parameter α und β vom Typ Double angegeben, mithilfe derer eine Klasse ProblemSizeGenerator die Anzahl an Instruktionen pro Knoten berechnet. Dabei ist α ein Faktor, welcher die Problemgröße des jeweiligen Knoten entweder vergrößert oder verkleinert. In der Property Datei kann man die vordefinierte Anzahl an Instruktionen instr vom Typ Double, mit welcher α multipliziert wird, definieren. Es sollte aber nicht jeder Knoten dieselbe Problemgröße haben, weswegen der Wert von β entscheidend ist. Der Parameter, der zwischen 0 und 1 liegen muss, gibt das Verhältnis der Problemgröße zwischen einzelnen Knoten einer Ebene und parallelen Knoten auf einer anderen Ebene an. Konkret ergibt sich p = α · instr · β für Einzelknoten und p = α · instr · (1 − β) falls der Knoten mehrere Nachbarn in einer Ebene besitzt. Bei den Graphen Povray und LDPMC werden Dummyknoten angefügt. Diese Knoten haben eine spezifische, in der Property Datei vordefinierte Größe. Die beschriebene Art der Kostenberechnung ist ein Standard, der allerdings nicht für jeden Graphtypus gleich ist. Im Folgenden sind die graphspezifischen Informationen für die Eingabedatei beschrieben. Zur Veranschaulichung sind pro Graph Beispielgrafiken angegeben. Diese sollen das Ergebnis der Problemgrößenberechnung darstellen. Wien2k Die Beschaffenheit des Wien2k Graphen lässt variable Dimensionen bezüglich der Anzahl an Iterationen i und der parallelen Knoten pnodes in den Ebenen 2 und 4 jeder Iteration zu. Die ersten beiden Parameter, welche für die Konstruktion eines solchen Graphen zuständig sind, bestimmen somit die Werte für i und pnodes. Zur Berechnung der Instruktionsanzahl einzelner Knoten müssen zusätzlich vier Werte angegeben werden. Zwei Gewichtungen e2, e4 sowie α und β. Liegt der Knoten in der zweiten Ebene, so ergibt sich die Problemgröße p durch: p = α · instr · e2 · (1 − β) Die Anzahl an Instruktionen in der dritten Ebene einer Iteration wird durch: p = α · instr · e4 · (1 − β) 18 3.3 Programmaufbau berechnet. Die Idee dahinter ist, dass die Ebenen bezüglich ihrer Problemgröße unterschiedlich stark gewichtet werden können. Meist benötigt man beim Wien2k Graph ein Verhältnis von 10 : 1 zwischen e2 und e4. Die Berechnung der Kosten für alleinstehende Knoten einer Ebene verändert sich nicht. Abbildung 3.2: Wien2k mit instr = 100, α = 2.0, e2 = 10, e4 = 1, β = 0.8 In jeder Ebene des Graphen sind links von den Knoten die jeweiligen Problemgrößen angegeben die sich jeweils für alle Knoten der Ebene ergeben. Povray Zur Generierung wird hier, neben den Standardparametern ein Integer pnodes angegeben. Dieser bestimmt die Anzahl an Knoten in der zweiten Ebene. Die Berechnung der Problemgröße für die parallelen Knoten ergibt sich durch: p = α · instr Die Ermittlung der Instruktionsanzahl für den letzten Knoten erfolgt mithilfe von pnodes und α. Dadurch wächst p für diesen Knoten je mehr Vorgänger er besitzt. Zusätzlich wird das Ergebnis um den Faktor 0.8 etwas gedämpft. p = pnodes · α · 0.8 · instr Abbildung 3.3: Povray mit instr = 100, α = 2.0, β = 0.8, pnodes = 4 19 3 Implementierung Diese Grafik zeigt die Problemgröße, die sich aus den jeweiligen Werten für instr, α und β pro Knoten ergibt. Die Werte gelten für jeweils für alle Knoten in der zugehörigen Ebene. Balanced Der Erste Parameter, welcher nach dem Namen folgt, bestimmt hier die Gesamtanzahl t an Knoten. Die tatsächlich erzeugte Knotenzahl kann allerdings variieren und hängt von den weiteren Werten ab. Der zweite Parameter bestimmt die Anzahl an Ebenen k in einem Block mit parallelen Knoten. Die Anzahl an Blöcken b mit k Ebenen errechnet sich durch den nächsten Parameter, der Zahl der Knoten zwischen den Blöcken a. Somit ist b = a − 1, da zwischen jedem Block ein einzelner gemeinsamer Nachfolgeknoten liegt. Die Zahl an Nachbarknoten l in einer der b · k Ebenen ergibt sich durch l = (t − a)/(b · k). Die Errechnung der Problemgröße jedes Knotens wird Standardmäßig mit den Werten α und β durchgeführt. Abbildung 3.4: Balanced Graph mit instr = 100, α = 2.0, β = 0.8 Die Problemgrößen gelten für alle Knoten in der Ebene in der sie angegeben sind. LU Dem GraphGenerator für diesen Workflow übergibt man zur Konstruktion nur die Anzahl an Ebenen k. Falls k ungerade ist, so wird der Wert um 1 dekrementiert. Der GraphGenerator erzeugt den Workflow beginnend beim letzten Knoten, aufsteigend, in Richtung der Wurzel. Jede Ebene besitzt einen Index ei welcher die Höhe darstellt. Ist ei gerade, so generiert der Algorithmus eine Ebene mit parallelen Knoten. Die Zahl der parallelen Knoten ist jeweils um 1 größer als in der Ebene ei − 2. Ist ei ungerade, so wird nur ein einzelner Knoten erstellt. Die Kostenberechnung erfolgt nach dem Standardverfahren. 20 3.3 Programmaufbau Abbildung 3.5: LU Graph mit instr = 100, α = 2.0, β = 0.8 LDPMC Der erste Parameter nach dem Namen bestimmt die Gesamtanzahl t an Knoten ohne den Wurzelknoten. Der zweite Parameter bestimmt die Gesamtanzahl der Ebenen k. Somit ergibt sich die Größe pnodes der Knoten pro Ebene durch pnodes = t/k. Ab der zweiten Ebene wird per Pseudozufall bestimmt wieviele Vorgänger ein Knoten besitzt und um welche Knoten von der Ebene zuvor es sich dabei handelt. Die maximale Anzahl an Vorgängern spezifiziert dabei der dritte Parameter. Dieser Wert muss allerdings größer als 1 sein, da die minimale Anzahl an Vorgängern 1 ist. Um die Simulation mit den gleichen Zufallszahlen noch einmal ausführen zu können, muss ein spezieller Wert vom Typ Long dem Zufallsgenerator übergeben werden. Diese Zahl wird durch den vierten Parameter bestimmt. Die Anzahl an Instruktionen für den Wurzelknoten ergibt sich aus dem Standardverfahren, für die Ermittlung der Problemgröße pro Knoten in den weiteren Ebenen wird allerdings die Anzahl der Vorgänger predecessors des jeweiligen Knoten in Betracht gezogen. Somit ergibt sich für jeden Knoten in den Ebenen nach dem Wurzelknoten folgende Formel für p: p = α · instr · predecessors · (1 − β) Abbildung 3.6: LDPMC-Ausschnitt mit instr = 100, α = 2.0, β = 0.8 21 3 Implementierung Die in der Grafik dargestellten Werte neben den Knoten gelten jeweils nur für die Grün gefärbten Knoten und nicht pro Ebene. Beispiel für die Eingabedatei zur Workflow Generierung Als Beispiel ist der Inhalt einer CSV-Datei angegeben, aus der ein Wien2k Graph erzeugt werden kann. 1 Wien2k; 5; 4; 10; 1; 1.0; 0.65; 4; 4; RI; Der Erste Parameter stellt den korrekten Namen des Workflows dar damit der zugehörige Generator aufgerufen werden kann. In diesem Beispiel wird der Graph mit 5 Iterationen, welche jeweils 4 parallele Knoten in den Ebenen e2 und e4 der Iteration i besitzen, initialisiert. Dies spezifizieren die Werte 5 und 4 nach dem Namen. Die weiteren 2 Parameter geben das Verhältnis der Problemgröße zwischen den Knoten in e2 und e4 mit 10 zu 1 an. Der Wert α ist 1.0 und für β wird 0.65 angegeben. Die beiden darauf folgenden Parameter bestimmen resmax und repmax und RI bestimmt die Anwendung von Resubmission Impact im Enactment Verfahren für diesen Workflow. 3.4 Scheduling-Komponente Diese Komponente weist eine enge Kohäsion mit der Ausführungskomponente auf. Hier befinden sich die Implementierungen von HEFT, HEFT-Replication, Replicate All und Resubmission-Impact. Die Schnittstelle dieser Komponente bildet die Klasse SchedulingExecutorFactory. Eine Klasse welche Objekte vom Typ Executor zurückgibt. Das Executor Interface definiert die Methode public Schedule executeHEFT(). Jeder Executor implementiert die Ausführung eines bestimmten Scheduling Verfahrens. Hier ist ebenfalls wieder das Entwurfsmuster der Fabrikmethode verwendet worden. Im Folgenden werden die Implementierungen der Scheduling Algorithmen beschrieben, für welche jeweils ein Executor bereitgestellt ist. 3.4.1 HEFT-Implementierung Eingabeparameter und Bedingungen Die Klasse HEFT realisiert den gleichnamigen Algorithmus und benötigt einen Workflow vom Typ DAG als Eingabe. Zusätzlich zu diesem wird noch eine Liste, welche GridRepresentant Instanzen beinhaltet, übergeben. Diese Klasse repräsentiert die Gridressourcen, die von GroudSim bereitgestellt werden, allerdings enthält sie einige Erweiterungen zu der ursprünglichen GridSite, die in GroudSim enthalten ist. Der HEFT Algorithmus verlangt konkrete Informationen darüber, wieviele Jobs auf einer CPU ausgeführt werden. Zusätzlich müssen die fixen Startzeitpunkte und Endzeitpunkte der Jobs auf der CPU bekannt sein. Da eine GridSite lediglich eine Warteschlange besitzt, welche 22 3.4 Scheduling-Komponente man ohnehin nur zur Laufzeit des Workflows benötigt, muss diese neue Klasse eingeführt werden. Eine Instanz von GridRepresentant enthält ein Array mit Objekten vom Typ CPUTimeTable, welche die Prozessoren repräsentieren. Diese Objekte beinhalten, nach einer korrekten Ausführung von HEFT, die Knoten in Form von GraphNode Instanzen mitsamt ihren Start- und Endzeitpunkten, welche dem jeweiligen Prozessor zugeteilt sind. Algorithmus Der Algorithmus verhält sich so wie es in den theoretischen Grundlagen beschrieben worden ist, allerdings mit kleinen Ausnahmen welche im Detail beschrieben werden. Die Instanz von HEFT erzeugt eine Liste der Knoten, basierend auf den jeweiligen Werten von ranku . Diese wird anschließend abgearbeitet indem pro Knoten in einer Schleife über alle GridRepresentat Objekte und ihre Prozessoren (CPUTimeTable-Instanzen) iteriert wird, um die minimale Startzeit des jeweiligen Knotens zu ermitteln. Allerdings gibt es hier eine kleine Änderung. Es ist eine Standarddauer für die Übertragung der Daten in der Klasse EngineConfig definiert. Die Werte cmi,j und ci,j sind somit fix vorgegeben. Die Berechnung von EST wird von HEFT und den CPUTimeTable Instanzen geteilt durchgeführt. Die Klasse HEFT übernimmt die Bestimmung des minimalen Startzeitpunktes sti für den Knoten ni , zu diesem alle Vorgänger beendet und die Daten übertragen worden sind: sti = max nm pred(ni ) (AF T (nm ) + cm,i ) Die Klasse CPUTimeTable bestimmt dann die frühestmögliche Laufzeit aufgrund dem Zeitanteil, den die bereits zugeteilten Knoten konsumieren. EST (ni , pj ) = max(pavailable[j], sti ) In diesem Fall ist pj eine Instanz von CPUTimeTable, welche diesen Wert berechnet. Zur Bestimmung von pavailable[j], wird in der Klasse CPUTimeTable über alle zugeteilten Knoten nrk iteriert und versucht einen Startzeitpunkt AF T (nrk ) zu finden, welcher die Bedingung AF T (nrk ) + AST (nrk+1 ) >= sti + durationi erfüllt. Wobei durationi die zufor berechnete Ausführungsdauer eines Knoten auf dem jeweiligen Prozessor in Sekunden angibt. Sei powerpj die Anzahl an MIPS für pj und instructionsi die Problemgröße für ni so ergibt sich der Wert für durationi wie folgt: durationi = instructionsi /powerpj Nach der Berechnung ergibt sich pavailable[j] = AF T (nrk ). Es wird spätestens beim letzten Knoten nrk , der pj zugeteilt worden ist, ein AF T -Wert ermittelt, welcher die oben angeführte Bedingung erfüllt. Die Berechnung von AF T und AST erfolgt nach der Ermittlung von EST (ni , pj ) für alle pj . Die Werte für AF T und AST , sowie den Identifikationsstring des GridRepresentantObjektes welches EST minimiert, speichert der Algorithmus im Knoten, also 23 3 Implementierung im Objekt der Klasse GraphNode. Der dem Knoten zugeteilte Prozessor speichert sich eine Referenz auf das zugehörige GraphNode Objekt, sowie dessen fixe Start- und Endzeitpunkte. Diese Informationen werden benötigt, um den nächsten freien Zeitschlitz pavailable[j] zu berechnen. Zusätzlich zu diesen Werten werden noch die Gesamtkosten cost berechnet die der Workflow insgesamt verbrauchen würde. Dabei wird die Anzahl der Instruktionen eines Knoten instructionsi mit den Kosten pro Instruktion costsP erInstr für die zugeordnete GridSite multipliziert. Der Wert cost ergibt sich dann aus der Summe der Kosten pro Knoten. Daraufhin berechnet der Algorithmus noch den kritischen Pfad makeSpan, also die erwartete Gesamtlaufzeit des Algorithmus. Dabei wird einfach der maximale AF T Wert ermittelt. Der Rückgabewert bei einem erfolgreichen Durchlauf des Algorithmus, ist ein Objekt der Klasse Schedule, welches die Liste der GridRepresentant Instanzen, den Workflow, den makeSpan und die Gesamtkosten cost beinhaltet. 3.4.2 HEFT-Replication-Implementierung ohne erneutes Scheduling Der HEFT-Replication Algorithmus ist in einer Klasse HEFTReplication implementiert. Es gibt zwei verschiedene Versionen. Die Erste kommt beim ResubmissionImpact Verfahren zum Einsatz, die Zweite beim Erneuten Scheduling während des Enactment Verfahrens. Der HEFT-Replication Algorithmus basiert auf dem normalen HEFT Verfahren, daher ist die Implementierung analog zu der von HEFT und befindet sich in der Methode mapNodeOnResources(). Sind allerdings AF T und AST für einen Knoten in der Liste erfolgreich berechnet, so ruft der Algorithmus zusätzlich die Instanzmethode checkAndReplicateParents() der Klasse GraphNode auf. Diese Überprüft alle Vorgänger, ob ihre Replikas erzeugt und nun den Ressourcen zugeteilt werden können. Falls der Algorithmus für eine Instanz von GraphNode Replikas erzeugen muss, so ruft HEFT Replication auf diesem Objekt die Methode generateReplicas() auf. Es werden repi Replikas erzeugt, wobei jeder Replikaknoten, welcher als solcher gekennzeichnet ist, eine Referenz auf seinen Erzeuger erhält. Nach der Generierung der Replikas, ruft der Algorithmus die Methode mapNodeOnResources() für jeden Replika auf. Man kann dem Algorithmus von außen mitteilen, ob jeweils der erste Replika eines Knotens auf eine andere Gridressource zugeteilt werden soll als der Erzeuger, selbst wenn diese Ressource die Laufzeit des Workflows minimieren würde. Die Variante des Algorithmus, welche zusätzliche Faktoren besitzt, ist im Kapitel über das Dynamic Enactment Verfahren beschrieben. 3.4.3 Resubmission-Impact Implementierung Die Implementierung dieses Algorithmus ist in der Klasse RIHeuristic definiert. Die Methode resubmissionImpact() realisiert den Ablauf des Verfahrens. Zunächst berechnet HEFT den kritischen Pfad des unmodifizierten Workflows. Danach ruft der Algorithmus die Methode evaluateDifferences() auf, welche rekursiv durch den Graphen iteriert. Dabei erfolgt bei jeder Rekursion die Berechnung von instructions0i . Diesen Wert speichert Resubmission Impact 24 3.4 Scheduling-Komponente im jeweiligen Objekt der Klasse GraphNode statt dem alten Wert instructionsi , bevor ein erneuter Aufruf von HEFT erfolgt. Die jeweiligen Differenzen di werden in einer Liste gespeichert. Die Berechnung von RIi und ri erfolgt nach dem beschriebenen Verfahren. Anschließend speichert Resubmission Impact diese Werte ebenfalls in dem zugehörigen Objekt der Klasse GraphNode. Zuletzt erfolgt der Aufruf von HEFTReplication, wobei die Klasse GraphNode innerhalb des Algorithmus soviele Replikas erzeugt, wie die zuvor gespeicherte Zahl ri vorgibt. Es wird ein Objekt der Klasse Schedule zurückgegeben. 3.4.4 Parallelisierung von Resubmission-Impact Die Berechnung der RIi Werte während des Resubmission Impact Verfahrens ist sehr zeitaufwendig, da für jeden Knoten ni HEFT ausgeführt werden muss. Die Länge der Ausführungszeit ist somit proportional zur Anzahl der Knoten. Die Schleife zur Berechnung der Variablen instructions0i und des neuen kritischen Pfades hef ti kann man allerdings sehr gut parallelisieren. Die Berechnungen von instructions0i und di erfolgen für jeden Knoten getrennt. Um die Ausführung des Resubmission Impact Verfahrens zu parallelisieren, muss man allerdings den HEFT Algorithmus für Nebenläufigkeit optimieren. Die ursprüngliche Variante in der Klasse HEFT greift lesend und schreibend auf die Instanzen von GraphNode zu. Der Algorithmus speichert nach jeder Abarbeitung eines Knotens, dessen, für diesen Durchlauf von HEFT, ermittelten AST und AF T Werte in den jeweiligen Instanzen. Es würde somit zu gravierenden Inkonsistenzen kommen, wenn mehrere Versionen von HEFT parallel auf AST und AF T , lesend sowie schreibend, zugreifen könnten. Ein weiteres Problem stellt der Zugriff auf die GridRepresentant Objekte dar, welcher ebenfalls schreibend und lesend erfolgt, da der bestehende Algorithmus in den CPUTimeTable-Objekten die Referenzen für die Knoten speichert. Parallelisierung von HEFT Die Modifizierung des ursprünglichen Algorithmus in HEFT, wurde in der Klasse ParallelHEFT vorgenommen. Die Felder ranku , AST und AF T , dürfen in den GraphNode-Objekten nicht geschrieben und gelesen werden wenn mehrere HEFT Algorithmen gleichzeitig auf den Workflow zugreifen. Es bieten sich zwei Lösungen an. Die Erste wäre den gesamten Workflow für jede nebenläufige Durchführung von HEFT zu kopieren. Dies würde allerdings die Speicherkapazitäten der Rechner zu schnell auslasten. Diese Implementierung verwendet deshalb die zweite Variante, welche nur die Felder ranku , AST und AF T kopiert und in einem Array speichert. Das Speichern der Identifikationsnummer der GridSite, welche EST für einen bestimmten Knoten minimiert, lagert der Algorithmus ebenfalls aus. Die Referenzen werden nun in einer Hashtabelle abgelegt und nicht mehr in den jeweiligen Instanzen. Die CPUTimeTable-Objekte pro GridRepresentant müssen allerdings für jeden Thread kopiert werden, weshalb es in der Klasse GridRepresentant eine Methode getThreadLocalCPUTimeTables() gibt. Dieser Methode übergibt der ausführende Thread eine eindeutige Identifi- 25 3 Implementierung kationsnummer, damit man die zugehörigen Ressourcen für die jeweilige Version von HEFT identifizieren kann. Es gibt pro Ausführung von HEFT einen eigenen Thread, der eine eindeutige Identifikationsnummer besitzt. Das Ergebnis ist, dass ParallelHEFT nur mehr die Gesamtlaufzeit makeSpan verpackt in einem Schedule Objekt an den Aufrufenden ausliefert. Aufteilung der Arbeit durch RIWorker Um die Schleife im Resubmission Impact Algorithmus, in welcher die Werte für di errechnet werden, zu parallelisieren, muss man die Ausführung von ParallelHEFT in Threads verlagern. Dafür zuständig ist die Klasse RIWorker welche von Runnable erbt. Jede Instanz dieser Klasse wird von ParallelRIHeuristic erzeugt, in welcher die parallele Version von Resubmission Impact implementiert ist. Zunächst erzeugt der Algorithmus v + 1 Instanzen, wobei v die Anzahl an Knoten darstellt. Anschließend speichert ParallelRIHeuristic die Objekte in einem ThreadPool mit fixer Größe. Die Anzahl der Threads welche parallel ausgeführt werden, kann in der Klasse EngineConfig spezifiziert werden. Jede Instanz von RIWorker erhält die Ressourcen, den Workflow und einen Knoten von welchem der RIi Wert bestimmt werden soll. Diese Objekte rufen nun den parallelen HEFT Algorithmus auf und speichern den kritischen Pfad hef ti . Will man di schon in den jeweiligen RIWorker Instanzen berechnen, so benötigt man den Wert von hef t. Allerdings würde es, wenn der erste Aufruf von HEFT nicht parallelisiert wäre um hef t zu berechnen, etwas an Performance kosten. Aufgrund von Amdahls Gesetz ist der Geschwindikeitsgewinn einer Applikation umso höher, je geringer der serielle Programmanteil ist. Es werden somit die di Werte nicht in den Threads gespeichert, sondern die jeweiligen hef ti Werte einer Klasse RICallback abgelegt. Dies ist auch der Grund, warum man v + 1 Threads erzeugt. Ein RIWorker berechnet den Ursprünglichen hef t Wert, ohne Erhöhung der Instruktionszahl des Knoten. Die Instanz welche den parallelen Resubmission Impact Algorithmus repräsentiert, ruft nachdem die Threads abgearbeitet werden die Methode getDifferences() im Objekt von RICallback auf. Dabei wird solange gewartet bis alle Threads ihre Ergebnisse eingetragen haben. Die Berechnung der jeweiligen di erfolgt in der Klasse RICallback, woraufhin schließlich eine Liste mit den jeweiligen Werten an ParallelRIHeuristic zurückgegeben wird. Ab hier unterscheidet sich die nebenläufige Variante von Resubmission Impact nicht mehr von der seriellen. RIi und repi Werte können nun nach dem Standardverfahren berechnet werden. 3.5 Die Ausführungskomponente Dieser Teil des Programms beinhaltet den Enactmet Algorithmus, sowie Klassen welche auf GroudSim zugreifen. Die Komponente unterstützt die Alloziierung von Ressourcen, Jobs können an die angelegten Ressourcen gesendet werden und es wird entsprechend auf Ereignisse innerhalb von GroudSim reagiert. Somit kann man diese Komponente in zwei Teile zerlegen. Der Erste Teil ist für den 26 3.5 Die Ausführungskomponente Enactment Algorithmus zuständig, der zweite Teil für die Kommunikation mit GroudSim. 3.5.1 Schnittstelle zu GroudSim Die Klassen, welche von GroudSim bereitgestellt werden, sollten möglichst stark von dem Workflowsimulator abgekapselt sein. Diese Vorgehensweise ermöglicht eine geringe Kopplung zwischen beiden Programmen. Es gibt eine Fassadenklasse namens GroudSimFacade, welche die Schnittstelle implementiert. Nach außen werden alle Methoden angeboten, die für die Kommunikation mit GroudSim nötig sind. Die Fassade gibt nur Objekte, die nicht in GroudSim vorkommen zurück, wie z.B. GridRepresentant Instanzen. Jegliche Klassen die mit GroudSim kommunizieren, werden von der Fassadenklasse verwendet. Die direkte Interaktion mit GroudSim findet in der Klasse GridUser statt. Hier werden die GridSite Insanzen alloziiert und Jobs gesendet. Des Weiteren reagiert diese Klasse auf Ereignisse von GroudSim, wie z.B. JobActivatedEventTypeEvents, indem entsprechende Methoden, welche der GridUser implementiert, von GroudSim aufgerufen werden. Die Fassadenklasse bildet bei jedem Sendevorgang GraphNode Instanzen auf GroudJob Objekte ab. Tritt dann ein entsprechendes Event auf, das einen GroudJob beinhaltet, so gibt die Schnittstelle nach außen nur die Instanz des zugehörigen GraphNode zurück. Die Schnittstelle ist für die Weiterleitung von Events an die DynamicEnactmentEngine verantwortlich, sowie für die Generierung von neuen Events durch die Klasse Submitter und DynamicEnactmentEngine. 3.5.2 Implementierung des Enactment Verfahrens Das Kernstück des Workflowsimulators befindet sich in der Klasse DynamicEnactmentEngine. Diese beinhaltet eine Instanzmethode namens simulate(). Der Aufruf dieser Methode markiert den Beginn der Simulation. Zuerst überprüft der Algorithmus welches Verfahren angewandt werden soll. Man kann den Scheduling Algorithmus für das initiale Schedule mithilfe des letzten Parameters in der CSV Datei zur Workflow-Generierung bestimmen. Implementiert sind Versionen für HEFT, Resubmission Impact und Replicate All. Die ursprüngliche Version, welche auch in den theoretischen Grundlagen besprochen worden ist, beinhaltet Resubmission Impact. Das Rescheduling Verfahren kommt nur bei der Variante mit Resubmission Impact zum Einsatz. Enactment mittels Resubmission Impact Die Implementierung dieses Algorithmus ist über diverse Methoden verteilt, da während der Ausführung die Kontrolle an GroudSim abgegeben wird. Die (c) Werte für tr und td ergeben sich aus dem in den Grundlagen beschriebenen Verfahren, nachdem der Algorithmus für die parallele Version von Resubmission Impact aufgerufen worden ist. In der Instanz von DAG, dem aktuellen Workflow, ruft Dynamic Enactment die Methode getFirstNode() auf, welche den Wurzelknoten zurückgibt. Nun wird dieser mitsamt seinen Replikas für die Ausführung an GroudSim übermittelt. Sobald der zugehörige GroudJob seinen 27 3 Implementierung Status wechselt, protokolliert das Programm dies mit und Enactment reagiert auf die Veränderung. Zudem wird protokolliert wieviele Kosten der jeweilige Knoten verursacht hat und wie hoch die Gesamtkosten für die Ausführung des Worklfows bereits sind. Insgesamt gibt es 3 entscheidende Ereignisse in der Ausführung des Algorithmus. Job schlägt Fehl Bei diesem Ereignis wird zunächst abgefragt, ob es noch Replikas gibt die in GroudSim ausgeführt werden, d.h. deren Status im Moment auf running gesetzt ist. Ist dies der Fall, so wartet Enactment bis ein weiteres Ereignis eintrifft. Dies passiert indem die Kontrolle an GroudSim abgegeben wird. Finden viele Ausfälle von Ressourcen statt, so wird es vorkommen, dass ein Knoten welcher fehlschlägt keine Replikas mehr besitzt die noch simuliert werden. In diesem Fall fragt der Algorithmus ab ob der Knoten bereits resmax mal gesendet worden ist. Ist diese Bedingung erfüllt, so bricht die Simulation ab und der Status des Workflow wird auf failed gesetzt. Bevor die Simulation abbricht, müssen alle Jobs, welche sich noch in der Ausführungsphase befinden abgebrochen werden, um die entstandenen Kosten ermitteln zu können. Dies betrifft alle Knoten deren Status auf submitted, queued oder running gesetzt ist. Befindet sich die Simulation bereits in der Phase, in der alle verbleibenden Knoten abgebrochen worden sind, so sendet der Algorithmus den Knoten nicht mehr erneut. Ansonsten leitet der Algorithmus den Knoten an die Fassadenklasse GroudSimFacade für eine erneute Ausführung weiter. Job ist erfolgreich abgearbeitet Nach Autreten dieses Ereignisses überprüft Dynamic Enactment, ob die Simulation beendet werden muss. Ist dies der Fall so wird die Ausführung aller verbleibenden Knoten abgebrochen und der Algorithmus protokolliert die Kosten dieser Knoten. Ansonsten fragt der Algorithmus ab ob es noch Replikas gibt, welche sich in der Ausführungsphase befinden. Ist der Status eines Replikas submitted, running, oder queued, so muss dieser abgebrochen werden. Besonderes Augenmerk liegt hier auf Knoten, welche sich im Zustand running befinden. Es könnte passieren, dass zwei Knoten gleichzeitig mit der Ausführung fertig werden. Darum wird jeder Knoten der running als Zustand besitzt, in ein HashSet eingefügt. Falls nun der Algorithmus das JobFinishedEventType Ereignis für diesen Knoten abarbeitet, wird dieser Knoten nicht mehr berücksichtigt, er kann kein erneutes Scheduling des Workflows verursachen. Sind alle Replikas des ursprünglichen Knoten behandelt, so überprüft der Simulator ob es sich bei dem Knoten um den letzten im Graphen handelt. Falls dem so ist, beendet Dynamic Enactment den Workflow wieder sachgemäß. Ist dies nicht der Fall, so wird überprüft ob ein erneutes Scheduling des Workflows nötig ist. Für das erneute Zurodnen der Ressourcen verwendet der Algorithmus das RI-Rescheduling Verfahren. Im Anschluss entscheidet der Simulator darüber, welche Nachfolgeknoten nj succ(ni ) des abgearbeiteten Knotens ni gesendet werden können. Sind alle Vorgänger von nj bereits fertig mit der Ausführung, so sendet der Simulator nj mit all seinen Replikas an GroudSim. 28 3.5 Die Ausführungskomponente Job ist Abgebrochen Hier wird überprüft ob der Knoten der Letzte noch zu Beendende ist, falls die Ausführung des Workflows fehlgeschlagen ist und die Abarbeitung abgebrochen werden muss. Erneutes Scheduling mit Resubmission Impact Die Variante von RI-Rescheduling, die in den theoretischen Grundlagen beschrieben worden ist, kommt während Enactment zum Einsatz. Dazu wird in der zugehörigen Executor Instanz des Rescheduling-Algorithmus der Wert von repi für jedes GraphNode Objekt im Workflow entsprechend angepasst, bevor Heft-Replication ausgeführt wird. Die Version von HEFT Replication die man hier für das Rescheduling verwendet, befindet sich ebenfalls in der Klasse HEFTReplication, allerdings gibt Resubmission Impact im Konstruktor noch eine boolesche Variable an die aufzeigt, dass die Instanz von HEFTReplication für Reschedulingzwecke benutzt wird. Zusätzlich übergibt der Algorithmus beim Erzeugen des Objektes noch die aktuelle Simulationszeit time. Die Variante des Algorithmus unterscheidet sich dadurch, dass nur Knoten berücksichtigt werden, welche noch nicht an GroudSim gesendet worden sind. Die aktuelle Simulationszeit begrenzt den minimal möglichen EST -Wert, so dass sich folgende Formel ergibt: n o EST (ni , pj ) = max pavailable[j], max (AF T (nm ) + cm,i ), time nm pred(ni ) 3.5.3 Weitere Eingabedateien anhand von Beispielen erklärt Nachdem nun die Grundkonzepte des Programms dargestellt worden sind, können die Eigenschaften der 2 weiteren Eingabedateien anhand von Beispielen besprochen werden. 1 grid_site_x; 1; 2000; 10; 1; 100000000; 9.6; 0.1; 10.0; 0.6; 1.0; Dies ist ein Ausschnitt aus der Datei welche benötigt wird um Grids zu erstellen. Der Name der GridSite kann willkürlich gewählt werden. Danach folgt die Anzahl der Instanzen welche von dieser GridSite erzeugt werden sollen, in diesem Fall ist der Wert 1. Der nächste Parameter 2000 bezieht sich auf die Rechenleistung eines jeden Prozessors in Millionen Instruktionen pro Sekunde. Danach wird die Gesamtanzahl an Prozessoren mit 10 festgelegt. Das Ausführen von Instruktionen kostet Geld, somit muss man eine Zahl angeben die das Verhältnis zwischen Kosten und Instruktionen angibt. In diesem Fall ist das Verhältnis 1 : 1. Würde man hier den Parameter 10 übergeben, so wäre das Verhältnis 1 : 10. Die weiteren Parameter bestimmen die mittlere Zeit zwischen Fehlern (MTBF), die Fehlergröße, d.h. wieviele Prozessoren im Durchschnitt ausfallen und die Dauer eines Fehlers. Diese werden GroudSim übergeben. 1 2 3 threadNumber=2 printWorkflows=true maxRescheduleCycle=10 29 3 Implementierung 4 5 6 7 simulateFailures=true instructionNumForBottomDummyNode=1 instructionNumForTopDummyNode=1 instructionNumForNode=10000000 Dieser Ausschnitt ist der Property Datei entnommen. Man kann diverse Parameter konfigurieren, welche alle in der Klasse EngineConfig vordefiniert worden sind. Gibt man für einen Parameter keinen Wert an, so wird er mit dem Standardwert aus der Klasse EngineConfig initialisiert. Die Schlüssel Werte Paare werden nun, von oben nach unten gelesen, beschrieben. Arbeitet man auf einem Zweikern Prozessor, so empfiehlt es sich die Zahl mit 2 zu initialisieren. Der nächste Parameter bestimmt, ob der erzeugte Workflow auf der Kommandozeile ausgegeben werden soll. Der Faktor cmax wird mit 10 gleichgesetzt, zusätzlich gibt die Datei an, dass Fehler simuliert werden sollen. Müssen zusätzliche Knoten an der Wurzel, oder am Ende der Graphen angehängt werden, z.B. bei Povray und LDPMC, so geben die nächsten beiden Werte die Anzahl an Instruktionen in diesen Knoten vor. Die oben eingeführte Variable instr wird durch den letzten Parameter dargestellt. 30 4 Experimente 4.1 Performancetest Abbildung 4.1: Performancetest: Zeiten in Sekunden angegeben In dieser Tabelle ist ein Performancetest veranschaulicht. Das Experiment wurde auf dem Server kreusspitze.dps.uibk.ac.at durchgeführt. Der Rechner besitzt insgesamt 8 Dual Core AMD Opteron 880 Prozessoren mit jeweils einer Taktrate von 2400 Mhz. Es wurden Workflows vom Typ Wien2k mit einer fixen Breite von 250 Knoten verwendet. Als Beispielressourcen wurden 4 GridSites mit 10 Prozessoren und eine GridSite mit 5 Prozessoren verwendet. Die Anzahl an Iterationen in Wien2k wurde jeweils so angepasst dass sich Gesamtgrößen von 1006, 2012, 2515 und 3521 Knoten ergaben. Das Ergebnis zeigt eine deutliche Senkung der Laufzeit des Programms bei Verwendung von 6 Threads bei hoher Problemgröße. Wurde das Programm mit 6 parallel laufenden Threads abgearbeitet, so ergab sich eine Laufzeit von 115005 Millisekunden. Unter Verwendung von einem Thread für das Resubmissoin Impact Verfahren ergab sich eine Laufzeit von 592097 Millisekunden. Man kann erkennen, dass die Simulation mit 6 Threads nahezu um den Faktor 6 schneller ist als die Simulation mit nur einem Thread. 31 4 Experimente 4.2 Kosten- und Laufzeitenvergleich verschiedener Fehlerszenarien In diesem Experiment wurden insgesamt 60 Workflows auf 5 GridSites mit jeweils 15 Prozessoren gescheduled und abgearbeitet. Es wurden 30 Wien2k Graphen und 30 LDPMC Graphen simuliert. Die Ausführung wurde in 3 Fehlerszenarien mit verschiedenen Werten für die MTBF (mittlere Zeit zwischen Fehlern) unterteilt. Es sind jeweils Zeiten von 4 Stunden (unstabile Ressourcen), 18 Stunden (normale Ressourcen) und 5 Tagen (stabile Ressourcen) ausgewählt worden. In jedem Szenario sind jeweils 10 Workflows vom Typ Wien2k und 10 Workflows vom Typ LDPMC abgearbeitet worden. Dabei wurden pro Workflowtyp 5 Graphen einmal mittels Replicate All und einmal mittels Resubmission Impact an die Ressourcen gescheduled. Die 5 Workflows sollten jeweils unterschiedliche Gesamtlaufzeiten von ca. 7-8 Stunden bis zu 14 Tagen beanspruchen. Die Laufzeiten wurden hier gleichmäßig verteilt, so dass sich im Schnitt Abarbeitungszeiten von 7-8 Stunden, dreienhalb Tagen, sieben Tagen, elf Tagen und 14-15 Tagen ergaben. Je nach Fehlerszenario ergibt sich eine unterschiedlich starke Verzögerung der Laufzeiten. Die Anzahl der Knoten für die 5 LDPMC und Wien2k Workflows befindet sich im Bereich von 63 bis 2646. Die Einzelknoten in Wien2k erhalten eine höhere Problemgröße als die parallelen Knoten. Der Faktor β für die Berechnung der Problemgröße ist hier 0.65. Dieser Faktor wurde so gewählt, damit Resubmission Impact nicht allen Knoten in der zweiten und vierten Ebene eines Blockes einen hohen Wert für RIi zuteilt. Zugleich muss deshalb bei den LDPMC Graphen die Problemgröße aller Knoten vergrößert werden, damit sich bei gleicher Knotenanzahl eine ähnliche Laufzeit wie bei Wien2k ergibt. Dies ist deshalb von Nöten, da die Anzahl der Ebenen bei LDPMC geringer als bei Wien2k ist. Jeder Graph wurde so konstruiert, dass sich maximal 32 parallele Knoten in einer Ebene befinden. Deshalb ist die Ebenenanzahl bei einem LDPMC Graphen viel kürzer als bei Wien2k. Für die Variablen resmax und repmax wurde jeweils ein Wert von 4 vergeben. Ziel dieses Experiments ist das Aufzeigen der Tatsache, dass Replicate All hohe Kosten im Vergleich zu RI verursacht. Die Eigenschaft, dass RI auch mit knappen Ressourcen auskommt, sollte hier ebenfalls aufgezeigt werden, sowie die Auswirkung von Fehlern auf Kosten und Laufzeit der Workflows. 4.2.1 Erfolgsrate Die Erfolgsrate gibt an, wieviele Workflows erfolgreich abgearbeitet worden sind. Bei Wien2k ist die Erfolgsrate in allen Fehlerszenarien konstant 1. Bei LDMPC ergibt sich eine Erfolgsrate von 40% im Falle von unstabilen Ressourcen. Dies kann man dadurch erklären, dass die Problemgröße in den Knoten von LDPMC Workflows größer als in dem meisten Knoten von Wien2k Graphen ist. Dadurch steigt die Chance eines erneuten Fehlschlags des gleichen Jobs, da dieser länger auf einer GridSite abgearbeitet wird, die MTBF allerdings knapp bemessen ist. Der Simulator arbeitet die beiden kürzesten Workflows erfolgreich ab, allerdings schlägt die Abarbeitung der längeren Graphen fehl. Diese Beobachtung wirkt sich auf die weiteren Messungen aus. 32 4.2 Kosten- und Laufzeitenvergleich verschiedener Fehlerszenarien Abbildung 4.2: Angabe der Erfolgsrate in Prozent 4.2.2 Laufzeitenvergleich In den beiden folgenden Grafiken ist jeweils die Summe aller Laufzeiten pro Fehlerszenario und Schedulingverfahren dargestellt Abbildung 4.3: Angabe der Laufzeit in Stunden In dieser Grafik kann man einen Laufzeitunterschied zwischen den beiden Verfahren Resubmission Impact und Replicate All erkennen. Es soll hiermit gezeigt werden, dass RI mit wenig, aber ausreichend vielen Ressourcen eine bessere Laufzeit erzielt als Replicate All. Da die maximale Anzahl an Replikas 4 beträgt, erzeugt Replicate All sehr viele Replikas. Diese Knoten können nicht allesamt gleichzeitig abgearbeitet werden, weshalb sich eine stärkere Verzögerung ergibt. Man sieht hier ebenfalls, dass die Laufzeit unter verwendung von un- 33 4 Experimente stabilen Ressourcen bei beiden Verfahren stärker verzögert ist als bei stabilen Ressourcen. Abbildung 4.4: Angabe der Laufzeit in Stunden Bei LDPMC fällt der Laufzeitunterschied zwischen RI und Replicate All ähnlich auf wie bei Wien2k. Aufgrund der niedrigen Erfolgsrate bei stabilen Ressourcen ergibt sich in Summe auch eine niedrige Laufzeit. 4.2.3 Kostenvergleich Dargestellt werden hier die Kostensummen von allen 5 Workflows pro Fehlerszenario und Scheduling Verfahren. Die Kostensumme ist mit der Anzahl an Instruktionen, welche pro GridSite abgearbeitet wurden gleichzusetzen, da die Kosten für eine Instruktion genau 1 betragen. Multipliziert man die in der YAchse dargestellten Werte mit 109 so erhält man die Anzahl an Instruktionen. Paradox erscheint hier zunächst, dass die Kosten für die Ausführung auf stabilen Ressourcen höher sind als auf unstabilen Ressourcen. Das liegt allerdings unter anderem daran, dass viele Replikas erzeugt werden. Die GridSites sind nahezu gleich schnell in der Abarbeitung der Instruktionen, somit werden die Replikas eines Knotens fast zeitgleich mit der Abarbeitung fertig bevor sie abgebrochen werden. Die Kosten die sie verursacht haben werden somit sehr hoch sein. Bei vielen Fehlern minimiert sich dieser Effekt allerdings. Replikas werden zum Teil schon sehr früh durch Systemausfälle abgebrochen. Dies hat zur Folge, dass sie nur wenige Kosten verursachen. Auffallend ist zudem, dass Replicate All viel mehr Kosten verursacht als RI, da mehr Ressourcen von Replicate All verwendet werden. Replicate All ist somit teurer wie RI, zudem ist in diesen Szenarien die Ausführungszeit unter Verwendung des RI Verfahrens geringer als bei Replicate All. Das RI Verfahren ist hier klar im Vorteil. 34 4.2 Kosten- und Laufzeitenvergleich verschiedener Fehlerszenarien Abbildung 4.5: 109 Instruktionen Bei den LDPMC Workflows ergeben sich für unstabile Ressourcen sehr wenige Kosten. Man beachte, dass lediglich 2 Workflows abgearbeitet werden können, die anderen 3 scheitern bei ihrer Ausführung. Dieses Verhalten hat eine geringere Kostensumme zur Folge. Man kann auch hier den Unterschied zwischen RI und Replicate All sehr gut erkennen. Interessant ist zudem, dass zwischen stabilen und normalen Ressourcen nur wenig Unterschied bezüglich der Kostensumme erkennbar ist. Die Summen sind bei diesem Workflowtyp etwas höher als bei Wien2k. Abbildung 4.6: 109 Instruktionen 35 4 Experimente Zusätzlich zu der Kostensumme ist hier noch die Summe der verschwendeten Kosten angegeben. Als solche werden alle Instruktionen von abgebrochenen oder fehlgeschlagenen Knoten angesehen. Der bei den Kostensummen beschriebene Effekt macht sich auch in dieser Grafik bemerkbar. Die Kostenverschwendung ist bei unstabilen Resoucen geringer als bei normalen und stabilen Ressourcen. Dies kann wiederum dadurch erklärt werden, dass viele Knoten frühzeitig beendet werden und somit wenig Kosten verursachen können. Beachtlich ist ebenfalls, dass die verschwendeten Kosten fast mehr als die Hälfte der Gesamtkosten ausmachen wenn man hier mit den oberen Grafiken vergleicht. Die Verschwendung ist bei Replicate All wieder deutlich höher als bei RI. Abbildung 4.7: 109 Instruktionen 36 4.2 Kosten- und Laufzeitenvergleich verschiedener Fehlerszenarien Bei LDPMC ergibt sich ein ähnlicher Effekt wie bei Wien2k. Bei den unstabilen Ressourcen können nicht alle Workflows abgearbeitet werden. Aus diesem Grund ist die Kostensumme hier noch geringer als bei Wien2k. Analog wie bei Wien2k werden ca. die Hälfte der Gesamtkosten verschwendet. Abbildung 4.8: 109 Instruktionen Als Vergleich wurde hier noch die Summe der Kosten aller Knoten, welche durch Systemfehler fehlgeschlagen sind, angegeben. Man sieht, dass bei Verwendung von unstabilen Ressourcen sehr viele Kosten durch Systemfehler verursacht werden. Bei normalen und stabilen Ressourcen ist dies nicht der Fall. Man sollte nun sehr gut erkennen können, dass die meisten Kosten bei den stabilen und normalen Ressourcen durch abgebrochene Knoten verursacht worden sind und nicht durch Systemfehler. Die Unterschiede zwischen RI und Replicate All fallen wieder deutlich auf. 37 4 Experimente Abbildung 4.9: 107 Instruktionen Beim LDPMC Graphen ist ein sehr ähnliches Ergebnis wie beim Wien2k Workflow zu beobachten. Abbildung 4.10: 107 Instruktionen 38 5 Zusammenfassung Workflow Execution Simulation ist ein Programm das Workflows erzeugen, effizient an erstellte Ressourcen verteilen und anschließend abarbeiten kann. Es wurde versucht, auf Modularität zu achten. Es können ohne Einschränkungen mehrere Workflowgeneratoren hinzugefügt werden. Die Implementierung der Schedulingalgorithmen sieht keine besondere Struktur der Workflows vor. Der Graph sollte lediglich aus einem Start- und einem Endknoten bestehen. Die internen Eigenschaften des Programms können zudem sehr leicht mithilfe der Property Datei eingestellt werden. Die Konfigurationsfreiheit ist nicht darauf beschränkt, dass man den Programmcode manuell ändern muss. Alle wichtigen Features wie Threadanzahl, Basisinstruktionsanzahl von Knoten, Fehlersimulation,. . . können konfiguriert werden. Dies ist allerdings auch von Nöten, da keine grafische Benutzeroberfläche zur Verfügung gestellt wird. Ein wichtiger Punkt war auch die Performance des Programms. Wie im ersten Experiment gezeigt worden ist, kann das Scheduling eines Workflows sehr viel Zeit in Anspruch nehmen. Der kritische Punkt ist hierbei der Resubmission Impact Algorithmus, welcher HEFT für jeden Knoten aufruft. Es wurde versucht diesen Algorithmus effizient zu parallelisieren, damit man auf Multiprozessorarchitekturen einen Performancevorteil erzielen kann. Die Ausgabedateien verwenden das Format CSV. Dies erleichtert das Parsen von tausenden Zeilen, die bei der Abarbeitung von vielen Knoten entstehen. Die im Anschluss durchgeführten Experimente sollen einen Einblick geben, dass man mit diesem Tool verschiedenste Fehlerszenarien und Workflowtypen genau analysieren kann. 39 Literaturverzeichnis [1] I. Foster. What is the grid? a three point checklist. 2002. [2] Ian Foster, Carl Kesselman, Gene Tsudik, and Steven Tuecke. A security architecture for computational grids. In Proceedings of the 5th ACM conference on Computer and communications security, CCS ’98, pages 83–92, New York, NY, USA, 1998. ACM. [3] Christian Baun, Marcel Kunze, Jens Nimis, and Stefan Tai. Cloud Computing - Web-Based Dynamic IT Services. Springer, 2011. [4] Christian Baun, Marcel Kunze, and Viktor Mauch. The koala cloud manager: Cloud service management the easy way. In IEEE CLOUD, pages 744–745, 2011. [5] Peter Mell and Tim Grance. The NIST Definition of Cloud Computing. Technical report, July 2009. [6] Alexandra Olteanu and Andreea Marin. Generation and evaluation of scheduling dags: How to provide similar evaluation conditions. Computer Science Master Research, 1(1), 2011. [7] Haluk Topcuouglu, Salim Hariri, and Min-you Wu. Performance-effective and low-complexity task scheduling for heterogeneous computing. IEEE Trans. Parallel Distrib. Syst., 13(3):260–274, March 2002. [8] Kassian Plankensteiner and Radu Prodan. Meeting soft deadlines in scientific workflows using resubmission impact. IEEE Trans. Parallel Distrib. Syst., 23(5):890–901, 2012. 40