Ich begrüße Sie zur letzten Vorlesung, nein Übung zur Softwaretechnik
1 mit einem Java-Rätsel.
Was haben wir hier?
Wir haben eine Main-Methode und wir haben hier drei Funktionen, die
etwas ausdrucken, und zwar hier oben eine Summe und hier unten eine
Summe.
Und die Frage ist nun ganz einfach, was werden diese beiden wohl
ausgeben, diese beiden Summen da oben, was werden die wohl für eine
Ausgabe erzeugen?
Ich mache es Ihnen heute nicht unnötig schwer, ich frage Sie einfach
diese vier Varianten, nachdem Sie mal eine Minute darüber nachgedacht
haben, was das sein könnte.
Okay, dann frage ich Sie jetzt mal, glauben Sie, dass hier diese erste
Zeile rauskommt?
Okay.
Vielleicht die zweite?
Wer ist dafür?
Beigen im Walde, okay.
Die dritte?
Wer meint, die dritte könnte rauskommen?
Okay.
Und die vierte?
Wer ist für die vierte?
Noch ein paar?
Okay.
Also für die ersten beiden ist irgendwie niemand und hier konzentriert
es sich ein bisschen.
Die vielen Sechsen, die sind, glaube ich, attraktiv.
Die vielen Sechsen sind attraktiv, meinst Du?
Ja, das weiß ich nicht so genau.
Was sagen Sie dazu?
Warum, glauben Sie, kommt hier das Nicht-Raus oder warum kommt das
hier raus?
Ich verrate Ihnen mal noch nicht, was rauskommt.
Aber was sind Ihre Überlegungen?
Welche der Zahlen meinen Sie?
Die hier.
Okay.
Der Hinweis ist, das hier ist nicht die Zahl, wonach sie aussieht,
sondern das ist eine Oktalzahl wegen der führenden Null.
Aha.
Ja, das ist mal ein Hinweis.
Das heißt, hier unten geht mal mit der Summe was kaputt.
Okay.
Noch ein Gedanke dazu vielleicht?
Okay.
Also das hier unten war schon der Hinweis, warum die Summe hier unten
kaputt geht und warum hier was anderes rauskommt, als man vielleicht
vermuten würde, wenn man es naiv addiert zu 4444.
Und das da oben ist noch ein Tuck fieser, denn wenn Sie Ihre Augen mal
auf super scharf stellen, dann stellen Sie vielleicht einen
Unterschied fest zwischen der Eins hier vorne, dieser Eins hier unten,
dieser Eins da und dieser Eins da.
Kann ich Ihnen ein L für eine Eins vormachen?
Können Sie sich vorstellen, wie lange Sie möglicherweise in einem
echten Programm nach einem Fehler dieser Bauart gesucht hätten?
Schlimm, nicht wahr?
Tut ein bisschen weh?
Okay.
Das ist nochmal, was wir gerade eben herausgefunden haben.
Und unser großer Tipp ist, vermeiden Sie führende Nullen, es sei denn,
Sie meinen tatsächlich die Oktalzahl, also man ist ja versucht, wenn
man so irgendwelche Zahlen untereinander schreiben möchte, dass man so
führende Nullen einsortiert, um das bündig zu machen oder sowas.
In Java ist das sehr gefährlich, in anderen Programmiersprachen je
nachdem auch.
Seien Sie vorsichtig mit führenden Nullen und wenn Sie mit diesem
kleinen L hier eine Typumwandlung zu long dieser Zahl machen wollen,
das ist ja das, was das L an dieser Stelle macht, dann verwenden Sie
doch, um das etwas deutlicher zu machen, einfach das große L.
Das tut das Gleiche und es steht einfach rein typografisch viel
deutlicher da als das kleine L.
Das war einfach nur eine kleine Falle, die man sich so einfangen kann.
Haben Sie Fragen zu diesem Java-Rätsel?
Okay, dann sind wir glaube ich für heute mit dem Rätsel durch und ich
übergebe das Kommando an meinen Kollegen.
Vielen Dank, guten Morgen auch nochmal von mir.
Ich tourne jetzt mit Ihnen die ersten paar Aufgaben des Übungsblattes
durch.
Wie Sie sich erinnern, war die erste Aufgabe eine Testaufgabe.
Da sollten Sie zuerst mal die Zwischensprache von diesem Kell-Text
erzeugen und danach einen Kontrollschlussgrafen daraus bauen.
Das ist nicht furchtbar schwierig, das ist, wenn ich nicht ganz falsch
liege, eine alte Klausuraufgabe.
Und was Sie im Wesentlichen tun müssen, ist eben Sachen rausstreichen,
die es in der Zwischensprache nicht gibt.
Das ist einmal oben das Public-Final-Double-Test-Me und alles, was
irgendwie mit dem Kontrollschluss zu tun hat, also was einen Sprung
nach sich zieht.
Das erste, was wir machen, ist ein bisschen umschreiben.
Wir schmeißen oben dieses Public weg, da bleibt dann nur noch ein
Kommentar übrig.
Und dann muss man eben die Schleife auslösen, die Wild-Schleife, die
hier unten aufhört, indem man sich den Schleifenanfang merkt, damit
man weiß, wo man hinterher hin muss.
Und ans Ende dieses Goto schreibt in Zeile 110.
Dieser Sprung hier nach oben, das ist die Wild-Schleife.
Die bricht genauso ab wie die ursprüngliche, also diese Bedingung
hier, die bleibt gleich.
Und was wir dann noch tun müssen, ist innen drin dieses If auflösen.
Dieses if y kleiner 0, das wird erstmal negiert, damit die Reihenfolge
des Programmcodes gleich bleibt.
Das heißt, in Zeile 60 haben wir dann ein if not und wenn das eben
nicht zutrifft, dann überspringen wir den when-Teil.
Dann fangen wir also direkt mit dem else-Zweig an, das ist das a
gleich c hier unten in Zeile 90.
Und falls doch der true-Fall eintritt, der ursprüngliche, dann fällt
man hier einfach durch, ist in der 70 und überspringt den else-Zweig.
Das heißt, hier kommt ein Goto rein, das eben dieses a gleich c
überspringt.
Das ist im Prinzip schon alles.
Dann sind wir in der Zwischensprache.
Und das sollten Sie in der Klausur mehr oder minder runterschreiben
können.
Ist ja auch nicht furchtbar schwierig.
Wenn man das gemacht hat, muss man sich überlegen, wo kriege ich denn
jetzt Grundblöcke her, damit ich einen Graph bauen kann.
Und Grundblöcke, die kann man immer daran identifizieren, wo ein
Sprung passiert.
Das heißt, dieses Goto 90 hier oben und das Goto 40 hier unten, da ist
sicherlich ein Block zu Ende.
Da biegt man ja im Programmfluss ab.
Dann gibt es noch ein Goto 100, das ist in der Mitte.
Da ist natürlich auch das Ende von so einem Knoten.
Wir haben in der Vorlesung gelernt, dass man nicht mitten in den
Grundblock reinspringen kann, sondern immer nur an den Anfang von
einem Block.
Das heißt, im nächsten Schritt gehen Sie hin und schauen, wo springe
ich denn überall hin.
Man springt hier zu der 40 nach oben, das heißt hier zu dem c gleich a
plus b und so weiter.
Das heißt, hier möchten wir in den Grundblock eintreten und dafür
braucht man natürlich einen.
Das heißt, an der Stelle müssen wir auch nochmal eine Grenze
einziehen.
Dann kommen Sie zu diesen zwei dunklen Linien.
Glücklicherweise brauchen Sie in Zeile 90 nicht noch was, weil da
haben Sie ja schon von dem Goto 100 oben drüber einen neuen Block.
Dann haben Sie die Aufteilung des Quelltextes schon hinter sich und
dann müssen Sie das eigentlich nur noch auf ein Blatt Papier malen und
ein bisschen Pfeile dazwischen stecken.
Das heißt, Sie machen um den Quelltext nach oben immer so einen
schönen Kasten und zeichnen dann hinterher die Kanten ein,
entsprechend diesen Goto-Befehlen.
Und dann kommen Sie zu so einem Bild.
Was Sie nicht vergessen dürfen in der Klausur ist ein Anfangs- und ein
Endknoten.
Der Endknoten ist einfach, da haben wir hier dieses Return-Statement.
Wenn das eine Void-Methode wäre, dann kann es auch sein, dass der
Endknoten leer ist, weil da einfach nichts passiert.
Und was wir auch noch brauchen ist ein Startknoten.
Der ist hier oben, da ist dann eben die Eingabe drin.
Das ist das, was wir hier im Quelltext nur als Kommentar gesehen
haben.
Und dann sind Sie schon fertig.
Sie sollten dann nochmal ein bisschen drüber lesen, ob die Reihenfolge
des Programms noch okay ist, ob Sie das mit den True- und False
-Verzweigungen richtig gemacht haben, ob das noch mit dem
ursprünglichen Quellcode übereinstimmt.
Und dann sind Sie eigentlich schon fertig.
Und in der Klausuraufgabe war man an der Stelle dann auch fertig.
Wir wollten noch ein bisschen mehr von Ihnen.
Wir wollten, dass Sie da JUnit-Testfälle schreiben, um so ein paar
Abdeckungskriterien mal abzuprüfen.
Und wenn man das in JUnit schreibt und dann mit ECLMA durchmisst, dann
bekommt man angezeigt im Eclipse, welche Anweisungen denn von den
Testfällen überdeckt worden sind.
Das sieht so aus.
Wir haben dann im Programmcode ein schönes Highlighting, also dieses
hellgrün.
Das heißt, ja, da ist ein Testfall drüber gestolpert.
Gelb heißt, naja, da war einer, aber nicht vollständig.
Das heißt, es gibt andere Belegungen, die man da noch prüfen müsste.
Wahrscheinlich in dem Fall war der If immer false.
Deswegen ist auch das Return-Statement nie aufgerufen worden.
Und naja, dass dieses If eben nur mit einer Wahrheitsbelegung und
nicht mit beiden ausgewertet wurde, das sagt Ihnen das Gelbe.
Wenn man dann mit der Maus drüber zeigt, dann sagt einem Eclipse auch,
wie viele von diesen Faden man denn verpasst hat, wenn es mehrere
gibt.
Zusätzlich dazu gibt es eine hübsche Tabelle.
Und das war im Wesentlichen die, die wir gerne gehabt hätten von
Ihnen.
Oder eine Zeile daraus.
Wir wollten ja wissen, wie ist es denn in der KfG-Aufgabe mit der
Überdeckung für Anweisungsüberdeckung.
Natürlich ist es 100 Prozent, weil sonst hätten wir diese
Anweisungsüberdeckung ja nicht erreicht.
Was man da dann auch noch sehen kann, ist eben, welche anderen Teile
vielleicht nicht vollständig abgedeckt worden sind.
Und wenn man jetzt, sage ich mal, seine Testabdeckung erhöhen will,
dann versucht man vermutlich nicht irgendwas von 99 auf 100 Prozent zu
bringen, sondern eher was von 0 auf 50, weil das ziemlich sicher
einfacher geht.
Sie sollten Testfälle bauen.
Das Erste, was einem da einfällt, ist ein Zweigüberdeckungstest.
Das kriegen Sie hin mit der Eingabe für A gleich minus 1 und B gleich
2.
Wenn Sie das in JUnit hinschreiben, dann ist das so wie hier in diesem
grünen Kasten.
Sie brauchen also irgendwo ein K, das Sie sich vorher initialisiert
haben.
Ich habe das in der AddBefore gemacht, damit es den Testfall nicht so
aufbläht.
Und dann müssen Sie eben diese Eingabe reinlaufen lassen.
Da das schon eine 100 Prozent Abdeckung gibt, sehr klar,
Zweigüberdeckung ist ja mehr als Anweisungsüberdeckung, kann man
diesen Testfall auch nehmen, um den ersten Aufgabenteil zu
beantworten.
Man kann einfach denselben Testfall nehmen, um auch die
Anweisungsüberdeckung hinzukriegen.
Wir wollten von Ihnen da einen minimalen Testfall, das heißt so wenig
wie möglich Aufrufe von dem Programm haben.
Und da das nur ein Aufruf ist, ist es natürlich trivialerweise
minimal.
Der dritte Teil von dieser Aufgabe, das war die Pfadüberdeckung.
Wer von Ihnen hat den Eingabewert gefunden, um eine Pfadüberdeckung
hinzukriegen?
Ehrlich?
Und wie kommt man dann in den dritten Durchgang?
Also Ihr Kommilitone sagte gerade,
er hat eine Eingabe, die zweimal durch die Schleife durchläuft.
Und dann habe ich gefragt, naja, was ist denn mit dem dritten
Durchlauf?
Antwort der Kommilitone, ich kann ja nicht alle machen.
Aber genau das ist, was Pfadüberdeckung sagt, Sie sollen alle
möglichen Pfade abdecken, die das Programm nehmen kann.
Und natürlich, wenn Sie eine Schleife haben, die von der Eingabe
abhängt, dann können Sie die Eingabe immer größer machen und kriegen
noch einen Schleifendurchlauf mehr.
Dementsprechend können Sie keine Pfadüberdeckung erreichen.
Das geht einfach nicht.
Deutlicher sieht man das bei Methoden, die zum Beispiel einen String
als Parameter haben und den zeichenweise durchgehen in der Schleife.
Da können Sie natürlich den String immer länger machen.
Und für jeden Buchstaben, den der String mehr hat, kriegen Sie einen
Schleifendurchlauf mehr und dann auch noch einen Pfad mehr.
Dementsprechend ist es zumindest theoretisch nicht möglich,
Pfadüberdeckung zu erreichen, weil Sie die Eingabe immer größer machen
können.
Praktisch können Sie argumentieren, naja, ich nehme einfach den
längsten String, den es gibt, oder ich nehme die größte Zahl, die es
gibt, in dem Bereich des Parameters.
Und dann habe ich ja alles abgedeckt, was pragmatisch möglich ist.
Auf die Diskussion würden wir uns einlassen, aber den Testfall haben
Sie vermutlich nicht geschrieben.
Die richtige Antwort an der Stelle wäre, naja, das geht nicht, weil
das ist halt potenziell unbegrenzt.
Und damit hat es sich erledigt.
Das heißt, der dritte Teil von der Aufgabe, der war ziemlich einfach,
wenn man auf die Idee kam.
Sie erinnern sich daran, in diesem Quelltext, den ich nochmal vorhole,
da gibt es eine Methode f, die aufgerufen wird.
Die ist nicht in der AFG-Aufgabenklasse drin, sondern in einer
Unterklasse davon.
Und wir haben sie gefragt, was ist denn, wenn man da eine andere
Methode einträgt, ein anderes f.
Das erste f, das war ja, das sieht man hier am Rand noch, das war im
Prinzip das Quadrieren von dieser Eingabe und hinterher das Vorzeichen
wieder dran pappen.
Wenn sie also eine negative Zahl reingeben, dann kriegen sie das
Quadrat und trotzdem das Minus davor.
Das war quasi die komplizierte Fassung, die einfache Fassung war.
Wir quadrieren es einfach und das Vorzeichen geht dadurch verloren.
Und die Frage ist, naja, was passiert denn dann mit der Abdeckung von
der KFG-Aufgaben, von dieser Test-me-Methode.
Test-me hatte ja in der Mitte diesen Aufruf von f, das heißt, hier
wird was berechnet und dann von diesem Berechnungsergebnis hängt der
weitere Verlauf ab.
Und wenn wir hier das f austauschen durch was, was nur positive
Ergebnisse zurückgibt, dann ist natürlich y auch immer größer gleich
0.
Und dementsprechend kann man nie in den Benzweig hier reinspringen.
Das heißt, diese Zeile werden sie nie abdecken können mit einem
Abdeckungstest und dementsprechend auch nicht mit den anderen
Abdeckungskriterien.
Wenn sie das in Eclipse laufen lassen, in E-Klemmer, dann sieht das
ungefähr so aus.
Naja gut, in B kommen sie nie an und diese Bedingung, die wird halt
nicht vollständig überdeckt.
Dafür haben sie den Elstzweig immer erreicht.
Und wir wollten von Ihnen wissen, wie kann man das denn detektieren.
Das ist auch wieder eine Frage aus der Klausur, die ich, gelinde
gesagt, ein bisschen daneben finde, weil die Antwort ist, naja,
dynamisch kann man einfach so ein Überdeckungswerkzeug nehmen wie E
-Klemmer.
Das haben wir gerade gemacht.
Dafür gab es einen Punkt in der Klausur und den anderen Punkt gab es
für eine statische Methode.
Und das ist alles, was mit Hingucken zu tun hat.
Also Inspektion oder Methode des scharfen Hinsehens oder Code Reviews
oder Walkthroughs oder was auch immer wir da in dem Testkapitel alles
gemacht haben.
Haben Sie Fragen soweit zu dieser Aufgabe?
Sehr schön.
Als nächstes haben wir Sie gefragt, was sind denn da für
Entwurfsmuster drin in der Aufgabe.
Und im Aufgabentext selbst gibt es eine Schablonenmethode, das haben
wir schon ein paar Mal gehabt.
Ich glaube sogar in einem Testfall.
Und naja, die Schablonenmethode ist in der KFG-Aufgabe drin, dieses
TestMe.
Das ist die Methode, die den Algorithmus quasi festschreibt.
Und die Einschubmethode, das Austauschbare, das ist genau das F.
Ausgetauscht, das ist ja genau das, was wir gemacht haben.
Ziemlich trivial an sich.
Es gab noch ein zweites Muster, das so ein bisschen hinkt.
Das ist in den Testfällen, da gibt es eigentlich eine Fabrikmethode,
wenn man die denn haben will, in diesem Setup.
Das ist aber nur in Anführungszeichen, weil die Methode ja nichts
zurückgibt, sondern eigentlich eine Klassenvariable setzt.
Und naja, danach haben wir auch nicht gesucht.
Das ist also nur am Rande.
Haben Sie Fragen zu dieser Testaufgabe?
Hatten Sie da Probleme mit?
Klar, warum man e-Klemmer vielleicht benutzen möchte?
Haben Sie sonst irgendwelche Fragen zu dem Testen und dem JUnit?
Okay.
Die nächste Aufgabe, die war mit Parallelität.
Sie erinnern sich vielleicht an dieses Beispiel aus der Vorlesung oder
dann zumindest vom Übungsblatt.
Da gab es ein Stückchen Programmtext.
Da wurde eine Globalvariable geprüft, ob sie größer als 0 ist.
Und wenn ja, dann wird sie dekrementiert.
Und die Frage ist jetzt, wenn man mehrere Fäden hat, also 2 zum
Beispiel, und die führen diesen Code gleichzeitig aus, dann gibt es ja
Probleme.
Das war das Beispiel aus der Vorlesung.
In der Übung haben wir jetzt da noch hinzugefügt, dass dieses
GlobalVar eben Volatil ist.
Und die Frage ist, naja, kann mit dieser Änderung, mit dem Volatil,
auch ein Wettlauf auftreten?
Also die erste Frage, was war denn nochmal Volatil?
Wissen Sie, was Volatil tut?
Thomas?
Vielen Dank.
Also ein Teil von Ihnen weiß es noch.
Volatil sorgt dafür, dass die Variable nicht im Cache vom Prozessor
gehalten wird, sondern direkt in den Arbeitsspeicher geschrieben wird.
Das heißt, wenn ein Prozessor nachschauen möchte, was denn der Wert
von dieser Variable ist, dann sagt ihm sein Cache immer, ich habe
keine Ahnung, guck mal im Speicher nach.
Das heißt, er kriegt immer den aktuellen Wert.
Das ist einerseits nicht schlecht, weil wenn sich der Wert ändert,
dann kriegt der Prozessor das ja gleich mit.
Aber das hilft leider trotzdem nicht, um den Wettlauf zu beheben.
Man kann sich das immer noch so vorstellen, dass man eine
Fadenverschränkung dieser Art bekommt.
Das heißt, Volatil ist am Anfang größer als 0, dann kommt der erste
Faden daher und prüft das und dann wird er schlafen gelegt, weil seine
Zeit jetzt um ist, er hat genug geprüft.
Dann kommt der zweite Faden, macht die Prüfung, es hat sich ja nichts
geändert.
Das Ergebnis ist also immer noch wahr.
Er dekrementiert es und legt sich schlafen und dann kommt der erste
Faden.
Und der weiß ja noch, ja Global War war ja größer als 0, ich mache
einfach mal da weiter, wo ich war.
Also dekrementiere ich es und schon hat man genau dieses
Wettlaufproblem.
Das heißt, das Volatil hilft an der Stelle einfach gar nichts.
Man müsste den Zugriff irgendwie sperren.
Das war schon der Aufgabenteil A.
Der Aufgabenteil B, da ging es um das parallele Summieren von einem
Array.
Das war im Wesentlichen der Aufgabentext, den Sie auf dem Blatt
hatten.
Und der Algorithmus, der steht jetzt hier auf der linken Seite.
Das heißt, wir haben so einen Teil-und-Herrsche-Vorgang.
Wir kriegen ein Array als Eingabe, das ist hier die Variable, die auch
Array heißt.
Und wenn das Array nur ein Element hat, dann gibt es eben genau dieses
Element zurück.
Da fehlt noch ein Klammer auf 0 zu.
Weil wir natürlich nicht das Array zurückgeben wollen, sondern nur den
Int.
Und wenn das Array größer ist, dann wird ein neuer Faden aufgemacht.
Der eine, also zwei neue Fäden.
Der erste, der kriegt die linke Hälfte, der zweite kriegt die rechte
Hälfte.
Und das Ergebnis ist dann quasi die Summe aus diesen beiden Hälften.
Es wird also rekursiv immer weiter aufgerufen.
Und die Frage war, wie viele Fäden brauchen wir denn, um ein Array der
Länge 8 durchlaufen zu lassen, durchzurechnen.
Dieses komische Gekrakel, das ist das Symbol für Faden.
Ist wohl auch irgendwo in den Vorlesungsfolien kursiert.
Und im ersten Durchlauf wird es geteilt.
Das heißt, wir haben zweimal ein Array der Länge 4.
Das wird dann wieder geteilt, weil das ist ja immer noch nicht 1 lang.
Wird nochmal geteilt.
Das ist immer noch größer als 1.
Und dann haben wir ganz viele Arrays der Länge 1.
Und die kann man dann tatsächlich explizit berechnen.
Das heißt, bis hier unten werden jedes Mal zwei neue Fäden
aufgespalten für jedes Teil Array.
Und hier unten wird dann das erste Mal wirklich gerechnet.
Das heißt, hier unten in diesen Fäden, da wird dann das Return Array 0
hier oben ausgeführt.
Naja, kann man einfach abzählen, wie viele Fäden das sind.
Das sind 14.
Das ist aber leider nicht die ganze Wahrheit, weil sie brauchen ja
auch noch den Hauptfaden, der am Anfang aufgerufen wurde.
Also eigentlich brauchen sie 15 Fäden, um das auszurechnen.
Und jetzt ist die große Frage, wieso ist das sequentiell denn
schneller als parallel?
Oder anders, ist es denn sequentiell schneller als parallel?
Und naja, die Antwort ist ja, sequentiell ist schon ein bisschen
cleverer an der Stelle, weil die ganze Infrastruktur, die man braucht,
um die Fäden neu aufzumachen und die Fäden wieder zusammenzuführen und
das ganze Scheduling, das dahinter hängt, ist so langsam, dass das die
Beschleunigung von der Parallelität wieder auffrisst.
Das heißt, sie haben da nichts gewonnen, wenn sie das so umsetzen.
Wenn sie so etwas bauen möchten in der Realität, dann würden sie
irgendwo eine Abbruchschranke reinmachen, die größer ist als 1.
Also sagen wir mal bei 100 oder bei 500 oder bei 1000, je nachdem wie
groß das Eingabe-Array normalerweise ist.
Und dann sequentiell durchrechnen, damit sie nicht so viele Fäden
aufspalten müssen.
Genau, das ist das Ganze nochmal in sauber erklärt.
Und dann kommt meine allzeit beliebte Frage, haben Sie Fragen zu
diesem Aufgabenteil?
Ist Ihnen aufgefallen, dass diese Aufgabe B direkt etwas mit der
Programmieraufgabe nebendran zu tun hat?
Ich sehe zumindest niemanden heftig nicken.
Dann darf Thomas jetzt erklären, wie wir die Earth Movers Distance
parallelisieren können und ob das wirklich Sinn macht.
Ja, in Aufgabe 3 haben wir Sie nochmal ein bisschen gekitzelt und ein
bisschen geärgert.
Denn zum einen, der Testfall, den wir Ihnen da mitgeliefert hatten,
der testet natürlich nur diese Performance-Aufrufschnittstelle.
Der war natürlich funktional ein bisschen kastriert.
Der war nicht so, dass Sie für jede beliebige Parallelisierung dort
mit einem grünen Testfall auch sicher sein konnten, dass Ihre
Parallelisierung richtig ist.
Aber die alten Testfälle, die Sie noch hatten, die konnten Ihnen da
vielleicht einen Tipp geben.
Zum zweiten mussten Sie ein klein bisschen darüber nachdenken, dass
die Earth Movers Distance nicht ganz so einfach parallelisierbar ist,
wie sie auf den ersten Blick aussieht.
Und ich wollte jetzt auf den folgenden Folien Ihnen mal ein bisschen
näher bringen, warum das vielleicht ein Tuck komplizierter ist, als
sich einige von Ihnen das vielleicht zurechtgelegt haben.
Was war nochmal die Earth Movers Distance?
Das war folgendes.
Wir haben hier zwei Histogramme.
Wir haben in unserem Beispiel jetzt hier sechs Buckets, sechs
Kategorien, wie auch immer.
Wir haben ein blaues Histogramm und ein rotes Histogramm.
Und die Earth Movers Distance berechnet Ihnen nun ein mögliches
Distanzmaß zwischen diesen beiden Histogrammen.
Wir sehen ja hier, dass hier Unterschiede teilweise drin sind, die
sind unterschiedlich hoch, die sind auch in unterschiedliche
Richtungen und manchmal haben wir auch keinen Unterschied.
Und die Earth Movers Distance sagt hier, das ist hier diese
Rechenvorschrift, dass man eben elementweise von links nach rechts
paarweise durch die Elemente der beiden Felder durchläuft und dabei
die Differenz berechnet der beiden.
Also man berechnet hier das Gelbe, das ist f1i-f2i für jedes Bucket
hier und zusätzlich die Differenz dessen, was wir von links sozusagen
noch mitgenommen haben.
Also hier fangen wir mit 0 an.
Die Differenz vom Blauen zum Roten ist plus 1.
Hier ist die Differenz vom Blauen zum Roten plus 3, aber um auf diese
4 von dem Gelben zu kommen, addieren wir noch die 1 aus dem
Vorgängerschritt hinzu.
Hier geht es ähnlich weiter, hier ist die Differenz minus 3 und diese
minus 3 addieren wir auf die 4 von Schritt 2 und landen wieder bei 1
und so weiter.
Das Gelbe sind sozusagen die Werte, die beim Durchlauf dieser Schleife
der Reihe nach in der Variable div landen werden.
Und die nächste Zeile macht jetzt nichts anderes als alle Gelben
betragsmäßig aufsummieren.
Und das sieht dann zum Beispiel so aus in unserem Beispiel.
Hier unten fangen Sie mit der 1 an, dann kommt hier eine 4 hinzu, hier
kommt eine 1 hinzu, hier kommt nochmal eine 1 hinzu, beachten Sie
übrigens, dass hier lustigerweise eine 1 addiert wird, obwohl diese
beiden Kategorien hier identisch sind.
Auch das hier wird betragsmäßig addiert, also die Kurve Summen geht
eben immer nach oben, egal ob Sie eine positive oder eine negative
Zahl der Variable div haben.
Soweit mal für Earth Movers Wissen, was sie tut.
So, die Frage ist nun, wie kriegen wir das parallel?
Und warum ist es nicht so einfach?
Es sieht also so aus, dass Sie sich vielleicht mal die Schleife
anschauen und dann stellen Sie vielleicht fest, das div hier, das ist
immer ein Wert aus der vorherigen Iteration.
Wir müssen also irgendwie diese vorherige Iteration irgendwie auflösen
oder retten oder sowas.
Nun könnten Sie ja auf die Idee kommen, naja, wenn ich das einfach,
also diese beiden grünen Streifen, das sind meine beiden Arrays, das
rote und das blaue aus der Vorgängerfolie und die sind, die könnte ich
jetzt, der sequenzielle läuft jetzt einfach von links nach rechts
durch und wenn Sie am Ende sind, dann haben Sie Ihre Summe und das ist
das Differenzmaß, was der Algorithmus berechnet.
Nun könnten Sie ja einfach das ganze blockweise tun und zum Beispiel
einfach die beiden Felder, die beiden Arrays in vier Blöcke aufteilen
und jeweils vom Beginn des Blockes bis zum Ende des Blockes laufen.
Das funktioniert nicht, weil Sie für die Berechnung des ersten Index
des zweiten Blockes ja den Übertrag brauchen vom Vorgängerblock.
Sie brauchen also das letzte Element vom Vorgängerblock.
Jeweils.
Das ist diese Abhängigkeit, die hier oben drin steckt.
Und nun kann man sich überlegen, kann ich das vielleicht irgendwie
auflösen oder korrigieren.
Also Überlegung 1 ist vielleicht, ok, ich habe hier zwar einen
Übertrag, aber kann ich vielleicht trotzdem einfach erstmal blockweise
naiv rechnen und das Einzelergebnis, was ich am Schluss eines Blockes
hier hinten jeweils bekommen habe, korrigieren, um den Betrag, den ich
danach dann im Vorgängerblock habe.
Der Witz ist ja, ich möchte die parallel berechnen, das kann ich auch
tun, aber ich muss dann das Ergebnis korrigieren, weil wenn alle
gleichzeitig beginnen zu rechnen, habe ich ja zu Beginn noch nicht
diesen Übertrag aus dem Vorgängerblock.
Sonst bin ich ja wieder sequenziell.
Das wäre also die erste Überlegung, um das trivial parallel zu
kriegen, so Blockparallel zu kriegen.
Kriege ich irgendwie diesen Übertrag auf mein Ergebnis, was ich
inzwischen von meinem Block habe, rübergerettet und aufgetragen?
Unsere Antwort ist, nein.
Wir haben das nicht geschafft.
Vielleicht sind Mathematiker unter Ihnen, die da herausgefordert sind,
aber was es Ihnen kaputt macht, ist die ABS-Funktion.
Die ist an der Stelle ein bisschen hinterhältig.
Die macht Ihnen diesen Übertrag deshalb kaputt, weil Sie hier zu
Beginn um einen Betrag eben verschoben werden, der in der Folge einen
sehr großen Fehler verursacht, wenn Sie die Absolutwerte der Zahlen
berechnen.
Das kriegen Sie nachher nicht mehr in den Griff.
Ich möchte das mal so stehen lassen, für die Details dürfen Sie sich
das gerne mal überlegen.
Nehmen Sie es für den Moment einfach mal so hin, die ABS-Funktion
verhindert, dass Sie beliebige Stellen in diesem Feld einfach direkt
berechnen können oder Berechnungen um einen Versatz korrigieren
können.
Das geht nicht so einfach.
Also brauchen Sie früher oder später irgendeine Parallelisierung, die
es Ihnen erlaubt, dieses Manko zu umgehen.
Was könnte man da tun?
Nun ja, jetzt sind wir hier wieder so ein bisschen inherent
sequenziell und jetzt müssen wir wahrscheinlich ein bisschen
feinkranularer in diesen Algorithmus reingehen und uns die Elemente
anschauen, die sich parallelisieren lassen und diese parallelisieren
und die anderen vielleicht mit irgendwelchen Abhängigkeiten versehen.
Und eine Möglichkeit, wie man das tun kann, ist beispielsweise, dass
man sich ein temporäres Array hier anlegt, in das man die
Zwischenergebnisse der Differenz speichert.
Also die Idee hier ist, man speichert zunächst mal alle Differenzen,
also f1 von i minus f2 von i.
Das ist mal der erste Schritt, den wir auf jeden Fall brauchen.
Das können wir ja trivial parallel machen.
Ich lege hier einfach ein drittes Array sozusagen drunter, das die
Differenzen aller Elemente aufnimmt.
Das kann man tun, das ist auch trivial parallel und lässt sich
unabhängig berechnen.
Das kann ich ja machen.
So, und dann kann ich den Diff berechnen.
Ich kann also für jedes Element aus meinem Feld voller Differenzen,
kann ich dieses Diff hier einmal durchschleifen.
Wenn ich das blockweise mache, so wie in der Idee von eben, dann habe
ich im zweiten, dritten und vierten Block wieder dieses Problem, dass
ich den Übertrag aus dem ersten zum zweiten, den Übertrag aus dem
zweiten zum dritten und so weiter nicht berücksichtigt habe.
Das ist aber eine Konstantaddition.
Ich bin ja nur um den, da ich bis jetzt nur eine Differenz und eine
Addition gemacht habe, bin ich nur um einen festen Betrag in die
Differenzen verschoben.
Es war noch keine Abs drin.
Ich könnte also, nochmal zusammengefasst, erstmal parallel die
Differenzen berechnen und dann oder gleichzeitig das Diff blockweise
berechnen und dann in den nachfolgenden Blöcken meine Differenzen um
den Wert korrigieren, nach oben oder unten, den ich beim Übertrag
nicht berücksichtigen konnte.
Sind Sie bis dato, rein vom Verständnis her, noch bei mir?
Okay, sehr schön, dann können wir weitermachen.
Dann fehlt uns nur noch die Summe, ne?
Genau.
Glücklicherweise habe ich da was vorbereitet.
Man kann nämlich hier, naja, jetzt muss man halt mit diesem
Absolutwert über dieses Temporärfeld laufen und das aufsummieren.
Das lässt sich bedingt parallelisieren.
Sie können ja auch jetzt wieder über dieses Temporärfeld einfach
blockweise parallel arbeiten, Teilsummen bilden und dann am Schluss
die Teilsummen addieren.
Also wenn Sie ein Feld, ein Array haben mit 64 Elementen, das können
Sie mit 4 Fäden bearbeiten.
Jeder Faden rechnet 8 Elemente zusammen und wenn diese 8 Fäden fertig
sind, haben Sie 4 Teilsummen aus den 4 Blöcken.
Die summieren Sie meinen Wegen noch sequenziell und dann sind Sie auch
schon fertig.
Ein bisschen eleganter wäre hier sowas wie eine Reduktion zu verwenden
oder sowas, aber das ist letzten Endes nur eine Geschmacksrichtung
dessen, dass Sie da parallel summieren.
Hier kommen die Tone, lamentiert, aber ist das nicht furchtbar
langsam?
Komme ich dazu.
Also, was ich Ihnen vorgestellt habe, ist das prinzipielle Problem.
Sie brauchen da eine irgendwie geartete Zwischenspeicherung und die
Sache ist die, jetzt kommt eine der Fallstricke, die da drin war.
Die Felder, die wir Ihnen zur Initialisierung gegeben haben, die waren
relativ groß, deutlich größer, als wir sie für die Bildähnlichkeiten
eigentlich brauchen.
Nämlich so im Millionenbereich.
Und da kommen zum Beispiel folgende interessante Dinge zustande.
Die Sache ist die, die Berechnungen, die Sie auf den Elementen machen,
die sind einigermaßen simpel.
Das sind simple Additionen und auch die Abs-Funktion ist sehr, sehr
einfach.
Was hier teuer ist in Java, ist das Allokieren von Speichern.
Und wenn Sie, wie vorgeschlagen, dieses T allokieren, dann brauchen
Sie sehr viel Zeit.
Warum ist das so?
Weil Java Ihnen zusichert, dass die Elemente in Ihrem Array null sind.
Das heißt, wenn Sie ein 64 Millionen Array allokieren, auf diese Art
und Weise hier, dann latscht Java einmal über alle 64 Millionen
Elemente und setzt diese auf null, damit das auch schön sauber
initialisiert ist.
Das kostet Zeit.
Das war sehr viel Zeit.
Und zwar so viel Zeit, dass es Ihnen in aller Regel die
Beschleunigung, die Sie möglicherweise durch eine solche
Parallelisierung bekämen, im Regelfall wieder auffrisst.
So, ich habe Ihnen mal ein paar Zahlen mitgebracht, was das eigentlich
heißt.
Normal heißt hier, Sie allokieren Speicher, wie Sie es gewohnt sind
aus Java.
Also einfach mit so einer Zeile hier.
Und nur das schiere Allokieren von 64 Millionen Elementen in einem
Beispiel hat auf unserer Testmaschine etwa 280 Millisekunden benötigt.
Das ist aber eher so 0 Millisekunden für das Allokieren des Speichers
und 279 Millisekunden für das Nullsetzen jedes einzelnen Elements.
Man sieht das auch daran, wenn wir danach einmal nochmal schreibend
durchlaufen, dann kostet das keine 290 Millisekunden mehr, weil dieses
Feld schon im Zwischenspeicher ist, schon im Cache ist und wir daher
uns ein bisschen Zeit ersparen.
Was ich Ihnen gerne mitgeben möchte ist, es gibt in Java, kennen Sie
Mythmasters, diese Serie, die am Anfang immer diesen Spoiler haben,
machen Sie das ja nicht zu Hause, macht das ja nicht zu Hause, liebe
Kinder, das ist gefährlich, hier sind Profis am Werk, die wissen, was
sie tun und so.
Das gilt für dieses Unsafe hier auch so.
Don't do that at home.
Es gibt in der Klassenbibliothek eine Möglichkeit, dass man diese
Allokiersemantik ändert und sozusagen sich vom Garbage Collector
verabschiedet und von dieser Nullsetzung verabschiedet und sich quasi
C -Semantik reinholt.
In C ist es so, dass Sie, wenn Sie Speicher allokieren, dass Sie den
einfach instantan bekommen, dass es also sehr schnell geht, Sie haben
aber dafür keine Initialisierung zugesichert, je nachdem, wie Sie es
allokieren, aber im Regelfall haben Sie dort keine Initialisierung
zugesichert und das Lustige ist, die brauchen wir ja auch gar nicht.
Wir schreiben ja ausschließlich erstmal auf unser T, die bräuchten wir
ja eigentlich nicht.
Insofern könnten wir uns eigentlich diese Millisekunden hier sparen
und ich habe es einfach mal ausprobiert, wie das ist.
Ja, das funktioniert auch ganz gut.
Wir haben dann halt eine Zu-Fuß-Speicherverwaltung am Bein.
Die Frage ist, lohnt sich das für die Performance?
Die Antwort ist nein.
Weil wenn Sie anschließend dann schreibend über das zwar schnell
allokierte Feld laufen und übrigens sich ziemlich viel Technik ans
Bein binden, Sie müssen diesen Speicher zu Fuß indizieren und so, das
ist alles sehr hässlich, dann sind Sie trotzdem noch langsamer als
wenn Sie einfach, oder in einem ähnlichen Bereich, als wenn Sie
einfach die Standard Garbage Collected Safe Normal Methode benutzt
hätten.
Das ist also auch hier leider nicht ganz die Lösung des Problems, denn
das ist die Zahl, die zählt.
Sie müssen ja schreibend durchlaufen.
Die Frage ist also, kann man den Earth Movers Distance sinnvoll
parallelisieren?
Die Antwort ist vielschichtig.
Ja, kann man.
Aber es kommt darauf an, wie groß, es kommt darauf an, was für eine
Maschine Sie haben.
Sie alle, die meisten von Ihnen, und auch wir haben diese Tests auf
Maschinen gefahren, in denen die Rechenleistung verhältnismäßig hoch
ist im Vergleich zum Speicherdurchsatz.
Das ist genau das, was uns hier die Geschwindigkeit kostet.
Wir haben 264 Millionen Float-Elemente, zweimal lesend vom Speicher
und einmal schreibend zum Speicher.
Ein Float hat in Java vier Byte.
Sie können sich also ausrechnen, wie viele Megabyte da durch den
Speicherbus fließen.
Und auf jedem dieser vier Bytes wird eine einigermaßen triviale
Operation im Prozessor durchgeführt.
Und die geht so schnell, dass der Speicherbus zum Engpass wird.
Das führt dazu, dass Sie eine Beschleunigung bei der Parallelisierung
nur dann in diesem Fall erreichen, wenn Sie den Cache effizient
nutzen.
Und von vorne nach hinten ein- oder mehrfach, was jetzt für die
vorgestellte Variante notwendig wäre, von vorne nach hinten durch 64
Millionen elementige Felder durchlaufen, ist nicht cache-freundlich.
Warum ist das nicht cache-freundlich?
Weil der Cache hat eine bestimmte Kapazität.
Wenn aber Ihre Daten länger sind als diese Kapazität, dann laschen Sie
von vorne einmal diesen Cache ab, dann haben Sie keine
Wiederverwendung mehr.
Sondern Sie machen hier weiter, dann ist wieder der Cache voll, dann
ist wieder der Cache voll, dann ist wieder der Cache voll.
Oh, ich muss ja nochmal durch meine Elemente durchlaufen.
Aber die sind nicht mehr im Cache.
Jetzt muss ich wieder von vorne beginnen und das wieder lesen.
Sie können also nur dann Geschwindigkeitssteigerungen durch
Parallelität herausholen, wenn Sie in den Größen bleiben, der Cache
Ihnen ermöglicht.
Nur so können Sie ein bisschen Performance herausholen.
Das war ein bisschen fies, diese Aufgabe, denn Sie erreichen selbst
auf 8 oder 12 Ternen, je nachdem nur ein Speedup von 1,3, 1,5 für die
Größenordnung.
Das war ein bisschen härter.
Die Erkenntnis ist, Parallelisieren geht, Parallelisieren geht oft,
Parallelisieren ist nicht immer einfach, wenn Sie Datenabhängigkeiten
haben und der Engpass beim Parallelisieren ist nicht zwangsläufig der
Prozessor, je nachdem, was Sie tun.
Haben Sie Fragen dazu?
Haben Sie das Gefühl, Sie haben das Problem, worum es geht,
einigermaßen verstanden?
Okay, das ist schön.
Haben Sie das Gefühl, Sie haben aus dieser Aufgabe auch ein bisschen
was gelernt, wenigstens durch die Präsentation?
Okay, das ist schön.
Dann sind wir, glaube ich, mit der Parallelitätsaufgabe...
Wir hatten noch eine Möglichkeit, wie es doch einigermaßen flott geht,
was du hier so hübsch als Alternative bezeichnet.
Alternative?
Ja, naja, wir brauchen ja das Eingabe-Array nicht mehr.
Ah ja, richtig.
Das war diese Sache, wir haben ja schon zwei Eingabe-Arrays, kann ich
die denn nicht einfach missbrauchen?
Na sicher kann ich das tun.
Also die Idee dahinter ist, wir hatten ja dieses Array T, in dem wir
die Zwischenergebnisse speichern, und wenn wir uns jetzt nicht mit
dieser Cache-Optimiererei da beschäftigen wollen, dann wäre ja auch
die Möglichkeit, ich benutze einfach die, die muss ich ja eh lesend
laden, also ich lade immer zwei Elemente, addiere sie, diffe sie,
absolviere sie, wie auch immer, und schreibe das Ergebnis einfach in
ein Element der beiden Eingabe-Arrays wieder zurück.
Das geht technisch, das können Sie tun, aber damit verändern Sie
natürlich die Eingabe-Arrays als solche.
Die Eingabedaten gehen dann verloren.
Und im allgemeinen Fall ist das natürlich dann eine Semantikänderung
Ihrer Funktion.
Das heißt, wenn Sie als der Programmierer, der die Funktion
programmiert, nicht wissen, ob der Aufrufer seine übergebenen
Variablen vielleicht nochmal im Originalzustand braucht, dann pfuschen
Sie den in die Daten und ins Handwerk.
Und wenn Sie semantisch nicht vorher klargemacht haben, dass Sie
Eingabevariablen, eigentlich Eingabevariablen, in der Funktion, in
place, ändern, dann hat der Aufrufer wahrscheinlich damit ein Problem,
dass seine übergebenen Variablen weg sind.
Insofern, das in place würde Ihnen hier diese ganze Speichergeschichte
ein bisschen entschärfen, die Cache-Geschichte bleibt aber, und Sie
haben eine Semantikänderung, die Sie im allgemeinen Fall eigentlich
nicht wollen, seien Sie haben sie eben vorhalb der Schnittstelle
vereinbart.
Okay.
Dann, jetzt aber.
Jetzt aber sind wir durch.
Okay, sehr schön.
Parallelität lohnt sich.
Genau.
Okay.
Die letzte Aufgabe auf dem Übungsblatt war eine Bonusaufgabe, da
sollten Sie UML nach Code abbilden.
Schauen Sie es sich einfach im Quelltext an, von der Musterlösung, wie
immer nicht sofort, sondern vielleicht morgen Mittag.
Es macht wenig Sinn, das hier am Quelltext durchzuturnen, weil Sie
ohnehin immer nur sehr wenig lesen können.
Schauen Sie sich einfach an, wie die unterschiedlichen Arten
implementiert sind.
In der Musterlösung sind noch weitere Implementierungsbeispiele drin,
als die, die wir gefordert hatten.
Einfach mal einen Blick drauf werfen und sich überlegen, könnte ich
das zum Beispiel in UML hinmalen in der Klausur in fünf Minuten.
Das ist ungefähr so die Zeit, die Sie dafür hätten, wenn Sie einen
Automaten kriegen und die Frage ist, wie macht man das denn in Java
mit der expliziten ausgelagerten Speicherung.
Haben Sie ungefähr fünf Punkte, vielleicht sechs, also fünf, sechs
Minuten und dann ist die Aufgabe durch.
Also einfach mal ausprobieren, ob Sie das hinbekommen.
Und ja, diese Klausurfrage, das ist natürlich genau das, was in der
letzten Übung gerne kommt.
Matthias, Matthias, sag mal, was kommt eigentlich in der Klausur dran?
Alles.
Gibt es da nichts, was ich weglassen kann?
Nee.
Auch nicht vielleicht das, was unter Optionales ist?
Das vielleicht schon, ja.
Das vielleicht schon, okay.
Also das, was unter Optionales steht, ist tatsächlich optional, ist
für die Klausur nicht relevant.
Wir werden Sie also nicht nach OCL abfragen und nach Java 2D.
Davon abgesehen, könnte Sie aber alles treffen.
Wenn in den Vorlesungsunterlagen eher so bla bla drin ist, sage ich
mal abwertend, dann kommen natürlich dazu auch eher bla bla Fragen.
Also nicht malen Sie ein Prozessmodell, sondern nennen Sie die sieben
Stufen und die 43 Dokumente, die daraus fallen.
Da muss man natürlich kein UML-Diagramm vom Wasserfall bauen.
Bei den UML-Sachen muss man natürlich eher ein UML-Dokument malen.
Wird die Klausur arg schwer?
Das wird die schwerste Klausur, die wir jeweils gemacht haben.
Und korrigieren wir arg hart?
Gnadenlos.
Wie immer.
Und wie viele Punkte gibt es?
Es gibt 60 Punkte.
Und wie lange muss ich schreiben?
Eine Stunde.
Also daher auch die Sache, wenn Sie fünf Minuten für eine Aufgabe
brauchen, dann sollten Sie da auch fünf Punkte für bekommen.
Wenn in der Aufgabe steht, es gibt zwei, dann sollten Sie nach fünf
Minuten vielleicht darüber nachdenken, etwas anderes zu machen, weil
es lohnt sich schon nicht mehr.
Danach die größere Frage.
Mit wie vielen Punkten hat man dann bestanden?
Keine Ahnung.
Voraussichtlich mit irgendwas um die 20.
Also 15 bis 25.
Das können wir Ihnen nach der Klausur sagen.
Als Klausurlerntipp möchte ich Ihnen gerne noch eine kleine Metapher
erzählen.
Wenn Sie jetzt hier säßen und stellen Sie sich vor, wir wollten Ihnen
das Kochen beibringen, dann stellen Sie sich vor, dass Sie bitte nicht
jetzt hergehen und auf chefkoch.de alle Rezepte auswendig lernen, die
es da gibt.
Sondern wir wollten Ihnen nicht zeigen, gucken Sie mal, hier gibt es
178 Millionen Rezepte, die kann man auswendig lernen.
Nein, wir wollten Ihnen zeigen, wie funktioniert ein Herd, wie
funktioniert ein Topf, wie funktioniert ein Messer, wie schmeckt
Gemüse und wie schmeckt Obst und welches Gewürz passt zu welchem
Gemüse und welchem Obst.
Und zu welchem Gericht Sie das jetzt verarbeiten, in Ihrem konkreten
Fall, das bleibt Ihnen überlassen.
Und wie so oft ist auch draußen in der Wirtschaft so, ob Ihrem Kunden
das Gericht schmeckt, das entscheidet halt der Kunde.
Sie müssen halt einfach so gut kochen, wie es irgendwie geht.
Kochen Sie kreativ, kochen Sie gut, kochen Sie nett, nutzen Sie die
Werkzeuge.
Wenn Sie immer nur alles aufkochen und pürieren, werden Ihre Gerichte
halt möglicherweise nach der Weile ein bisschen fad und Sie werden
irgendwann Kunden verlieren.
Dann kommt der Restaurantretter?
Oder die Nachklausur?
Da wollte ich jetzt nicht hin.
Da wolltest Du nicht hin, okay.
Die Superkochs.
Genau, also lernen Sie so, dass Sie das Gefühl haben, Sie haben die
Werkzeuge verstanden, das Prinzip verstanden.
Sie wissen, wie ein Messer funktioniert, wie man es schleift und
welche Länge von Messern man für welches Gemüse einsetzt.
Nicht 178.000 Kochrezepte.
Okay?
Haben Sie Fragen zur Metapher?
Okay.
Dann sind wir für heute mit dem Übungsstoff durch.
Wir haben aber noch ein bisschen Vorlesungsstoff für Sie.
Das ist aber leichte Kost, Sie brauchen keine Angst haben.
Wenn mich nicht alles täuscht, haben Sie diese Folie von Herrn Tichy
schon gezeigt bekommen.
Wenn nicht, dann ist das äußerst schade.
Und dann müssen Sie es zu Hause nachlesen.
Das gleiche gilt für die nächsten drei Folien, weil die sind nicht so
super toll, interessant und lustig.
Und wir fangen jetzt direkt an mit dieser Folie.
Und Sie haben sich mit Professor Tichy mit Inspektionen und
Lesetechniken und mit lauter solchen Offline-Sachen beschäftigt.
Und wir haben gedacht, wir bringen Ihnen noch mal ein bisschen was
online mit.
Also Werkzeuge, die das Ganze ein bisschen leichter machen.
Wie schon Unit-Tests zeigen, ist eigentlich so ein Entwicklungsprozess
oder Schritte im Entwicklungsprozess sind eigentlich viel cooler, wenn
man irgendetwas Technisches hat als Informatiker, an dem man sich
festhalten kann.
In den Unit-Tests haben Sie Programmquelltext geschrieben, um das
Programm zu testen.
Und wenn Sie jetzt hier für eine Inspektion noch ein technisches
Werkzeug haben, das vielleicht schön integriert ist in den
Entwicklungsprozess, das ist doch gleich viel attraktiver eigentlich.
Wenn man was zum Tatteln hat, macht es gleich mehr Spaß.
Macht es gleich mehr Spaß.
Und deswegen haben wir uns gedacht, wir zeigen Ihnen mal ein, zwei
Werkzeuge, die Inspektionen ein bisschen informatiktauglicher machen,
indem Sie einfach so ein bisschen technisches Gedöns zur Inspektion
hinzufügen.
Und das können Sie prozessmäßig im Wesentlichen von der Werkzeugseite
her auf zwei Arten und Weisen machen.
Das eine ist, Sie integrieren die Inspektion, das Review, in den
Entwicklungsprozess selbst.
Das ist, glaube ich, das andere Beispiel, das kommt danach.
Aber das hier ist die Methode, Sie haben Ihren ganz normalen
Entwicklungsprozess, das heißt, Sie checken was aus, Sie entwickeln
was, Sie buchen es wieder ein, Sie kommitten das Ganze und dann hängt
irgendwo in Ihrem Versionskontrollsystem vielleicht ein hübsches
Webfrontend, das Ihnen die Commits aus dem Versionskontrollsystem
wieder schön darstellt und dann kann man in diesem Webfrontend an die
Commits noch irgendwelche Metadaten dran pappen, wie zum Beispiel
Kommentare.
Andere Entwickler können einfach einen Commit kommentieren und sagen,
der Quelltext hat mir aber sehr gut gefallen.
Oder die Übergabe der Parameter funktioniert zwar so, ist aber eine
Sicherheitslücke, an der Stelle können wir das nicht so und so machen
und einen Änderungsvorschlag bringen.
Dann kann man zu einem Commit eben eine verbesserte Version einpflegen
und die erscheint dann eben dort auch.
Außerdem kann man bei einer Inspektion eben auch Bewertungen
verteilen, wie gut der Quelltext ist, der da inspiziert wurde.
Also bin ich als Entwickler, der den Code eines anderen Entwicklers
inspiziert, würde ich diesen Code jetzt freigeben wollen für den
Kunden?
Ist das okay so?
Oder hat Matthias vielleicht missprogrammiert?
Und dann kann ich in dem Review-Werkzeug auf den Knopf drücken und
sagen, ja, also die Änderungen, die er da jetzt eingebucht hat, das
ist okay, die geben wir an den Kunden frei.
Da ich ja keinen missbaue, Thomas aber schon, wie ich das aber vorher
wusste, kann man natürlich, also hier ist es optional, er bucht es ein
und es ist direkt sichtbar.
Also alle anderen leiden direkt unter dem, was er da verbrochen hat
und können sich dann hinterher in dem Inspektionswerkzeug beschweren.
Jetzt bin ich sein Chef und sage, ich sag Käse.
Wenn die eh immer nur alle meckern, dann machen wir das einfach mal
verpflichtend.
Das ist die Alternative.
Thomas darf zwar auschecken, er darf programmieren und darf das dann
einchecken, aber eben nicht ins Versionskontrollsystem, sondern in das
Inspektionswerkzeug.
Das heißt, irgendwo poppt dann ein Fenster auf und sagt, ja der
Thomas, der hat da mal wieder was programmiert, soll mal besser einer
angucken.
Genau das passiert dann.
Der Prozess ist jetzt so gebaut, Sie dürfen quasi als Entwickler nicht
direkt in das Versionskontrollsystem einbuchen, das nachher für
offizielle Versionen des Programms verwendet wird, sondern Sie buchen
in das Inspektionswerkzeug ein.
Im konkreten Fall Gerrit ist das tatsächlich einfach ein Git nach
außen, Sie buchen also einfach per Git ganz normal Ihre Änderungen in
das Gerrit rein.
Und der Witz ist jetzt, Gerrit nimmt Ihren Commit und stellt den jetzt
auch, wie die anderen Werkzeuge, auf einer Weboberfläche dar und
erfordert jetzt, das ist jetzt konfigurierbar, verschiedene Aktionen
an diesem Commit, bevor Gerrit diesen Commit dann weitergibt an das
echte Versionskontrollsystem.
Und das ist eine Art der, wie soll ich sagen, der Commit-Hürde, wenn
Sie so wollen.
Sie programmieren etwas, Sie buchen etwas in Gerrit ein.
In Gerrit ist dann hinterlegt, dass zwei andere Entwickler Ihren Patch
da irgendwie genehmigen müssen und erst wenn zwei andere Entwickler
meinetwegen gesagt haben, ja, Ihre Änderung ist okay, dann fließt sie
ins tatsächliche offizielle Repository von Git oder so.
Das wäre eine Möglichkeit, wie man das gestalten kann.
Übrigens ist das auch eine schöne Variante, die Gerrit Leute sagen
selbst, so kann man relativ leicht bei irgendwelchen
Softwareprojekten, die Open Source sind oder sowas, einfach Änderungen
der großen Masse annehmen.
Sie können ja einfach jeden Wild in das Gerrit buchen lassen und Sie
als Kernentwickler gucken sich die Änderungen an und entscheiden dann,
na, das war Quatsch oder diese Änderung war okay und dann übernehmen
Sie diese Änderungen von Ihrem Git, ohne dass Sie Ihr hochheiligstes
Git an irgendwelche fremden Leute zum Schreiben exponiert haben.
Eine Möglichkeit, einfach Inspektion zu nutzen.
Genau.
Wie sieht es jetzt aus?
Das ist ein Werkzeug, das sich als Eclipse-Plugin tarnt.
Sie haben ja also einen Eclipse-Schirm, was die Details machen, ist ja
nicht so dramatisch.
Der Punkt ist, Sie haben in der Mitte Ihren Quelltext-Editor, der wird
erweitert um Markierungen, hier zum Beispiel das Blaue oder das Rote,
zu einer Markierung gibt es hier rechts ein Kommentarfeld, wo also ein
Inspektor, ein Reviewer hier oben seine Kommentare hinzugeben kann und
eben sagen kann, naja, dieser Abschnitt, der ist eben nicht so hübsch
oder da ist irgendwie ein Fehler drin oder sowas.
Hier unten haben Sie noch die Liste dieser hinzugefügten Kommentare
zum Quelltext und das heißt Agile Review hier und dieses Werkzeug
bucht einfach die Kommentare und Metainformationen, die zur Inspektion
gehören, einfach zum Projekt in die Versionskontrolle einfach mit ein.
Fertig.
Das ist eines der Werkzeuge aus der ersten Kategorie.
Sie haben Metadaten beim Projekt dabei, die können Sie nutzen, müssen
Sie aber nicht von technischer Seite her.
Möglicherweise ist es organisatorisch durchgesetzt.
Genau.
Blabla.
Gerrit, das hatte ich Ihnen schon gesagt.
Zu Gerrit haben wir kein Foto mehr.
Doch.
Willst du sehen?
Gib mir mal das Gerrit-Foto.
Wollen Sie sie auch sehen?
Ja, einer hat...
Auch nicht viel übersichtlicher.
Ist ein Webfrontend.
Hier oben haben Sie die Daten zu dem Commit.
Die Informationen, die hier stehen, die haben Sie mit Git in Gerrit
gepusht.
Und hier oben stehen jetzt die Daten zu dem Commit.
In diesem Feld hier stehen die Inspektoren, die Reviewer.
Je nach Konfiguration von Gerrit sind da Zwangseinträge drin oder der
Einbucher kann da selbst Reviewer benennen.
In diesem speziellen Fall ist übrigens der Hudson ein Inspektor.
Der taucht doch hier unten bei den Kommentaren wieder auf.
Matthias, was ist denn Hudson?
Ein Hudson?
Fluss in New York?
Auch.
Ne, aber das hier.
Hudson ist ein Build-Server.
Der macht nichts anderes als immer bei der Versionskontrolle
nachfragen.
Gibt's denn was Neues?
Und wenn es mal was Neues gibt, dann holt er sich das unübersetztes
und dann kann man noch verschiedene andere Werkzeuge daran schließen.
Der kann zum Beispiel die Testfälle automatisch laufen lassen.
Wofür brauchen wir die Testfälle?
Und wenn dann ein Testfall fehlschlägt aufgrund eines Commits, dann
steht das natürlich im Webfrontend drin.
Dieser Commit führt dazu, dass Test XYZ nicht mehr durchläuft.
Also Beispiel.
Ich programmiere etwas.
Meine Programmierung, mein Stückchen, was ich jetzt geändert habe,
meinen Quelltext, buche ich ein.
Dann landet es in Gerrit.
In Gerrit ist hinterlegt, dass alle Änderungen einmal durch den Hudson
durch müssen.
Der Hudson hängt also mit seinem Schnorchel an Gerrit und stellt fest,
oh, da ist eine frische Änderung.
Er lädt sich mein Zeug runter, lässt alle Unit-Tests laufen.
Laufen die alle grün durch?
Daumen hoch.
Dann hat er vielleicht noch weitere Tests, wie zum Beispiel
Checkstyle.
Entspricht das, was ich da geschrieben habe, den allgemeinen Code
-Conventions des Projekts.
Läuft meinetwegen grün durch?
Alles klar.
Dann habe ich vielleicht noch ein MA hintendran, das mir irgendwie
Testabdeckung der Unit-Tests analysiert.
Habe ich mindestens 85% Testabdeckung auch in dem Code, den ich gerade
eingebucht habe?
Alles klar, grün.
Und wenn alle diese Dinge, die ich in Hudson konfigurieren kann, diese
automatischen Prüfungen über meinen Quelltext gelaufen sind, dann
macht der Hudson hier oben einen grünen Haken hin bei Verified und
sagt, alles klar, die Änderung von Herrn Karcher, die darf hier
durchgehen, bezogen auf die formalen Prüfungen, die der Hudson da
macht.
Genau.
Was gibt es hier noch mit der restlichen Metainformation?
Man kann halt auch zu Fuß irgendwelche Kommentare einpflegen und
irgendwelche Abhängigkeiten einpflegen und sowas.
Und der Punkt ist, wenn ausreichend Reviewer, alles konfigurierbar,
bla bla, eben gesagt haben, jawohl, die Änderung von Herrn Karcher,
die ist durch den Hudson durch, ich finde die auch okay, wir können
die funktional brauchen, die sieht auch hübsch aus, es gibt keine
Sicherheitsbedenken oder sowas, dann drücken Sie auf den Knopf,
genehmigt, dann wandert das durch.
Okay, damit sind wir, glaube ich, mit den Review-Werkzeugen an der
Stelle durch.
Das ist alles, was ich Ihnen auch schon mündlich gesagt habe.
Und dann kommen wir so langsam zu den Prüfprogrammen.
Kommen wir zu Ihnen?
Genau, das eine Prüfprogramm, was Sie schon kennen, ist das
Checkstyle, das setzen Sie schon ein und verwenden Sie.
Es gibt natürlich jede Menge andere Prüfprogramme, die Sie
automatisiert auf den Quelltext ansetzen könnten.
Es gibt eine ganze Reihe von Fehlersuchprogrammen, die Sie da benutzen
können oder noch mehr Prüfprogramme?
Ich spick mal sowas oder sowas oder sowas.
Ja genau, das sind alles Dinge, die Sie automatisch mit statischer
oder dynamischer Analyse finden können, wie zum Beispiel, Sie haben
aus Versehen einen Quelltext geschrieben, der nicht mehr erreichbar
ist, den kann man also rausmachen, solche Dinge.
Die kann man teilweise eben auch einfach in diesen Hudson einpflegen
und der lehnt beispielsweise dann Ihre Einbuchung ab in Gerrit, wenn
Sie gewisse Voraussetzungen nicht erfüllt haben, die das Projekt eben
einfach durchsetzt.
Dann schauen wir uns doch mal die Integrationstests an.
Was sind Integrationstests?
Na ja, Integrationstests sind die nächsthöhere Stufe in der
Granularität und dafür ist natürlich erforderlich, dass Sie die
Komponenten schon mal getestet haben.
Bei Integrationstests können Sie, wir kommen gleich dazu, auf
unterschiedliche Art und Weisen die verschiedenen Einzelkomponenten
des Systems Stück für Stück zu einem Gesamtsystem zusammenklappen und
das Zusammenspiel der Komponenten testen und dafür gibt es, wir haben
uns mal so eine Matrix ausgedacht, die sieht ungefähr so aus.
Sie können also entweder vorgehensorientiert oder testzielorientiert
Integrationstesten.
Wir sagen Ihnen gleich, was das ist.
Oder Sie können inkrementell integrieren oder eben unmittelbar.
Das ist eigentlich meiner Meinung nach die wichtigere Unterscheidung.
Bei inkrementell fügen Sie Komponente für Komponente zusammen und
gucken dann, wie das Zusammenspiel funktioniert und können immer Stück
für Stück das Ganze testen.
Unmittelbar ist, ich glaube, der Begriff Big Bang trifft die
Vorgehensweise hier auch ganz gut, also Sie integrieren erstmal alles
und Big Bang trifft es an der Stelle im Sinne von, dann muss alles
gehen, aber es könnte halt auch sein, dass alles in die Luft fliegt.
Was bedeuten die einzelnen Dinge?
Ah ja, hier haben wir es nochmal.
Sie haben die Sache, Sie müssen alle Systemkomponenten fertig haben,
Sie können also schlecht teilintegrieren, wenn es unmittelbar komplett
ist und Sie haben Schwierigkeiten, wenn Sie Testüberdeckung oder
Defektlokalisierung betreiben wollen, weil das ganze System
funktioniert ja oder nicht.
Alternativ machen Sie es eben Stück für Stück.
Sie testen einfach die fertigen Komponenten, soweit sie vorhanden
sind.
Das hat auch den Vorteil, Sie können den Integrationstest für
Komponenten beginnen, obwohl andere Komponenten vielleicht noch gar
nicht fertiggestellt sind.
Sie haben da also eine etwas zeitlichere Flexibilität und
Überschneidung möglicherweise.
Einen kleinen Nachteil gibt es allerdings, wenn Sie Abhängigkeiten
haben in einem Modul, in irgendeiner Komponente auf etwas, was noch
nicht fertig ist, dann brauchen Sie unter Umständen einen Testhelfer,
der eben so tut, als wäre diese Komponente, die Sie brauchen.
Sie haben also unter Umständen da unten ein bisschen mehr Testarbeit,
weil Sie noch Mockups schreiben müssen für Code, der eben noch nicht
fertig ist.
Ja, weil möglicherweise eine Komponente, irgendeine andere Komponente
braucht, die aber noch nicht fertig ist, dann muss ich die halt
irgendwie simulieren oder so.
Dafür muss ich dann ein bisschen zusätzlichen Code schreiben, um den
Simulant zum Testen zu benutzen, der echten fertigen Komponent.
Hat aber auch den Vorteil, wenn man zum Beispiel eine Abhängigkeit hat
von einer Datenbank, dann will man nicht jedes Mal die große Oracle-12
-Datenbank mit 84 GB Daten befüllen, um testen zu können, sondern
vielleicht will man genau dafür ohnehin einen Mock schreiben, der nur
ein paar Dummy-Daten zurückliefert, ohne Datenbank-Anbindung.
Also Vorteil und Nachteil zugleich.
Die Kategorisierung vorgehensorientiert oder testzielorientiert ist so
ein bisschen, wie akademisch Sie vorgehen möchten.
Bei der vorgehensorientierten gehen Sie etwas akademischer vor,
nämlich so, wie Sie sich das System ausgedacht haben, wie die
Architektur, wie Sie sich als Modell zurechtgelegt haben, Ihnen das
diktiert.
Und bei der testzielorientierten Integrationstesterei gehen Sie eben
von Testzielen aus, die Ihnen möglicherweise von Ihrem Projektleiter
oder von wem auch immer gesetzt werden und Sie schauen halt, dass Sie
diese Testziele erfüllen, statt akademisch Ihr Modell zu befolgen.
So, und was bedeuten jetzt die Begriffe im Einzelnen?
Ja, das Big Bang hatten wir schon, das ist so die Kernaussage, es geht
nichts, bis schließlich alles geht, mit ein paar Nachteilen behaftet.
Ja, dann können Sie geschäftsprozessorientiert testen, das heißt, das
ist natürlich testzielorientiert, weil das Testziel hier sozusagen
inherent ist, ich möchte einen bestimmten Geschäftsvorfall testen
können, von vorne nach hinten, und dann gehen Sie eben
geschäftsprozessorientiert an die Testerei ran und prüfen eben die
Geschäftsvorfälle der Reihe nach, ob sie funktionieren.
Funktionsorientiert heißt, Sie bündeln Ihre fertigen Komponenten so
zusammen, dass Sie die funktionalen Anforderungen zusammengefasst
haben und testen die der Reihe nach, oder Sie gehen halt einfach, so
schnell Ihre Programmierer halt programmieren können und testen, was
fertig wurde.
Das ist möglicherweise auch suboptimal aus Sicht des Testers, könnte
aber zeitlich einen Vorteil.
Alternativ können Sie diese berühmten Top-Down, Bottom-Up, Inside-Out
und so weiter Vorgehensweisen machen, da geht es lediglich darum, wie
Sie an die Komponenten herangehen.
Bei Top-Down gehen Sie einfach logisch von der obersten Ebene her und
versuchen, von der abstrakten Ebene nach unten sich die
Testkomponenten zusammenzustöpseln.
Und bei Bottom-Up machen Sie das eben genau umgekehrt.
Das sind natürlich die beiden gegenläufigen Aufbauzeiten.
Inside-Out und Outside-In ist sozusagen der Kompromiss aus Bottom-Up
und Top-Down, also wenn Sie Bottom-Up und Top-Down machen, dann haben
Sie ja so eine Art Komponentenbaum mit den Vor- und Nachteilen, je
nachdem von welcher Seite Sie an den Baum rangehen.
Wenn Sie Inside-Out oder Outside-In machen, dann versuchen Sie, diese
Nachteile dadurch ein bisschen zu entschärfen, dass Sie halt irgendwo
in der Mitte anfangen und dann halt von einer möglicherweise sehr
zentralen Komponente nach außen laufen oder von außen eben das Gesamte
so Stück für Stück nach innen aufbauen und die beiden Ansätze eben
aufeinander zulaufen lassen.
Also die beiden Ansätze Top-Down oder Bottom-Up.
Hardest-First ist davon eigentlich nur noch eine Geschmacksrichtung
sozusagen bezogen auf dessen, was möglicherweise das Inside ist.
Ja, das war's.
Wenn wir gut integriert haben, haben wir hinterher ein System.
Das können wir natürlich auch wieder testen.
Jetzt haben wir hoffentlich ein lauffähiges System und das können wir
jetzt als System testen.
Magst du?
Kann ich gerne.
Im Wesentlichen geht man dann von ganz außen vor.
Das ist also Top-Top-Top-Top-Top-Down.
Man guckt sich einfach das Gesamtsystem an, wie man das fertig gebaut
hat und wie es ursprünglich spezifiziert wurde und testet es einfach
mal als Blackbox.
Also quasi aus Sicht des Benutzers oder wenn man irgendeinen Dienst
hat, dann eben aus Sicht eines Dienstnehmers.
Und man versucht eine möglichst realistische Umgebung dafür zu haben.
Also idealerweise nimmt man echte Daten und auch die richtigen Geräte,
die dafür benutzt werden.
Also macht es nicht auf dem Entwickler-PC, sondern am besten auf der
Maschine, wo das Ding dann hinterher auch läuft.
Und naja, dann geht man eben Schritt für Schritt durch.
Das heißt, man guckt sich den funktionalen Teil an und nicht den nicht
-funktionalen und guckt erstmal...
Was war denn nochmal funktional und nicht-funktional?
Funktional und nicht-funktional.
Naja, das eine ist das, was es tut und das andere ist, wie es das tun
soll.
Also die funktionalen Anforderungen, das waren die...
Naja, es muss XYZ berechnen oder darstellen oder whatever.
Okay.
Und nicht-funktional war...
Naja, ich muss auf die Berechnung nicht länger warten als N Sekunden.
Ah ja, richtig.
Diese weichen Qualitätsdinger, die man am besten als Zahl hinschreibt
und nicht, es muss schnell gehen oder es muss gut bedienbar sein.
Der Benutzer darf höchstens drei Klicks brauchen, um Funktion A zu
Funktion B zu wechseln oder so.
Genau.
Das heißt, man teilt das ein, macht einen funktionalen, einen nicht
-funktionalen Systemtest und geht dann eben diese einzelnen Kriterien
durch.
Idealerweise macht man das dann anhand von den Dokumenten, die man
ganz am Anfang vom Prozess mal aufgeschrieben hat.
Weil wenn man alles erreicht hat, alles getestet hat, was da drin
steht, dann ist man ja fertig, weil mehr hat man ja vertraglich auch
nicht zugesichert.
Was da noch dazu kommt, ist genau das, was wir in der letzten
Übungsaufgabe gemacht haben, den Leistungstest.
Das will man natürlich auch irgendwie machen.
Man will gucken, wie man an die Systemgrenzen rankommt, was die
Leistung und was die Beanspruchbarkeit angeht.
Oder auch immer ganz lustig, was passiert mit dem System, wenn man da
hinkommt?
Oder drüber raus.
Oder drüber hinaus.
Da gibt es manchmal ganz lustige Effekte.
Genau.
Was wir dabei natürlich auch tun, sind Regressionstests.
Das heißt, wir bauen die Tests möglichst wiederholbar und lassen die
jedes Mal wieder neu laufen, wenn sich irgendwas am System ändert.
Das heißt, diese Systemtests und auch alle Tests, die wir vorher
gemacht haben, sollten wir aufbewahren und auch die Ergebnisse davon,
damit wir bei Änderungen oder bei Eingriffen oder bei was auch immer
sicherstellen können, dass alles, was schon mal ging, immer noch geht.
Dass wir also nie durch irgendeine Änderung was kaputt machen.
Idealerweise, habe ich gesagt, schreibt man sich das auf, weil man
will ja nicht alles nochmal durchlesen von den Testergebnissen,
sondern man will nur gucken, was hat sich denn geändert zum letzten
Durchlauf.
Das ist insbesondere dann interessant, wenn das nicht Unit-Tests sind,
die rot oder grün sind, sondern wenn man irgendeine Zeitnutzung macht
oder sowas.
Dann ist es halt hilfreich, wenn man sich die Ergebnisse nicht
durchlesen muss, sondern einfach gesagt bekommt, hier hat sich was
verändert.
Dann ist man mit dem Systemtest schon durch.
Relativ wenig Folien, vermutlich aber relativ viel Zeit, weil das ist
ja die Vorbereitung für das, was man eigentlich gerne haben möchte.
Man möchte die Software abgenommen bekommen vom Kunden.
Das heißt, es findet dann ein Abnahmetest statt.
Und wenn man einen Kunden hat, einen echten, dann nimmt man den
natürlich her.
Der soll da mitmachen.
Der soll in der Regel das zumindest beaufsichtigen, überwachen, weil
dann muss er hinterher unterschreiben, dass er das abnimmt.
Also sollte er auch gesehen haben, was er da tut.
Und meistens ist es auch so, dass er da selber mit einwirkt.
Das heißt, man bekommt dann vom Kunden Menschen, die das tatsächlich
durchführen.
Und idealerweise macht man das natürlich mit Echtdaten.
Also vorher hat man versucht, realistische Daten zu nehmen in den
Systemtest.
Und beim Abnahmetest sollte man dann tunlichst echte Daten haben, weil
sonst weiß man ja nicht, ob es klappt.
Insbesondere wird der Kunde darauf bestehen, funktioniert es denn auch
mit dem, was wir hier im Keller liegen haben und nicht nur das, was
ihr euch ausgedacht habt.
Normalerweise ist es dann so, dass der Auftraggeber einfach Testfälle
aus den Systemtests übernimmt, weil wenn sie da schon was getestet
haben, was der sich ohnehin angucken möchte, dann muss er sich ja den
Testfall und das Szenario nicht nochmal neu ausdenken.
Meistens geht er dann hin und ändert irgendwelche Details, dreht an
irgendeiner Schraube, macht einen Sonderfall draus, führt noch einen
Umlaut in irgendein Namensfeld ein und dann explodiert es, hoffentlich
nicht, und ist dann zufrieden.
Und was sie dann bekommen, ist eben eine formale Abnahme im Sinne von,
der Kunde sagt, ja, das, was ich bestellt habe und das, was ich
bezahlen werde, das habe ich jetzt geliefert bekommen.
Natürlich genau das, was sie als Systemhaus oder als Entwickler haben
wollen.
Sie wollen das Okay vom Kunden, weil der am Ende die Rechnung bezahlt.
Und wenn er das Okay an der Stelle gibt, dann hat er halt auch keine
Chance mehr, aus diesem Bezahlen rauszukommen, zumindest auf normalem
Wege.
Ja, dann sind wir mit dem Testen und Prüfen schon schön durch.
Es gibt noch ein bisschen Literatur in diesem Kapitel, die Sie sich
gerne mal anschauen können.
Und das war alles, was in diesem hübschen Testkapitel drin ist.
Es gibt noch eine Testmethode, mit der ich mal Kontakt hatte bei web
.de.
Das ist die schwäbische Testmethode.
Die geht so, nicht gemotzt, ist genug gelobt.
Und es war also so, dass die ersten Versionen von web.de vor 15 Jahren
oder so, liefen auf den Arbeitsplatzrechnern der Mitarbeiter unter
Solaris, weil der Chef der IT der Meinung war, Solaris ist ein super
performantes Betriebssystem, dort kann man diesen Dienst zuverlässig
laufen lassen und sowas.
Und die Mitarbeiter teilten diese Meinung nicht ganz so.
Linux war zu der Zeit sehr hip und modern und man brauchte das
unbedingt und so.
Und dann ging der Chef eines schönen Tages mal in Urlaub für drei
Wochen und dann haben sie das web.de-System genommen und haben es auf
Linux portiert, einfach mal so, zack, haben es laufen lassen und haben
dem Chef nichts gesagt.
Als er zurückkam, haben wir geguckt, wie lang es braucht, bis er es
merkt.
Sie sind bis heute auf Linux.
Wahrscheinlich hat er schon gemerkt, dass irgendwann keiner mehr
Solaris -Lizenzen haben wollte.
Ja, das ist das zweite, erste Geschäftsmodell-Problem.
Auch wieder schwäbisch.
Dinge, die man bezahlen muss, in N skalieren nicht, wenn Kunden nichts
bezahlen, aber in N skalieren.
Das ist ein grundsätzliches Freemailer-Problem.
Genau.
Wollen wir mit dem Kapitel noch anfangen?
Ja, ein bisschen.
Okay, dann fangen wir mal noch mit dem hübschen Abnahmekapitel an.
Die Frage ist, jetzt haben wir den Abnahmetest gemacht.
Das Ding ist beim Kunden, was passiert denn dann?
Also im Wasserfall sind wir dann ganz hier unten, das heißt Abnahme,
Einsatz und Wartung.
Das heißt, wir müssen es offensichtlich beim Kunden einsetzen und
vermutlich dann auch warten.
Heißt, dem wird im Betrieb irgendwas auffallen, da werden Dinge kaputt
gehen und dann muss man eben nachpflegen.
Das ist hier nochmal ein bisschen ausgerollt mit den einzelnen Dingen,
die man da so hat.
Das heißt, wenn man den Abnahmeprozess durch hat, dann hat man also
offensichtlich ein installiertes Produkt beim Kunden.
Das ist dann da und läuft.
Der Systemtest ist durch, der Kunde hat also gesagt, toll, nehme ich.
Und wir haben dem Kunden die Dokumentation über den Zaun geworfen und
sind dann schnell weggerannt, damit er nicht merkt, was da alles für
Quatsch drin steht.
Aber ganz wichtig, dieses Okay, das Thomas gerade simuliert hat, das
haben wir natürlich protokolliert, das haben wir aufgeschrieben.
Und das haben wir ihnen auch unterschreiben lassen.
Das heißt, hier hat es wieder hübsche Dokumente, nämlich ein Abnahme-
und Einführungsprotokoll, in dem am Ende halt drin steht, ja, wir
haben es gekriegt.
Ab diesem Moment ist man also nicht mehr im Entwicklungsprozess,
sondern jetzt ist man in Wartung und Pflege.
Und naja, das ist wohl irgendwo am Ende und man muss offensichtlich
auch noch implementieren.
Das heißt, da hängt auch noch was dran.
Das haben wir eigentlich schon gemacht im letzten Ding, deswegen
springe ich das mal ein bisschen schneller durch.
Genau, wenn man den externen Auftraggeber hat, das ist das, was wir
gerade hatten, der muss das eben abnicken.
Irgendwo sollte noch stehen, was passiert, wenn man keinen externen
Auftraggeber hat, sondern für den Markt entwickelt.
Dann macht man so Sachen wie Betatests.
Das ist gestern auch in Folien gelesen, aber ich weiß nicht mehr genau
wo.
Naja, ist klar, wenn man keinen Kunden hat, der es abnehmen kann, weil
es den Kunden nicht gibt, dann muss man seine Produkte eben so an den
Mann bringen und sich Feedback von den zukünftigen Nutzern holen.
Das ist das, was wir alle kennen.
Da kommt diese Frage, wer ist meine Zielgruppe und was möchten die?
Sind es die Autofahrenden Männer zwischen 25 und 35?
Dann muss ich vielleicht anders vermarkten, als wenn es irgendwie die
Senioren zwischen 70 und 90 sind oder so.
Genau.
Wenn man allerdings so einen externen Auftraggeber hat, dann ist die
Welt wieder schön und einfach.
Man muss nur vorher mit dem vereinbaren, wer denn dieses Produkt
wartet und pflegt.
Wenn er das nur nutzt, Sie allerdings derjenige sind, der es wartet
und pflegt, dann sollten Sie dafür Geld bekommen.
Das sollten Sie vorher vereinbaren.
Und wenn er das selber warten und pflegen will, naja, dann braucht er
offensichtlich die Quellen und die Entwicklerdokumentation und das
ganze Zeug, um das auch machen zu können.
Auch das sollten Sie vorher vereinbaren.
Insbesondere sollten Sie sich im Klaren sein, dass Sie damit ja Ihr
Betriebsgeheimnis rausschenken.
Der braucht Sie dann nicht mehr.
Möglicherweise.
Also als Microsoft-Firma nicht hingehen und seinem Kunden den Windows
-Quelltext geben.
Das hat aber auch andere Gründe.
Das hat auch andere Gründe, ja.
Das ist bei der Individualentwicklung aber nicht anders.
Also da muss man ein bisschen aufpassen.
Genau.
Während das Produkt genutzt wird, dann gibt es natürlich diese
Qualitätsmerkmale, die man sich im Abnahmetest oder im Systemtest
schon angeguckt haben sollte.
Und wenn man das Ding dann warten und pflegen muss, dann kommt einfach
noch dazu, naja, es muss offensichtlich wartbar sein und es sollte
testbar sein über das Maß hinaus, das wir schon gemacht haben.
Und es sollte ein bisschen Flexibilität mitbringen.
Wenn Sie im Produkteinsatz sind und da ändert sich ein bisschen was
beim Kunden, der ruft an und sagt, ich bräuchte jetzt aber mal den
Knopf in grün, dann ist das wahrscheinlich einfach.
Und wenn das ein Bankhaus ist und sagt, ich möchte jetzt aber die
Kontokorrentzinsberechnung anders haben und Sie müssen dann zwei Tage
die Bank ausschalten, weil Sie da irgendwas umdrehen müssen im Kern,
dann ist Ihr Produkt offensichtlich nicht ganz so flexibel und eher
ungeschickt.
Mir kommen diese zusätzlichen Merkmale übrigens sehr, sehr bekannt vor
aus dem Modellierungskapitel.
Dort hat man ja möglicherweise schon das Modell und den Entwurf so
angepasst, dass man nachher dann, nämlich jetzt, Wartbarkeit und
Testbarkeit und Flexibilität vielleicht mehr hat, als wenn man
irgendwie hanebüchen einen Entwurf hätte, der die Wartbarkeit oder
Testbarkeit oder die Flexibilität eben einschränkt.
Genau.
Wenn Sie das Produkt beim Kunden einführen, dann müssen Sie es
natürlich erstmal installieren und einrichten und was das alles
dazugehört, aber vermutlich müssen Sie die Benutzer auch entsprechend
schulen.
Also Sie installieren da irgendwas Neues und verabschieden sich dann,
dann wird es vermutlich beim Kunden am ersten Tag krachen.
Wenn er nämlich selber wieder Kunden hat, die er bedienen muss und
nicht weiß, wie sein System funktioniert.
Das alles gehört da dazu.
Das alles, wie schon 15 mal gesagt heute, müssen wir natürlich
entsprechend dokumentieren und protokollieren.
Was haben wir noch?
Wenn Sie das Ganze einführen und Sie sind auf der grünen Wiese, dann
ist es natürlich toll.
Das ist genau das, was Sie haben wollen.
Sie haben keine Abhängigkeiten, Sie müssen nichts beachten.
Das ist aber in aller Regel nicht der Fall.
In der Regel ist es so, dass Sie ein System ausrollen und ein anderes
ersetzen oder ergänzen.
Das müssen Sie sich vorher vielleicht mal überlegen.
Stellen Sie sich vor, Sie haben hier eine Fluggesellschaft als Kunden
und die wollen ein neues Buchungssystem.
Okay, ich installiere das Freitagabend, am Montag früh können Sie
damit arbeiten.
Hoffentlich.
Genau, hoffentlich.
Wenn nichts schief geht und die Datenübernahme aus dem Altsystem und
die Installation auf den 823 Clients an 48 Standorten funktioniert,
dann können Sie am Montag Ihren ganz normalen Betrieb wieder
hochfahren.
Dann sagt der Kunde nicht, oh toll, weil es läuft, sondern er sagt,
spinnen Sie eigentlich?
Samstag und Sonntag arbeiten wir auch.
Sie haben einen 24-7 Betrieb, Sie können nicht für drei Tage den
Betrieb lahmlegen und dann vielleicht wieder hochfahren.
Das ist einfach keine Option.
Keine Option, genau.
Das heißt, Sie müssen sich vorher darüber Gedanken machen, dass Sie
dafür Zeit brauchen.
Das größte Problem bei der Übertragung von dem Alt in ein neues System
ist das Mitnehmen von den Daten.
Und ich sag mal, einen Dump aus dem Altsystem rauslassen und das Neue
einspielen, das kann man drei-, viermal im Laborbetrieb machen und
dann weiß man, wie das geht.
Und dann hat man alle Sonderfälle abgefangen und dann ist es okay.
Aber während Sie das neue System ausrollen, entstehen ja weiterhin
Daten im Altsystem, die Sie auch noch irgendwann erfassen müssen.
Und das weißt Sie dann irgendwann halt schon.
Sie müssen sich also überlegen, kriege ich eine direkte Umstellung
hin?
Das heißt, ich entlade das Altsystem und belade das neue System.
Dann haben Sie bei der Fluggesellschaft das Problem, dass das halt
nicht instantan passiert, sondern eher über Tage.
Und das geht nicht.
Eine Alternative ist ein Parallellauf.
Sie installieren einfach das neue System, laden die Altdaten rein und
sagen, okay, viel Spaß, Sie haben jetzt das Neue und das Alte, machen
Sie es einfach doppelt.
Und wenn Sie das Alte nicht mehr brauchen, schalten Sie das Alte ab.
Schalten Sie das Alte halt ab, kein Problem.
Dann sagt der Kunde vielleicht, wenn es gut läuft, ja, ist wunderbar,
das machen wir, weil wir haben genug Zeit und nichts zu tun und das
ist eh super und dann wissen wir, dass es geht.
Und das Geschäftsmodell erlaubt das vielleicht oder so?
Ja, dann ist es toll.
Wenn aber die Landhäuser eher 200.000 Passagiere pro Woche
transportiert und der Passagier muss dann wissen, ob er im Altsystem
oder im Neuesystem gebucht ist, das hört sich dann auch an, als ob das
schief gehen muss.
Also parallel ist in dem Fall keine Lösung, weil Sie wollen ja nicht
am Schalter stehen und alles doppelt machen, doppelt einchecken und
doppelt Ihr Gepäck abgeben und so weiter.
Oder entscheiden, in welchem System.
Und entscheiden, dass Sie ins Neue oder ins Alte System wollen, das
geht ja auch nicht, stellen Sie sich vor, Sie haben ein Problem.
Herzlichen Glückwunsch, Sie sind Betakunde in unserem neuen
Buchungssystem.
Wenn Sie Fragen haben, am Ende der Halle gibt es einen Schalter, der
kann das vielleicht beantworten.
Der ist mit dem neuen, kleinen bestückt.
Ja, geht aber nur bis 10 Minuten vor dem Abflug, weil da transferieren
wir alle Daten ins Altsystem, weil sonst kriegen Sie kein Essen.
Genau.
Ist einfach ungeschickt.
Da macht man dann vermutlich eher einen Versuchslauf, das heißt, man
setzt das neue System auf einen definierten Stand, man zieht sich also
einen Damm vom Altsystem, installiert das neue System, lädt die Daten
rein und sagt, okay, jetzt habe ich ja noch Verkehrsdaten.
Also ich protokolliere einfach, was passiert denn im Altsystem und
spiele das doppelt wie beim Parallelllauf einfach nochmal durch und
gucke, ist dann das Ergebnis das gleiche.
Das hat den Charme und den Vorteil, dass man das natürlich offline im
Keller machen kann.
Das heißt, Sie holen sich irgendeinen definierten Stand plus die
Änderung und mit der Änderungsliste sitzt einfach ein armer Praktikant
im Keller und checkt 400 Passagiere ein, die jetzt gar nicht da sind,
aber er tut es halt und dann guckt er hinterher, ob die Maschine voll
ist.
Und wenn das alles klappt, dann kann man wohl gefahrlos umstellen.
Also quasi eine Simulierung vom Echtbetrieb.
In den Folien haben Sie dieses ganze Gelaber nochmal ein bisschen
hübscher, inlesbar und den Teil überspringe ich einfach.
Das haben wir alles jetzt schon durchgeturnt.
Was dann am Ende rauskommt, wie immer, 143 Protokolle und
Dokumentationen.
Was Sie aber auch noch protokollieren und festhalten sollen, da reiten
wir auch ein bisschen drauf rum.
Das kennen Sie auch schon aus dem Versionskontrollkapitel, das ist
das, was Sie ausgerollt haben.
Sie sollten die Software archivieren.
Sie müssen wissen, was für ein Stand läuft denn beim Kunde.
Hey Landhäuser, könnt ihr uns euren Stand unserer Software nochmal
schicken, weil wir haben leider vergessen, welchen wir euch geschickt
haben.
Ja, es geht nicht, der Server steht.
Irgendwie blöd.
Das machen wir nicht.
Das heißt, Sie sollten wissen, was haben Sie bei welchem Kunden, in
welcher Version, mit welchem Patch installiert.
Und, naja, idealerweise weiß die Software auch, welche Version sie
hat.
Das haben Sie bei der Anwendungssoftware einfach immer in diesem Hilfe
-Übermenü.
Da steht die Version und die Revisionsnummer.
Das haben Sie natürlich bei einer großen Software auch.
Die sollte auch wissen, welchen Stand sie hat.
Und wenn Sie wissen, welchen Stand die Software beim Kunden hat und
der mit einem Problem auf sie zukommt, dann können Sie das einfach
nachstellen und dann beheben.
An der Stelle haben wir thematisch, glaube ich, einen Bruch in den
Folien und an der Stelle hören wir dann mit den Folien auf.
Sie dürfen aber noch nicht gehen, weil wir haben...
Ich habe noch einen Hinweis für Sie.
Einen Hinweis?
Zum einen, die Verleihung für Aufgabe 3 machen wir nächste Woche.
Sie haben uns also heute nicht zum letzten Mal ertragen.
Wir werden uns nochmal wiedersehen nächste Woche für ein paar Minuten
in der Vorlesung.