• Keine Ergebnisse gefunden

5 Performance-Tests

5.5 Tests mit Criterion

5.5.1 Map-Tests Teil 2

Um ein wenig Gefühl für das Framework zu bekommen, haben wir versucht die Ergebnisse aus dem ersten Test zu verifizieren und unsere Entscheidung zu untermauern. Getestet wurde die Laufzeit von Map.insert mit zufälligen Zahlen und Strings der Länge 10. Die durchschnittliche Zeit für die Tests ist in Millisekunden in der folgenden Grafik dargestellt.

An der Grafik ist abzulesen, dass die Ergebnisse sich sehr stark von der ersten Phase unterscheiden, denn Strings scheinen um den Faktor 4 langsamer zu sein als Integer, während Tupel überraschenderweise ein sogar noch besseres Ergebnis liefern. Leider ist in der Implementierung des Tests ein Fehler unterlaufen, welcher sehr spät auffiel, sodass diese Erkenntnis leider zu spät kam.

Dieses zweite Diagramm trägt die Anzahl der Insert-Operationen gegen die Zeit auf und legt eine lineare Regressionsgerade an, welche die Datenpunkte sehr genau schneidet. Zu erwarten gewesen wäre allerdings eine leicht gebogene Kurve, da die Laufzeit durch ( ( )) gegeben sein sollte. Dies liegt daran, dass die Testmenge für die Inserts noch zu klein ist, daher haben wir einen weiteren Test mit extrem großen Werten durchgeführt, welcher für die weiteren Tests allerdings kaum relevant ist, da Matrizen mit so vielen Produktionen eher unwahrscheinlich sind.

0,000 100,000 200,000 300,000 400,000 500,000 600,000 700,000 800,000 900,000

0 5000 10000 15000 20000 25000 30000 35000 40000 45000

Zeit in ms

Insert Operationen

MapTest2

Integer Tupel Strings

Linear (Integer) Linear (Tupel) Linear (Strings)

Die Skalierung der x-Achse ist diesmal in Sekunden gefasst und man erkennt, dass sich die Laufzeiten etwas mehr als verdoppeln, wenn man die Eingabegröße verdoppelt. Die Ineffizienz der Strings wird hier ebenfalls noch einmal deutlich, da der Test für 1,6 Millionen Inserts mit Strings auf der Testmaschiene den Speicher sprengt und deswegen ausgelassen werden musste. Die Regression im Diagramm ist allerdings immer noch linear und sehr passend, daher muss man hier direkt auf die Werte schauen:

Inserts Integer Tupel Strings 100000 0,558 0,465 2,031 200000 1,200 1,000 4,148 400000 2,57 2,235 8,835 800000 5,53 4,918 18,58 1600000 12,01 10,95

Bei dieser Eingabegröße sieht man also langsam den logarithmischen Faktor. Ein Grund warum dieser nicht so stark ausgeprägt ist könnte sein, dass wir mit einer leeren Map beginnen und nur Wertepaare einfügen. In einem späteren Test werden wir sehen, dass bei lookup-Operationen in einer großen Map der Faktor auch bei kleineren Werten schon zum Tragen kommt. Insgesamt sind aber Grammatiken mit einer Größe von über 100000 Produktionen wohl nur in den seltensten Fällen nötig und dank der Aufteilung in einzelne Höhen in unserer Implementierung wahrscheinlich unrealistisch.

0,000 5,000 10,000 15,000 20,000 25,000 30,000 35,000 40,000

0 200000 400000 600000 800000 1000000 1200000 1400000 1600000 1800000

Zeit in s

Insert Operationen

MapTest3

Integer Tupel Strings

Linear (Integer) Linear (Tupel) Linear (Strings)

5.5.2 Transposition

Für die Transpositionstests haben wir die Testmatrix 1 gewählt, da die Funktion somit alle Produktionen (außer dem letzten Terminal) durchgehen und verändern muss. Die Ergebnisse sind in den folgenden Diagrammen dargestellt.

Erkennbar ist, dass auch bei einer sehr großen Menge von Produktionen die Transposition kaum Zeit benötigt und unsere theoretische Ermittlung der Komplexität ( ) korrekt ist.

0 5 10 15 20 25

0 5000 10000 15000 20000 25000 30000 35000 40000 45000

Zeit in ms

Produktionen

TranspositionTest

5.5.3 Skalarmultiplikation

Für die Multiplikation mit einem Skalar haben wir eine theoretische Laufzeit von O(|G|*log(b)) ermittelt. Der log(b)-Faktor fällt in unserer Betrachtung hier allerdings raus, da alle Terminalwerte auf 1 gesetzt sind. Wir testen die Funktion mit allen drei Testmatrizen, da ihre unterschiedliche Beschaffenheit Einfluss auf die Laufzeiten haben sollte.

Die Linearität des Algorithmus scheint also auch hier bestätigt zu sein. Allerdings fällt auf, dass alle Tests mit der Grammatik G1 im selben Nanosekundenbereich durchlaufen und die der anderen beiden Grammatiken deutlich länger benötigen. Des Weiteren scheint es ungewöhnlich, das Grammatik G3 weniger Zeit benötigt als Grammatik G2, denn der Algorithmus übernimmt alle Höhenstufen außer der letzten und betrachtet dann jede Produktion der Stufe 0. Während G1 genauso viele Stufen wie Produktionen hat, hat G2 nur die Hälfte und G3 nur die Stufe 0. Da G1 wesentlich kürzere Ausführungszeiten vorweist, scheint das Durchlaufen der Höhenstufen aber keinen Einfluss zu haben. Alle Grammatiken

0 2 4 6 8 10 12 14 16 18 20

0 5000 10000 15000 20000 25000 30000 35000 40000 45000

Zeit in ms

Produktionen

ScalarMultTest

G2 G3 Linear (G2) Linear (G3)

haben gemeinsam, dass nur ein Terminal enthalten ist und somit nur eine Multiplikation ausgeführt wird. Daher ist dieses Ergebnis sehr überraschend und leider für uns bisher nicht erklärbar.

5.5.4 Matrixelement berechnen

Die Funktion zur Berechnung eines Elements der Matrix wurde mit allen Testmatrizen getestet. Ihr worst case ist die Grammatik G1, da hier in jeder Höhe die Berechnungen zum nächsten Schritt gemacht werden müssen.

0 1 2 3 4 5 6 7 8 9 10

0 5000 10000 15000 20000 25000 30000 35000

Zeit in s

Produktionenl

GetElementTest

G1 G2 G3 Poly. (G1 ) Poly. (G2) Poly. (G3)

Die Diagramme bestätigen eindeutig, dass die Funktion mit G1 am meisten Probleme hat. In den Auswertungen müssen bei G1 doppelt so viele DownSteps berechnet werden wie in G2, was zu einer überproportionalen Erhöhung der Laufzeit führt. Überraschend ist wieder die Matrix G3, da bei der Berechnung sehr viele Lookups in einer Map mit bis zu 30.000 Elementen durchgeführt werden und jede Produktion eine Addition verursacht. Die gezeigten Trendlinien sind Polynome mit Grad 2 und passen sich sehr gut den Funktionen an. Die Werte von G1 und G2 lassen ebenfalls eine Laufzeit von O(|G|²) vermuten, da sich die Laufzeit bei Verdopplung der Eingabegröße in etwa vervierfacht. Für G3 gilt dies nicht; hier ist die Laufzeit etwas mehr als verdoppelt, was auf einen Einfluss der recht großen Map schließen lässt, welche im Gegensatz zum MapTest hier auch ausgelesen wird.

So richtig genau bestimmen können wir den zusätzlichen Faktor aber nicht, da auch nach mehreren Wiederholungen des Tests die Werte stark schwanken.

Insgesamt schaut es nun so aus, dass die Laufzeit durch ( ) beschränkt ist, was kein gutes Ergebnis wäre, da wir einen linearen Algorithmus angestrebt haben. Allerdings zeigt uns der Test mit G2 und G3, dass die Laufzeiten für alle Operationen, die nicht auf DownSteps operieren, vergleichsweise klein sind. Der Test zeigt uns also, dass die Höhe der Matrix der wesentliche Faktor in der Laufzeit ist und die Gesamtzahl der Produktionen dagegen wenig Einfluss hat. Nimmt man also eine weniger stark komprimierte Matrix (G1 ist optimal komprimiert, da alle Einträge gleich sind), sollten sich die Laufzeiten nicht wesentlich unterscheiden, obwohl dadurch mehr Produktionen vorhanden sind.

5.5.5 Gleichheitstest

Der Gleichheitstest erreicht seine worst case Laufzeit genau dann, wenn die beiden übergebenen Produktionen jeweils auf 4 neue unterschiedliche Nichtterminale zeigen, welche für sich wiederrum ebenfalls auf 4 neue unterschiedliche Nichtterminale zeigen, da in diesem Fall keine redundanten Gleichungen auftauchen. Dies ist für den Algorithmus der worst case, da die Verarbeitung der sich daraus ergebenden Matrizen die Hauptzeit einnimmt. Daher haben wir hierfür eine spezielle Matrix dsGrammar, welche wir hier zum Testen einsetzen. Welche im Prinzip eine maximale nicht komprimierte Matrix darstellt. Die

0 0,05 0,1 0,15 0,2 0,25 0,3 0,35

0 5000 10000 15000 20000 25000 30000 35000

Zeit in s

Produktionen

G3

Funktion erwartet eine Höhe und eine Liste von Namen welche sie verwenden kann als Parameter. Die daraus resultierende Grammatik hat insgesamt ∑ Produktionen sodass wir nicht mit sehr großen Höhen testen können bevor wir wie im MapTest3 an die Speichergrenzen stoßen. Des Weiteren dauern die Tests ab einer Höhe von 7 mehrere Tage, sodass ein Effizienter Test nicht möglich war.

Zu erkennen ist immerhin, dass die Laufzeit drastisch ansteigt, wenn eine Höhe hinzugefügt wird. Von Höhe 5 auf Höhe 6 vervierfacht sich die Anzahl der Produktionen, während sich die Laufzeit um etwa den Faktor 80 erhöht. Damit liegen wir weit unter aber immer noch bei einer Skalierung von , was an sich für die Operation einen noch akzeptablen Wert entsprechen würde.

5.5.6 Ergebnisübersicht

Insgesamt wurden alle Algorithmen erfolgreich implementiert. Die Benchmarks haben gezeigt, dass die theoretischen Laufzeitberechnungen in den meisten Fällen sehr gut der Praxis standhalten und die Implementierung zum Teil überraschend schnell Operationen wie Multiplikationen auf komprimierte Matrizen ausführen kann. Dafür steigen jedoch die Zugriffszeiten und der Gleichheitstest von bestimmten Bereichen zweier Matrizen. Dafür können wir bei gut komprimierten Matrizen mit sehr viel größeren Grammatiken arbeiten als es in der unkomprimierten Version möglich wäre bevor es bei den primitiven Algorithmen zum Speicherüberlauf kommt.

Aufgefallen ist, dass die Zugriffszeiten auf die verwendeten Maps sehr viel niedriger sind als von uns gedacht und somit die Aufteilung der Produktionen in verschiedene Höhen nicht den gewünschten Erfolg hat. An manchen Stellen im Code hat sich diese Aufteilung auch als äußert nachteilig hinsichtlich der Implementierbarkeit gezeigt und manchmal brauchten wir Maps verschiedener Stufen im selben Schritt, sodass die Idee der Top-Down Implementierung nicht immer aufgegangen ist. Das Zusammenziehen der Maps könnte außerdem weiteren Spielraum für Optimierungen geben, so würde z.B. der Circuit Datentyp obsolet werden, da er im Prinzip nur eine Vereinigung der ProductionMaps enthält.

Die Implementierung benötigt an der einen oder anderen Stelle noch einen Feinschliff und es gibt sicher noch ein wenig Luft nach oben, die Performance und auch die Handhabung der einzelnen Funktionen zu verbessern. Trotzdem sind wir aber mit dem Ergebnis recht zufrieden und zum Teil überrascht, mit welch riesigen Matrizen die Implementierung problemlos umgehen kann, auch wenn man dies nicht überbewerten sollte, da unsere Testgrammatiken sehr gute bis optimale Kompressionsraten aufweisen.